API Reference
A small, conventional REST API: submit a job, poll it, fetch a branded, verified artifact. Stdlib-only clients welcome — no SDK required.
DocMark’s /v1 API turns your content into a branded, verified document — deck, Word, Excel,
brochure, or newsletter — in your (or your customer’s) corporate brand. Your software authors the
content; DocMark renders the design deterministically and verifies the output before returning it.
Availability. The managed cloud API is available now, and API access is included in every plan —
/v1draws on the same monthly render credits. Your workspace’s admins can create and revoke tokens themselves: sign in at app.docmark.md and open API tokens. If you are not an admin, or your workspace has none yet, email hello@docmark.md and we will set one up. The same API also runs self-managed — on-prem or your own cloud tenant (deployment modes).
Authentication
Every request carries a bearer token scoped to one tenant:
Authorization: Bearer <your-api-token>
Tokens are issued per tenant. An auth failure never distinguishes between missing, malformed and
revoked — no oracle — but the status code differs by endpoint: submitting returns 403
({"error": "invalid or missing API token"}), while GET /v1/usage returns 401. Handle
both if you key retry or token-refresh logic on the status code.
Submit a job — POST /v1/jobs
Send a job spec; get back a job id. The request is strictly validated — unknown keys are rejected (so a typo fails loudly instead of silently doing the wrong thing).
curl -sS https://docmark-api.happyriver-6d7d834c.australiaeast.azurecontainerapps.io/v1/jobs \
-H "Authorization: Bearer $DOCMARK_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"theme": "slate",
"type": "brochure",
"topic": "Q3 workplace strategy for a national occupier",
"pages": 2
}'
# -> { "job_id": "job_...", "state": "pending", "contract_version": "1.18.0" }
Request fields
theme is the only required field. Everything else is optional, and unknown keys are rejected.
| Field | Type | Applies to | Notes |
|---|---|---|---|
theme | string | all | Required. Preset name; membership validated server-side. |
type | string | all | deck (default) · docx · xlsx · brochure · newsletter · video |
topic | string | deck, video, docx, brochure | Free-text topic DocMark writes from. A deck, video or docx needs topic or content (a docx can send markdown instead); a brochure needs topic. |
content | array | deck, video, docx | Structured sections {heading, bullets}, rendered verbatim — never re-authored by AI. If topic is also sent, content wins. A docx from content needs some body text. Refused on brochure (400): a brochure is written from topic. For a Word document from prose, markdown is usually the better fit. |
markdown | string | docx | Caller-prepared Markdown rendered faithfully to a branded .docx, AI off — the model never rewrites your words. |
title | string | all | Max 200 chars. xlsx uses it as the workbook title. |
subtitle | string | all | Max 160 chars. |
profile | string | all | Design profile — a look variant of the same brand, e.g. engineering. |
pages | integer | brochure | 1–4, honoured exactly. Absent = 1. |
charts | array | deck | Caller-authored chart specs: {kind: bar|line|pie, title, categories, series} |
sheets | array | xlsx | Caller-authored sheets — tables and/or native charts. |
items | array | newsletter | Update items {heading, body}. Caps enforced server-side. |
intro | string | newsletter | Intro paragraph. |
issue | string | newsletter | Dated standfirst, e.g. Product update · June 2026. |
footer | string | newsletter | Footer line. |
language | string | video | en (default) or zh (Simplified Chinese). Narration and slide-text language. Any other value is rejected. |
voice | string | video | male (default) or female. |
web_search | boolean | none | Reserved. true is refused with 400 on every type (contract 1.18.0); false is accepted and does nothing. |
input_blobs | array | deck | Job-scoped input files for spreadsheet→chart extraction (contract 1.6.0+). |
tenant | string | operator only | Render another tenant’s brand. Honoured only for entitled operator tokens. |
The full machine-readable shape, including response types, is in job.schema.json.
type: "video" is a premium, entitlement-gated output: unless your tenant is entitled to video, a
video submit is rejected with 402 before it is enqueued (default-deny). Ask us to enable video on
your account; all other types are available to any valid token.
Poll status — GET /v1/jobs/{id}
curl -sS https://docmark-api.happyriver-6d7d834c.australiaeast.azurecontainerapps.io/v1/jobs/job_... -H "Authorization: Bearer $DOCMARK_TOKEN"
# -> { "state": "pending" | "processing" | "done" | "failed", "findings": "...", "provenance": {...}, ... }
state: "pending" | "processing"— still queued or mid-render; keep polling.state: "done"— the artifact is ready; fetch it below.state: "failed"— the job carriesfindings: a string, up to 2000 characters, giving the recorded reason the job failed. A verification-gate withhold puts its machine-readable findings JSON here; any other failure (a render error, a rejected spec) puts its reason. Fix and resubmit.provenance— on a done job, a manifest of the brand, design profile (if one was selected), and engine that produced the artifact.brand_sourcesays where the brand came from:mount(DocMark’s versioned brand store),baked(the brand built into the worker image) ordefault.brand_versionis set only whenbrand_sourceismount, and is null otherwise. Best-effort: the key is absent when the sidecar was not written (a failed write, older results, custom queues) or cannot be read, so read it defensively rather than assuming it is there.
Fetch the artifact — GET /v1/jobs/{id}/result
Returns the raw bytes with the correct content type (.pptx / .docx / .xlsx / .pdf / .png /
.mp4). A 409 means “authorized but not rendered yet — keep polling.”
Submit and wait — POST /v1/jobs:render
A convenience wrapper for short jobs: submits through the same path and blocks until the artifact is
ready, returning the bytes directly (?timeout_s=, clamped 5–120, default 90). On timeout it
returns 202 and the job keeps running — poll GET /v1/jobs/{id}. Async-only types (video,
newsletter, brochure) are rejected here; use the async submit for those.
Usage — GET /v1/usage
Your token’s own tenant render-budget usage: the daily sliding-window count plus your monthly quota
(monthly_used, monthly_quota, monthly_unlimited, resets_at). Bearer-auth only; a dedicated
read rate limit that never consumes your submit budget.
Rate limits & quotas
Each render debits one credit against your monthly quota (Individual 20, Studio 50, Team 300), with a
derived daily burst cap. When a limit is reached, a submit is rejected with 429 before it is
enqueued — code credit_exhausted (daily burst) or monthly_quota_reached (monthly quota) — each
carrying a Retry-After header and an upgrade_url. Check current consumption with GET /v1/usage.
The job contract is versioned
Every response echoes contract_version (currently 1.18.0). The wire schema is producer-owned
and pinned by conformance tests, so your integration won’t silently break — additive fields bump the
minor version, and the schema is published for codegen: job.schema.json.
The verification promise
DocMark withholds a deliverable that fails its structural/visual gates rather than shipping a
broken one. A withheld job fails with findings your agent or code can act on — the same loop a
human would run, automated. This is the difference between “an AI made a slide” and “a verified,
on-brand document your customer can send.”
Next: connect your AI agent over MCP · deployment modes & where the API runs · request access