ogmake
Menu

API reference

Generated from the API's own OpenAPI document (GET /v1/openapi.json) — every route and schema here is proven against real responses by apps/api/src/openapi.contract.test.ts.

Background images (bg)

Pass bg as a top-level POST /v1/images field (or query param on the signed/public GET routes) to render one of the 40 curated free-pack background photos full-bleed behind the card, e.g. bg=bg-01. The template's title/description text gets its own contained backdrop panel so it stays legible over the photo — the photo itself is never dimmed or scrimmed.

Only templates whose design isn't a deliberate physical/mechanical artifact (a receipt, a punchcard, a ballot, ...) accept it: basic, blog, docs, changelog, product, event, podcast, job, release, case-study, testimonial, lesson, newsletter, spotlight, aurora, signal and verbatim. An unrecognized bg id is rejected with invalid_input; a valid id passed to a template that doesn't support backgrounds is silently ignored, same as any other param a template's schema doesn't declare.

Array fields on GET routes

POST /v1/images takes an array field as a real JSON array in the request body. The two GET routes — signed GET /i/{keyId}/{sig} (Signing) and unsigned GET /p/{keyId}/{template} (Public URLs) — have no JSON body, so an array field is carried as bracket-indexed query keys instead: field[0]=first&field[1]=second, up to that field's own max length, indexes contiguous starting at 0. A plain repeated key (field=a&field=b) is rejected as a duplicate, not merged into an array.

11 templates require at least one array field, so a request for them cannot be built without this syntax:

Template Array field(s)
changelog changes
receipt items
patch added
ledger rows
snellen lines
redacted lines
ballot options
metro stops
facts rows
bracket contenders, finalists
app platforms

POST /v1/images

Requires Authorization: Bearer og_live_....

Request body — PostImagesBody | PostImagesHtmlBody

PostImagesBody

Field Type Required
template string no
saved string no
url string no
refresh boolean no
params object no
format "png" | "jpg" | "webp" | "pdf" no
preset "og" | "x" | "square" no
scale "1" | "2" no
v string no
bg string no
font string no
webhook_url string no
auto boolean no
hint string | null no

PostImagesHtmlBody

Field Type Required
html string yes
css string no
width integer yes
height integer yes
format "png" | "jpg" | "webp" | "pdf" no

Responses

