01Basics
Every job request needs an API key. Send it as a bearer token:
Authorization: Bearer
Keys are scoped: a job can only be read, downloaded, or cancelled by the
key that created it. Requests are rate-limited per key
(X-RateLimit-Limit / X-RateLimit-Remaining headers).
Endpoints
Job status & progress
Download the rendered file
Cancel a pending job
Job lifecycle
Poll GET /v1/jobs/:id until a terminal state
(completed, failed, cancelled).
Results are retained for a limited time (default 7 days).
02The archive
Upload your project as a ZIP. It must contain a root document — either
a file named main.g.md, or exactly one *.g.md file.
Other files (images, fragments, fonts) are assets referenced by that document
and are bundled inside the archive.
project.zip
├── main.g.md ← root document
├── img/logo.png
└── sections/part.g.md
03Create a job
format—pdf(default) ordocxIdempotency-Keyheader — required, unique, 1–255 chars ofA-Za-z0-9_-- Body —
multipart/form-datawith afilefield containing the ZIP
curl -X POST \
-H "Authorization: Bearer $KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-F "file=@project.zip" \
"http://localhost:3000/v1/jobs?format=pdf"
{
"id": "3f2a9e4c-...",
"status": "queued",
"statusUrl": "/v1/jobs/3f2a9e4c-...",
"resultUrl": "/v1/jobs/3f2a9e4c-.../result"
}
Reusing the same Idempotency-Key returns the existing job instead
of duplicating it. Reusing it with different data returns 409.
04Check status
{
"id": "3f2a9e4c-...",
"status": "processing",
"format": "pdf",
"progress": { "percent": 44, "stage": "rendering", "message": "..." },
"warnings": null,
"createdAt": "2026-09-04T18:00:00.000Z",
"finishedAt": null,
"errorCode": null,
"error": null
}
While processing, progress reports percent, stage, and a message.
On failure, errorCode is machine-readable and error
is a human-friendly explanation.
05Download the result
curl -OJ \
-H "Authorization: Bearer $KEY" \
"http://localhost:3000/v1/jobs/$JOB/result"
| Status | Meaning |
|---|---|
200 | File bytes (PDF or DOCX) |
409 | Not ready yet |
422 | Job failed — no result |
404 | Unknown job |
06Cancel a job
Cancels a pending or queued job. Once processing has
started, cancellation is rejected with 409 job_already_processing.
07Errors
Errors are JSON with an error code, and often a
message. Below are the most common ones.
| Code | Status | Meaning |
|---|---|---|
missing_api_key | 401 | No bearer token |
invalid_api_key | 401 | Unknown or revoked key |
rate_limit_exceeded | 429 | Per-key request limit |
invalid_idempotency_key | 400 | Header missing or malformed |
idempotency_key_reused | 409 | Key reused with different data |
invalid_format | 400 | Use pdf or docx |
invalid_file_type | 400 | Must be a ZIP archive |
file_too_large | 413 | ZIP exceeds max size |
job_quota_exceeded | 429 | Your job limit is full |
global_job_limit_exceeded | 429 | Service queue is full |
job_not_found | 404 | No such job for this key |
result_not_ready | 409 | Job hasn’t finished |
render_failed | 422 | Job failed, no result |
job_already_processing | 409 | Cannot cancel a running job |
Async errors
Reported in the errorCode of a failed job:
| Code | Meaning |
|---|---|
archive | Corrupt, protected, or unsupported archive |
archive:no_main | No root main.g.md found |
render | Unexpected rendering error |
render:bad_input | Syntax errors or unsupported features |
render:timeout | Rendering timed out |
output_too_large | Output exceeds max size |
timeout | Overall job timeout |