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. Tokens are issued by hand while we onboard teams personally: 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.14.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 | Free-text topic. At least one of topic or content is required. |
content | array | deck | Structured sections, rendered verbatim — never re-authored by AI. Not read on docx or brochure — those author from topic. For a faithful document, use markdown. |
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 | deck, brochure | Server-side search while authoring. API-mode workers only. |
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 exactly which brand version, design profile, and engine produced the artifact. Best-effort: the key is absent when the sidecar is missing or unreadable (older results, custom queues), 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.14.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