GraceSoft Storehouse

API reference · v1.2.0

GraceSoft Storehouse API

Public-domain Proverbs and original GraceSoft quips, as static JSON.

Base URL https://staticapi.gracesoft.dev

GraceSoft Storehouse is a read-only, versioned REST API. Every response is a static JSON file served from Vercel's global CDN, so it's fast, cacheable, and needs no API key.

Envelope. Successful responses are { "data": …, "meta": { "version", "generatedAt" } }. Lists also carry meta.count. Errors are { "error": { "status", "code", "message" }, "meta" }. Product blueprints are the exception: they follow their own published schema, unwrapped.

Time zone. "Today" is always evaluated in Asia/Singapore (UTC+8). New quips go live at 00:00 SGT on their publishAt date, after the daily scheduled rebuild.

Caching. Responses carry ETag and Cache-Control headers; send If-None-Match to get 304 Not Modified.

CORS. Browser access is limited to an allowlist of GraceSoft origins. Server-side clients (bots, scripts) can call the API from anywhere.

Versioning. Additive changes stay in v1. Breaking changes go to v2.

Licence. Proverbs text is public domain. Quips are © D.L. (GraceSoft) — see CONTENT_LICENSE.md for terms of reuse.

Translations

The public-domain Bible translations available.

GET /v1/translations.json

List translations

Every translation in the registry, sorted by code.

Responses

  • 200 The translation registry. — TranslationListResponse
  • 304 The resource hasn't changed since the If-None-Match ETag.
  • 4XX The request can't be served, e.g. 405 for any method other than GET, HEAD, or OPTIONS. — ErrorResponse

Example

curl https://staticapi.gracesoft.dev/v1/translations.json
{
  "data": [
    {
      "code": "asv",
      "name": "American Standard Version",
      "year": 1901,
      "language": "en",
      "license": "Public Domain",
      "source": "https://ebible.org/find/details.php?id=eng-asv"
    },
    {
      "code": "kjv",
      "name": "King James Version",
      "year": 1769,
      "language": "en",
      "license": "Public Domain",
      "source": "https://ebible.org/find/details.php?id=eng-kjv2006"
    },
    {
      "code": "web",
      "name": "World English Bible",
      "year": 2000,
      "language": "en",
      "license": "Public Domain",
      "source": "https://ebible.org/find/details.php?id=engwebp"
    }
  ],
  "meta": {
    "version": "1.2.0",
    "generatedAt": "2026-10-30T20:39:49.369Z",
    "count": 3
  }
}

Proverbs

The 31 chapters of the book of Proverbs, per translation.

GET /v1/proverbs/{code}/index.json

List chapters

The 31 chapters of Proverbs for one translation, with verse counts.

Parameters

NameInTypeDescription
code requiredpathstring

A translation code from /v1/translations.json, e.g. kjv.

Responses

  • 200 Chapter summaries for one translation. — ChapterListResponse
  • 304 The resource hasn't changed since the If-None-Match ETag.
  • 404 No resource exists at this path. — ErrorResponse

Example

curl https://staticapi.gracesoft.dev/v1/proverbs/kjv/index.json
{
  "data": [
    {
      "translation": "kjv",
      "chapter": 1,
      "verseCount": 33,
      "href": "/v1/proverbs/kjv/1.json"
    },
    {
      "translation": "kjv",
      "chapter": 2,
      "verseCount": 22,
      "href": "/v1/proverbs/kjv/2.json"
    },
    {
      "translation": "kjv",
      "chapter": 3,
      "verseCount": 35,
      "href": "/v1/proverbs/kjv/3.json"
    },
    "… 28 more"
  ],
  "meta": {
    "version": "1.2.0",
    "generatedAt": "2026-10-30T20:39:49.369Z",
    "count": 31
  }
}
GET /v1/proverbs/{code}/{chapter}.json

Get a chapter

One chapter of Proverbs, with every verse.

Parameters

NameInTypeDescription
code requiredpathstring

A translation code from /v1/translations.json, e.g. kjv.

