Reference
The appendix: status codes, the error-code catalogue, rate-limit specifics, ID prefixes, status enums, and the plan/storage endpoints. Keep this open while you build.
For the precise request/response schema of any endpoint, also consult the auto-generated OpenAPI
spec at {BASE_URL}/swagger/public-v1/swagger.json and the interactive docs at {BASE_URL}/docs
(README §5).
1. HTTP status codes
Section titled “1. HTTP status codes”| Status | Meaning in this API |
|---|---|
200 OK |
Success (reads, and operations that return the updated resource). |
201 Created |
A resource was created (asset upload, embed token). |
202 Accepted |
Async work accepted (execution / batch submitted; ZIP export building). |
204 No Content |
Success with no body (delete, revoke). |
302 Found |
Redirect to a file (ZIP download endpoints — follow it). |
400 Bad Request |
Malformed request: bad ID format, invalid body, validation failure. |
401 Unauthorized |
Missing/invalid/expired bearer token, or bad client credentials at the token endpoint. |
402 Payment Required |
Storage quota exceeded. |
403 Forbidden |
No workspace context, accessing another workspace’s resource, or an operation needing API key auth. |
404 Not Found |
Resource does not exist (or is not published, for workflows). |
409 Conflict |
Resource state conflict. |
413 Payload Too Large |
Uploaded file exceeds 250 MB. |
422 Unprocessable Entity |
Valid request, but the resource state forbids it (e.g. outputs not ready; cancel an already-terminal run). |
429 Too Many Requests |
Rate limit exceeded — back off and retry. |
500 Internal Server Error |
Unexpected server error. Quote the request_id. |
2. Error response shape (RFC 7807)
Section titled “2. Error response shape (RFC 7807)”Every error body follows RFC 7807 Problem Details:
{ "type": "https://docs.madoo.ai/public-api/errors/workflow-not-found", // URI for the error type "title": "Not Found", // short title for the HTTP status "status": 404, // HTTP status code "detail": "Workflow wf_… not found.", // human-readable, may change "code": "workflow_not_found", // machine-readable — branch on THIS "instance": "/api/v1/executions", // the request path "request_id": "0HMÉ…", // correlation id — quote it to support "errors": [ // present for field validation only { "field": "allowed_origins", "message": "At least one allowed origin is required." } ]}- Branch on
code, never ondetail. typeis a URI built from the code (codewith underscores turned into dashes), underhttps://docs.madoo.ai/public-api/errors/. It identifies the error type; branch oncode.errors[]appears only for field-level validation failures (code: validation_error).request_idis the correlation ID for that request — include it in any support request.
3. Error-code catalogue
Section titled “3. Error-code catalogue”Codes you will encounter, grouped by area. (Not exhaustive — always read the code field at
runtime.)
Authentication (01)
code |
HTTP | Meaning |
|---|---|---|
invalid_request |
400 | Missing grant_type or malformed Basic header. |
unsupported_grant_type |
400 | grant_type ≠ client_credentials. |
invalid_client |
401 | Bad/missing credentials, or key revoked/expired. |
Workflows (03)
code |
HTTP | Meaning |
|---|---|---|
invalid_id |
400 | Workflow ID not a valid wf_…. |
not_found |
404 | Workflow missing from the workspace. |
invalid_version |
400 | Requested version out of range. |
version_not_available |
404 | That version’s definition is unavailable. |
invalid_status |
400 | status filter not one of draft/published/archived/all. |
Authoring (10) — validation_error (422) carries the structured
errors[]/warnings[] issue lists; the per-issue codes (unknown_node_type, unknown_preset,
unknown_model, cycle_detected, …) are catalogued in 10-authoring.md §2.
code |
HTTP | Meaning |
|---|---|---|
validation_error |
400 / 422 | 400: missing name/definition (field errors). 422: blocking definition issues (errors[]) — also refuses publish of an invalid definition. |
plan_limit_exceeded |
402 | Workflow cap (or custom-interface gate) of your plan reached. |
executions_in_progress |
409 | DELETE refused: non-terminal executions exist. |
invalid_state |
422 | Lifecycle transition not allowed (publish/revert of an archived workflow). |
precondition_failed |
412 | If-Match tag stale: the definition changed since you read it. |
idempotency_conflict |
409 | The same Idempotency-Key was reused with a different request payload. |
Executions (04)
code |
HTTP | Meaning |
|---|---|---|
invalid_request |
400 | Missing/invalid workflow. |
workflow_not_found |
404 | Workflow missing. |
workflow_not_published |
400 | Workflow exists but is not published. |
validation_error |
400 | Inputs or supplied execution bounds failed validation (for example a missing required input, malformed bound, or an exact plan already above that bound). |
invalid_id |
400 | Execution ID not a valid run_…. |
not_found |
404 | Execution missing. |
not_completed |
422 | Outputs requested before the run is terminal. |
already_terminal |
422 | Tried to cancel a finished run. |
invalid_status |
400 | ZIP requested for a non-completed run. |
Assets (05)
code |
HTTP | Meaning |
|---|---|---|
invalid_file |
400 | No/empty file. |
invalid_file_type |
400 | Extension not allowed. |
invalid_request |
400 | Missing path. |
forbidden |
403 | Asset in another workspace. |
not_found |
404 | No asset at path. |
file_too_large |
413 | Over 250 MB. |
quota_exceeded |
402 | Storage quota exceeded. |
unsupported_format |
415 | Structured-data inspection cannot read the selected/detected format. |
duplicate_column |
422 | CSV headers are ambiguous after trim/case-fold. |
ambiguous_table |
422 | JSON exposes multiple arrays and table was not selected. |
invalid_data |
422 | CSV/JSON content is malformed or not a tabular row collection. |
row_limit_exceeded |
422 | Dataset cardinality exceeds the explicit inspection bound. |
source_changed |
409 | Asset ETag changed between validation and read; retry inspection. |
invalid_options |
400 | Bundle validation/checksum policy is unsupported. |
Bundle-level deterministic problems such as ASSET_CHECKSUM_MISMATCH, DUPLICATE_ASSET_ID,
INVALID_RELATIVE_PATH, and ASSET_CONTENT_SIGNATURE_MISMATCH are returned in the successful
bundle-preflight response’s issues[]. A cross-workspace reference remains HTTP 403.
The contract, all authoring surfaces, and the distinction between logical paths and storage references
are documented in the canonical
Bundle manifest authoring guide.
Workflow definitions (10) — issue codes inside a 422 validation_error
(errors[], each with path, node_id and a suggestion), from REST validate/create/update, MCP
validate_workflow and the agent. The full list is in 10 §2;
the ones for nodes that fill a document template (design/template_render, document/pdf,
aggregate/pdf):
code |
Meaning |
|---|---|
missing_template_id |
The node needs template_id (a tpl_…), or template_revision was given without it. |
invalid_template_id |
template_id is not a tpl_… returned by design-template discovery. |
invalid_template_revision |
template_revision is not a positive number. |
template_not_found |
The template does not exist in this workspace (for design/template_render: is not published). |
template_revision_not_found |
That published revision does not exist. |
template_pin_failed |
design/template_render could not fix the selected revision (message says why). |
internal_template_parameter |
documentId, documentVersionId, templateContract or another internal document field was authored; use template_id / template_revision. |
unknown_port |
A connection names a port the node does not have; on a template node the message lists the template’s field codes. |
template_field_not_connected |
document/pdf or aggregate/pdf: a field the template marks required and gives no default value has nothing connected to its input port, so every run would fail. Connect a value to it, or give the field a default (or make it optional) in the template. |
Design templates (11)
code |
HTTP | Meaning |
|---|---|---|
invalid_id |
400 | Template ID not a valid tpl_…, or a page / element ID malformed. |
invalid_sample_set_id |
400 | Sample set ID malformed. |
invalid_status |
400 | status filter not one of published/draft/archived/all. |
invalid_revision |
400 | Revision number not positive. |
missing_idempotency_key |
400 | A draft edit, publish, create or duplicate without Idempotency-Key. |
validation_error |
400 | Command fields invalid (the message names the field). |
not_found |
404 | Template, revision or draft element missing from the workspace. |
page_not_found / master_not_found / sample_set_not_found |
404 | The named page, master page or sample set is not in the draft. |
precondition_required |
428 | If-Match missing: read the draft (outline or content) first. |
precondition_failed |
412 | If-Match stale: the draft changed since you read it. |
idempotency_conflict |
409 | The same Idempotency-Key was reused with a different request. |
archived |
409 | The template is archived: it cannot be edited or returned to draft. |
content_invalid |
422 | A whole document (PUT …/draft/content, create with content) failed validation; errors[] lists every problem (field = JSON path). |
publish_not_ready |
422 | The draft fails a publish-readiness check. |
invalid_argument / design_page_selection_invalid |
400 | Direct render: page_selection or another argument is invalid. |
preview_failed / render_failed |
422 | The exact preview or the direct render could not complete (message says why; a render may return the service’s own code instead of render_failed). |
render_too_large |
413 | Direct render over the page/size bound: use a workflow. |
design_render_busy |
503 | The render pool is saturated; retry after Retry-After. |
Workflow folders (03)
code |
HTTP | Meaning |
|---|---|---|
invalid_id |
400 | Not a fld_… (or root), or a workflow ID not a wf_…. |
not_found |
404 | Folder missing from the workspace. |
conflict |
409 | A folder with that name already exists in the same location (create, rename or move). |
code |
HTTP | Meaning |
|---|---|---|
invalid_request |
400 | Empty items / invalid workflow. |
batch_create_failed |
400 | Batch could not be created. |
invalid_state |
422 | start/cancel not allowed from the current batch state. |
no_failed_items |
422 | Nothing to retry. |
validation_error |
400 | Invalid embed token request (field errors). |
api_key_required |
403 | Embed token creation needs API key auth. |
invalid_jti |
400 | Embed token id not an emb_…. |
Cross-cutting
code |
HTTP | Meaning |
|---|---|---|
too_many_requests |
429 | Rate limit exceeded. |
internal_error |
500 | Unexpected server error. |
4. Rate limiting
Section titled “4. Rate limiting”Madoo rate-limits the Public API to protect the platform. Two policies apply:
| Policy | Applies to | Limit |
|---|---|---|
| Public API | All /api/v1/* endpoints except the token endpoint |
Plan-based sliding window, partitioned per user/organization. Fallback when no plan limit is configured: 60 requests/minute. |
| Auth | POST /api/v1/auth/token |
Sliding window per IP. Production: a small number of requests per 15-minute window (strict, to deter credential guessing). Development: ~100/minute. |
Behaviour:
- Responses to
/api/v1/*include anX-RateLimit-Limitheader. Treat it as an advisory hint, not a precise live budget — the authoritative signal is the 429 response. - Exceeding a limit returns HTTP 429 with a Problem Details body (
code: too_many_requests). - Build a backoff-and-retry into your client (exponential backoff with jitter). For the token endpoint specifically, cache the token rather than minting one per request (01-authentication §4).
5. ID prefixes
Section titled “5. ID prefixes”| Prefix | Resource | Endpoints |
|---|---|---|
wf_ |
Workflow | /api/v1/workflows |
run_ |
Execution | /api/v1/executions (⚠️ the execution ZIP endpoints take the raw GUID — strip run_) |
bat_ |
Batch execution | /api/v1/batch-executions (ZIP endpoints keep the bat_ prefix) |
emb_ |
Embed token or editor embed token (jti) |
/api/v1/embed/tokens, /api/v1/embed/editor/tokens |
tpl_ |
Document template (DesignDocument) | /api/v1/design-templates; template_id of template nodes in workflow definitions |
tplv_ |
Immutable published revision of a template | returned by publish, /versions and contracts |
fld_ |
Workflow folder (root = the top level) |
/api/v1/workflow-folders |
IDs are opaque — pass back exactly what the API returned.
6. Status enums
Section titled “6. Status enums”Execution status (status on an execution; terminal values marked ✅):
pending · running · completed✅ · partial_success✅ · failed✅ · cancelled✅
Batch status (status on a batch):
pending · running · completed · partial_success · failed · cancelled
Batch item status (status on a batch item): mirrors execution states (pending, running,
completed, failed, cancelled).
Design template status (status on a template): draft (never published, or returned to draft) ·
published (selectable; its draft stays editable for the next revision) · archived (hidden from
selection; pinned workflows keep their revision). List filter: published (default), draft,
archived, all.
7. Plan, limits, and usage
Section titled “7. Plan, limits, and usage”GET {BASE_URL}/api/v1/planReports your organization’s plan, its limits, and current usage — useful for showing remaining credits or guarding against limits before submitting work.
{ "plan_code": "growth", "plan_name": "Growth", "limits": { "max_credits_per_month": 5000, "max_workflows": 100, "max_users": 25, "max_workspaces": 10, "max_storage_bytes": 53687091200, "max_concurrent_executions": 20, "output_retention_days": 90, "api_access": "enabled", "embed_access": "enabled" }, "usage": { "current_workflows": 12, "current_users": 4, "current_storage_bytes": 1048576000, "credits_used_this_month": 1840 }}
output_retention_daysmatters for integrators: generated outputs (and their download URLs) are retained for this many days. Download and persist anything you need to keep beyond that window.
8. Storage usage and quota
Section titled “8. Storage usage and quota”GET {BASE_URL}/api/v1/storage/usage{ "total_bytes": 1048576000, "file_count": 1342, "breakdown": [ { "category": "uploads", "total_bytes": 524288000, "file_count": 420 }, { "category": "outputs", "total_bytes": 524288000, "file_count": 922 } ]}GET {BASE_URL}/api/v1/storage/quota{ "current_bytes": 1048576000, "max_bytes": 53687091200, // null if no quota configured "usage_percent": 1.95, "is_warning": false, // approaching the limit "is_blocked": false // at/over the limit — uploads may be refused (402)}When is_blocked is true, uploads can be rejected with 402 (quota_exceeded). Watch
is_warning to clean up or upgrade before you hit the wall.
9. Pagination recap
Section titled “9. Pagination recap”All list endpoints share the same envelope and cursor mechanics:
{ "data": [ … ], "has_more": true, "next_cursor": "…", "total_count": 137 }limit1–100 (default 25; batch items default 50).- Pass
next_cursorasstarting_afterfor the next page. - Stop when
has_moreisfalse. - Catalog endpoints (node types, models, presets, effects, capabilities) return the full
catalog in one response when
limitis omitted — paging there is opt-in (09-catalog.md).
10. Machine discovery (/.well-known/)
Section titled “10. Machine discovery (/.well-known/)”The API describes itself to agents and tooling through standard discovery documents
(RFC 8615). All of them are anonymous (no token needed — discovery happens before you
hold a credential) and cacheable (Cache-Control: public, max-age=3600):
| Document | URL | Format |
|---|---|---|
| API catalog | GET /.well-known/api-catalog |
RFC 9727 linkset (application/linkset+json): service-desc → the OpenAPI document at /swagger/public-v1/swagger.json, service-doc → the interactive docs at /docs. |
| Agent-skills index | GET /.well-known/agent-skills/index.json |
Cloudflare Agent Skills Discovery RFC v0.2.0: the published skills with sha256:{hex} content digests. |
madoo-workflows skill |
GET /.well-known/agent-skills/madoo-workflows/SKILL.md |
text/markdown — agent-oriented instructions for the full lifecycle (auth → catalog discovery → authoring → publish → execute → outputs), condensed from these docs. Verify the bytes against the index digest. |
An agent runtime that supports skill discovery can point at the host and pick up the
madoo-workflows skill with zero configuration; everything in it links back to the chapters
in this guide for depth.
That’s the whole Public API v1. If something here disagrees with the live
{BASE_URL}/swagger/public-v1/swagger.json, trust the spec (it is generated from the running code)
and let us know so we can update this guide.