Status Type Description Retry-After
201 PostImagesResponse | PostImagesHtmlResponse Image resolved (rendered or served from cache). A raw-`html` request is always a fresh render (2 credits). Auto mode (`auto: true`) adds `credits`, `sourceUrl` and `refreshAfter`. no
202 PostImagesAccepted Async mode (`webhook_url` was sent): accepted; the render runs in the background and its outcome is POSTed to the webhook as `image.completed` or `image.failed` (validation and quota failures arrive there, not here). **Webhooks.** When `webhook_url` is set, the outcome is POSTed there as JSON `{ id, type, created_at, data }` (`id` = the delivery id, `created_at` = unix seconds). The URL must be `https://`, is checked by an SSRF guard (private, loopback, link-local and metadata addresses are refused; ports 80/443 only; redirects are not followed) and is re-checked on every delivery attempt. **Signature.** Every request carries `X-Ogmake-Signature: t=<unix seconds>,v1=<hex>` where `v1 = HMAC-SHA256(key, t + "." + rawBody)` in lowercase hex, and `key` is the raw bytes of the base64-decoded signing secret of the API key that submitted the job (the same secret that signs `/i` URLs). Recompute it over the exact raw body, compare in constant time, and reject a `t` more than 5 minutes old. Node: `crypto.createHmac('sha256', Buffer.from(signingSecretBase64, 'base64')).update(t + '.' + rawBody).digest('hex')`. Other headers: `X-Ogmake-Event` (the event type), `X-Ogmake-Delivery` (the delivery id, stable across retries — use it to de-duplicate). **Retries.** A non-2xx response, a timeout (8 s) or a network error is retried up to 3 more times, 1, 5 and 30 minutes apart (4 attempts in all); a refused target is not retried. Each delivery and its attempts are listed by `GET /v1/webhooks/deliveries`. Events: `batch.completed` (`data`: `batch_id`, `status`, `total`, `succeeded`, `failed`, `results_url`), `image.completed` (`data`: `request_id` plus the fields of the synchronous 201 response) and `image.failed` (`data`: `request_id`, `error`, `message`). no
400 object Invalid request no
401 object Missing/unknown/revoked API key no
402 object Async mode only (`webhook_url`): no credit available for the job (plan credits up to the hard cap, plus unexpired top-ups on paid plans). Nothing is stored or charged. Auto mode: `paid_only` on the free plan. no
403 object `url` is not on the saved URL template's hosts/path (`source_host_not_allowed`). Nothing is fetched or charged. Auto mode: `forbidden` when auto mode is not enabled for the account. no
409 object Auto mode: this `Idempotency-Key` was used in the last 24 hours with a different request body (`idempotency_conflict`), or the first request with it is still running (`idempotency_in_progress`, with `Retry-After`). Auto mode with a `hint`: `hint_in_progress` while the same hint for the same page is being read by another request. yes
413 object Request body too large: 64 KB by default; up to 300 KB only for raw `html` requests that send the `X-Ogmake-Raw: html` header. Auto mode with a screenshot: the screenshot is over 5 MB, or the multipart body is over its own cap. no
422 object Saved URL template: the page could not be read (`source_unavailable`) or it has no value for a required field (`source_field_missing`, `detail` names the field). Auto mode with a `hint` and no screenshot: `page_unreadable` when the page's text could not be read; attach a screenshot or set the card fields in `params`. Nothing is charged. no
429 object | QuotaExceeded | object Rate limited, quota exhausted, or (async mode) 50 unfinished async jobs already in progress. Auto mode: `quota_exceeded` when the account is past its monthly cap and the page has no paid 90-day window (`Retry-After` until the next billing period); `rate_limited` is the key's own rate limit. Auto mode with a `hint`: `quota_exceeded` also when a new hint's 2 credits (plus the card's 3 on a new page: 5) do not fit; `ai_limit_reached` when the account's AI reads for the month are used up (`Retry-After` until the next month). yes
500 object Unexpected server error (e.g. a database dependency failure) no
503 object Render engine unavailable. Auto mode: `temporarily_unavailable` when the page's product image could not be fetched just now; nothing is charged. With a `hint` or screenshot: `ai_unavailable` when the AI read failed or AI reading is paused; nothing is charged and nothing retries on its own (`Retry-After`). yes

POST /v1/url-templates/analyze

Requires Authorization: Bearer og_live_....

Request body — AnalyzeUrlBody

Field Type Required
url string yes

Responses

Status Type Description Retry-After
200 AnalyzeUrlResponse What a URL template can pull from the page: the page type, candidate fields (text, prices, images, links) with their source and confidence, and suggested templates. Free; limited to 5 per minute and 30 per hour per account. no
400 object Not a public http(s) URL no
401 object Missing/unknown/revoked API key no
413 object Request body too large no
422 object The page could not be read no
429 object Rate limited (per minute, hour or day) yes

POST /v1/batch

Requires Authorization: Bearer og_live_....

Request body — PostBatchBody

Field Type Required
items BatchItem[] yes
webhook_url string no

Responses

Status Type Description Retry-After
202 PostBatchResponse Accepted; rendering has started. no
400 object Invalid request; `detail[].field` names `items[i].field`, a CSV header/row, or `webhook_url`. no
401 object Missing/unknown/revoked API key no
402 object Not enough credits for the whole batch: the item count exceeds the credits available (plan credits left up to the hard cap, plus unexpired render top-ups on paid plans); `detail` states both numbers. Nothing is stored or charged. no
413 object Request body larger than 1 MiB no
429 object Rate limited, or 5 unfinished batches already in progress yes
500 object Unexpected server error (e.g. a database dependency failure) no

GET /v1/batch/{id}

Requires Authorization: Bearer og_live_....

Responses

Status Type Description Retry-After
200 BatchStatus Batch status with per-item stored URLs or errors no
401 object Missing/unknown/revoked API key no
404 object No such batch for this account no
429 object Rate limited yes

GET /v1/webhooks/deliveries

Requires Authorization: Bearer og_live_....

Responses

Status Type Description Retry-After
200 WebhookDeliveriesResponse Deliveries with their attempt counts and last result no
400 object Invalid `limit` no
401 object Missing/unknown/revoked API key no
429 object Rate limited yes

GET /v1/usage

Requires Authorization: Bearer og_live_....

Responses

Status Type Description Retry-After
200 UsageResponse Current-period usage for the authenticated account no
401 object Missing/unknown/revoked API key no
429 object Rate limited yes

GET /v1/templates

Responses

Status Type Description Retry-After
200 TemplatesResponse Every registered template's id, version, and params JSON Schema no

Error codes

Every error response is JSON { "error": <code> }.

Code Meaning
invalid_input Request body or params failed validation.
unknown_template `template` does not match a registered template id.
params_too_large Encoded params exceeded the size limit.
invalid_json Request body was not valid JSON.
unauthorized Missing or unknown API key.
unauthenticated No session — sign in first.
key_revoked The API key exists but has been revoked.
forbidden Authenticated, but not allowed to perform this action.
not_found Nothing at this path/id.
rate_limited Per-key burst limit hit; see `Retry-After`.
quota_exceeded Past the monthly cap and its grace window.
render_unavailable The render engine is temporarily unavailable.
key_limit This account already has the maximum of 5 active API keys.
invalid_signature The webhook signature did not verify (or no webhook secret is configured).
no_subscription No billing subscription exists yet for this account.
billing_unavailable The billing provider's API failed; retry after the given `Retry-After`.