chapter requiredpathinteger (1–31)

Chapter number, 1–31.

Responses

  • 200 One chapter with its verses. — ChapterResponse
  • 304 The resource hasn't changed since the If-None-Match ETag.
  • 404 No resource exists at this path. — ErrorResponse

Example

curl https://staticapi.gracesoft.dev/v1/proverbs/kjv/3.json
{
  "data": {
    "translation": "kjv",
    "translationName": "King James Version",
    "chapter": 3,
    "verses": [
      {
        "verse": 1,
        "text": "My son, forget not my law; but let thine heart keep my commandments:"
      },
      {
        "verse": 2,
        "text": "For length of days, and long life, and peace, shall they add to thee."
      },
      {
        "verse": 3,
        "text": "Let not mercy and truth forsake thee: bind them about thy neck; write them upon the table of thine heart:"
      },
      "… 32 more"
    ]
  },
  "meta": {
    "version": "1.2.0",
    "generatedAt": "2026-10-30T20:39:49.369Z"
  }
}
GET /v1/proverbs/{code}/today Dynamic

Get today's chapter

The chapter matching today's day of the month in Asia/Singapore ("a Proverb a day"). On the 19th, this is chapter 19.

Served by a Vercel Function that reads the static chapter file; responses are cached until the next 00:00 SGT. Rate-limited.

Parameters

NameInTypeDescription
code requiredpathstring

A translation code from /v1/translations.json, e.g. kjv.

Responses

  • 200 One chapter with its verses. — ChapterResponse
  • 404 No resource exists at this path. — ErrorResponse
  • 429 Rate limit exceeded (60 requests a minute per IP on the dynamic endpoints). Returned by the Vercel Firewall, so the body isn't the JSON error shape. Wait a minute and retry.

Example

curl https://staticapi.gracesoft.dev/v1/proverbs/web/today
{
  "data": {
    "translation": "web",
    "translationName": "World English Bible",
    "chapter": 3,
    "verses": [
      {
        "verse": 1,
        "text": "My son, don’t forget my teaching, but let your heart keep my commandments,"
      },
      {
        "verse": 2,
        "text": "for they will add to you length of days, years of life, and peace."
      },
      {
        "verse": 3,
        "text": "Don’t let kindness and truth forsake you. Bind them around your neck. Write them on the tablet of your heart."
      },
      "… 32 more"
    ]
  },
  "meta": {
    "version": "1.2.0",
    "generatedAt": "2026-10-30T20:39:49.369Z"
  }
}

Quips

Original GraceSoft quips and puns. Only published quips are served.

GET /v1/quips/index.json

List quips

Every published quip, sorted by id. Drafts and future-dated quips are never included.

Responses

  • 200 Every published quip. — QuipListResponse
  • 304 The resource hasn't changed since the If-None-Match ETag.
  • 4XX The request can't be served, e.g. 405 for any method other than GET, HEAD, or OPTIONS. — ErrorResponse

Example

curl https://staticapi.gracesoft.dev/v1/quips/index.json
{
  "data": [
    {
      "id": 1,
      "slug": "when-youre-on-a-keto-diet",
      "variant": "statement",
      "punchline": "When you're on a keto diet, you gotta lock down your intake.",
      "tags": [
        "food"
      ],
      "createdAt": "2026-10-06",
      "publishAt": "2026-10-06"
    },
    {
      "id": 2,
      "slug": "oh-oh-oats",
      "variant": "qa",
      "question": "What does Santa like to have for breakfast?",
      "punchline": "Oh, oh, oats.",
      "tags": [
        "food",
        "christmas"
      ],
      "createdAt": "2026-10-06",
      "publishAt": "2026-10-06"
    },
    {
      "id": 3,
      "slug": "young-tau-foo",
      "variant": "qa",
      "question": "What hawker dish would you consume for youthfulness?",
      "punchline": "Young tau foo.",
      "tags": [
        "food",
        "singapore",
        "hawker"
      ],
      "createdAt": "2026-10-06",
      "publishAt": "2026-10-06"
    },
    "… 66 more"
  ],
  "meta": {
    "version": "1.2.0",
    "generatedAt": "2026-10-30T20:39:49.369Z",
    "count": 69
  }
}
GET /v1/quips/{id}.json

Get a quip

One published quip. Unpublished, draft, and retired ids return 404.

Parameters

NameInTypeDescription
id requiredpathinteger (≥ 1)

The quip's permanent id.

Responses

  • 200 One published quip. — QuipResponse
  • 304 The resource hasn't changed since the If-None-Match ETag.
  • 404 No resource exists at this path. — ErrorResponse

Example

curl https://staticapi.gracesoft.dev/v1/quips/2.json
{
  "data": {
    "id": 2,
    "slug": "oh-oh-oats",
    "variant": "qa",
    "question": "What does Santa like to have for breakfast?",
    "punchline": "Oh, oh, oats.",
    "tags": [
      "food",
      "christmas"
    ],
    "createdAt": "2026-10-06",
    "publishAt": "2026-10-06"
  },
  "meta": {
    "version": "1.2.0",
    "generatedAt": "2026-10-30T20:39:49.369Z"
  }
}
GET /v1/quips/random Dynamic

Get a random quip

A random published quip. Never cached. Served by a Vercel Function that reads /v1/quips/index.json. Rate-limited.

Parameters

NameInTypeDescription
tagquerystring

Only pick from quips with this tag.

Responses

  • 200 One published quip. — QuipResponse
  • 404 No resource exists at this path. — ErrorResponse
  • 429 Rate limit exceeded (60 requests a minute per IP on the dynamic endpoints). Returned by the Vercel Firewall, so the body isn't the JSON error shape. Wait a minute and retry.

Example

curl https://staticapi.gracesoft.dev/v1/quips/random?tag=food
{
  "data": {
    "id": 2,
    "slug": "oh-oh-oats",
    "variant": "qa",
    "question": "What does Santa like to have for breakfast?",
    "punchline": "Oh, oh, oats.",
    "tags": [
      "food",
      "christmas"
    ],
    "createdAt": "2026-10-06",
    "publishAt": "2026-10-06"
  },
  "meta": {
    "version": "1.2.0",
    "generatedAt": "2026-10-30T20:39:49.369Z"
  }
}

Blueprints

Reviewed product blueprints: short JSON summaries of GraceSoft products that GraceSoft Concierge uses when a product has no content feed. Responses are not wrapped in the { data, meta } envelope; each blueprint follows product blueprint schema v1, published at /schemas/product-blueprint-1.json. The ETag is the blueprint's (or the index's) hash.

GET /v1/blueprints/index.json Dynamic

List product blueprints

Every published blueprint, sorted by product, with its hash. The index hash changes when any published blueprint changes, is added, or is removed, and only then.

Responses

  • 200 The published product blueprints. — ProductBlueprintIndex
  • 304 The resource hasn't changed since the If-None-Match ETag.
  • 4XX The request can't be served, e.g. 405 for any method other than GET, HEAD, or OPTIONS. — ErrorResponse

Example

curl https://staticapi.gracesoft.dev/v1/blueprints/index.json
{
  "schema": "gracesoft.product-blueprint-index/1",
  "generated_at": "2026-10-31T04:39:49+08:00",
  "hash": "sha256:f3faf590936df457e71dc44426ef87b399fb0505d738bbf4d43c646ef8576f35",
  "blueprints": [
    {
      "product": "capture",
      "url": "https://staticapi.gracesoft.dev/v1/blueprints/capture.json",
      "version": 1,
      "reviewed_at": "2026-10-28",
      "hash": "sha256:5bb56c6775493336faca4f6994487e8cb5fc782fdfbb08f6e14060f0cbdaa254"
    },
    {
      "product": "skylight",
      "url": "https://staticapi.gracesoft.dev/v1/blueprints/skylight.json",
      "version": 1,
      "reviewed_at": "2026-10-28",
      "hash": "sha256:bef98f0884286c0c3cd8e0494dbf6d18ebf3ab64826cb69429cc9b38e9cf6ca6"
    }
  ]
}
GET /v1/blueprints/{product}.json Dynamic

Get a product blueprint

One published blueprint. Unpublished and unknown products return the same 404.

Parameters

NameInTypeDescription
product requiredpathstring

The product slug, e.g. capture.

Responses

  • 200 One product blueprint. — ProductBlueprint
  • 304 The resource hasn't changed since the If-None-Match ETag.
  • 404 No resource exists at this path. — ErrorResponse

Example

curl https://staticapi.gracesoft.dev/v1/blueprints/capture.json
{
  "schema": "gracesoft.product-blueprint/1",
  "product": "capture",
  "name": "GraceSoft Capture",
  "tagline": "Privacy-first forms with an inbox built in.",
  "status": "live",
  "published": true,
  "version": 1,
  "reviewed_at": "2026-10-28",
  "cite_url": "https://gracesoft.dev/products/capture",
  "summary": "GraceSoft Capture is a **privacy-first, security-first form builder** for contact, feedback and enquiry forms.\n\nEmbed a form on any site with no cookies and no trackers, then reply, assign and close every submission from one shared inbox. Submissions are encrypted at rest, deleted automatically after the retention period you choose, and easy to look up, export or erase for PDPA requests.\n\n- Build from a preset, or import a Google Form or CSV\n- Embed with a hosted link or an iframe under a strict Content Security Policy\n- Stop spam without puzzles for real visitors\n- Work every submission from a shared inbox",
  "audience": [
    "Small businesses replacing a third-party contact form",
    "Teams collecting product feedback and bug reports",
    "Small teams sharing one inbox for enquiries",
    "… 1 more"
  ],
  "features": [
    {
      "name": "Form builder with presets",
      "description": "Start from a contact, enquiry, feedback, callback request or event registration preset, or a blank form. Twelve field types, including drop-downs, checkboxes, dates and links, with a live preview beside you.",
      "availability": "available",
      "plans": [
        "free",
        "pro",
        "team"
      ]
    },
    {
      "name": "Versioned publishing",
      "description": "Edit a draft while the live form stays unchanged. Each publish creates a new version, and every submission records the version it was made against.",
      "availability": "available",
      "plans": [
        "free",
        "pro",
        "team"
      ]
    },
    {
      "name": "Conditional logic",
      "description": "Show or hide fields based on earlier answers. Free allows one condition per field; Pro and Team allow several.",
      "availability": "available",
      "plans": [
        "free",
        "pro",
        "team"
      ]
    },
    "… 15 more"
  ],
  "not_offered": [
    "Payment collection inside forms",
    "File upload fields",
    "Password fields in forms",
    "… 2 more"
  ],
  "plans": [
    {
      "key": "free",
      "name": "Free",
      "price": 0,
      "currency": "SGD",
      "interval": null,
      "features": [
        "3 forms",
        "100 submissions a month",
        "1 member",
        "… 2 more"
      ],
      "max_members": 1
    },
    {
      "key": "pro",
      "name": "Pro",
      "price": 12,
      "currency": "SGD",
      "interval": "month",
      "features": [
        "25 forms",
        "2,000 submissions a month",
        "1 member",
        "… 2 more"
      ],
      "max_members": 1,
      "note": "Also available yearly at 120."
    },
    {
      "key": "team",
      "name": "Team",
      "price": 39,
      "currency": "SGD",
      "interval": "month",
      "features": [
        "Unlimited forms",
        "10,000 submissions a month",
        "Up to 10 members",
        "… 2 more"
      ],
      "max_members": 10,
      "note": "Also available yearly at 390."
    }
  ],
  "pricing_note": "Monthly or yearly billing. Downgrading never deletes data: premium settings are kept and switch off until you upgrade again.",
  "privacy_highlights": [
    "No IP addresses or browser details are stored for form visitors.",
    "Embedded forms set no cookies and load no trackers or third-party scripts (Cloudflare Turnstile only when you turn it on).",
    "Submissions, replies and notes are encrypted at rest.",
    "… 2 more"
  ],
  "links": {
    "landing": "https://gracesoft.dev/products/capture",
    "signup": "https://formscapture.gracesoft.dev/register",
    "terms": "https://gracesoft.dev/legal/terms",
    "privacy": "https://gracesoft.dev/legal/privacy"
  },
  "faq": [
    {
      "q": "Is there a free plan?",
      "a": "Yes. Free includes 3 forms and 100 submissions a month. Pro and Team add more forms and submissions, premium design controls and, on Team, members and assignment."
    },
    {
      "q": "Does Capture put cookies or trackers on my website?",
      "a": "No. The embed sets no cookies and loads no trackers or third-party scripts. Cloudflare Turnstile loads only on forms where you turn it on."
    },
    {
      "q": "How does Capture stop spam without a CAPTCHA?",
      "a": "Every form has a honeypot, a self-hosted proof-of-work check and a minimum time to fill in. You can add Cloudflare Turnstile to a form if you want an extra check."
    },
    "… 3 more"
  ],
  "changelog": [
    {
      "date": "2026-10-28",
      "summary": "First blueprint."
    }
  ]
}

Schemas

Meta

FieldTypeDescription
version requiredstring

API release version (semver).

generatedAt requiredstring (date-time)

When this response was built.

ListMeta

FieldTypeDescription
version requiredstring

API release version (semver).

generatedAt requiredstring (date-time)

When this response was built.

count requiredinteger (≥ 0)

Number of items in data.

Translation

FieldTypeDescription
code requiredstring

name requiredstring

year requiredinteger

language requiredstring

ISO 639-1 language code.

license required"Public Domain"

source requiredstring (uri)

Where the text was obtained.

ChapterSummary

FieldTypeDescription
translation requiredstring

chapter requiredinteger (1–31)

verseCount requiredinteger (≥ 1)

href requiredstring

Path to the chapter resource.

Verse

FieldTypeDescription
verse requiredinteger (≥ 1)

text requiredstring

Plain text (no Markdown or HTML). May contain curly quotes and em dashes.

Chapter

FieldTypeDescription
translation requiredstring

translationName requiredstring

chapter requiredinteger (1–31)

verses requiredVerse[]

Quip

FieldTypeDescription
id requiredinteger (≥ 1)

Permanent id; never reused.

slug requiredstring

variant required"qa" | "statement"

qa has a question and a punchline; statement has only a punchline.

questionstring

Present only when variant is qa.

punchline requiredstring

Plain text. Paragraphs are separated by a blank line (\n\n).

tags requiredstring[]

createdAt requiredstring

ISO 8601 date (YYYY-MM-DD) or date-time with offset.

publishAt requiredstring

ISO 8601 date or date-time. A date means 00:00 Asia/Singapore.

TranslationListResponse

FieldTypeDescription
data requiredTranslation[]

meta requiredListMeta

ChapterResponse

FieldTypeDescription
data requiredChapter

meta requiredMeta

QuipListResponse

FieldTypeDescription
data requiredQuip[]

meta requiredListMeta

QuipResponse

FieldTypeDescription
data requiredQuip

meta requiredMeta

ErrorResponse

FieldTypeDescription
error requiredobject

meta requiredMeta

ProductBlueprint

Product blueprint schema v1. The full JSON Schema (draft 2020-12) is published at /schemas/product-blueprint-1.json.

/schemas/product-blueprint-1.json

ProductBlueprintIndex

FieldTypeDescription
schema required"gracesoft.product-blueprint-index/1"

generated_at requiredstring (date-time)

Build time in Asia/Singapore, with its offset.

hash requiredBlueprintHash

blueprints requiredProductBlueprintIndexEntry[]

ProductBlueprintIndexEntry

FieldTypeDescription
product requiredstring

url requiredstring (uri)

Absolute URL of the blueprint.

version requiredinteger (≥ 1)

reviewed_at requiredstring (date)

hash requiredBlueprintHash

BlueprintHash

sha256: + SHA-256 of the RFC 8785 canonical JSON.

string