Skip to content

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).


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.

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 on detail.
  • type is a URI built from the code (code with underscores turned into dashes), under https://docs.madoo.ai/public-api/errors/. It identifies the error type; branch on code.
  • errors[] appears only for field-level validation failures (code: validation_error).
  • request_id is the correlation ID for that request — include it in any support request.

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).

Batch / Embed (06, 07)

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.

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 an X-RateLimit-Limit header. 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).

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.


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.


GET {BASE_URL}/api/v1/plan

Reports 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_days matters 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.


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.


All list endpoints share the same envelope and cursor mechanics:

{ "data": [ … ], "has_more": true, "next_cursor": "…", "total_count": 137 }
  • limit 1–100 (default 25; batch items default 50).
  • Pass next_cursor as starting_after for the next page.
  • Stop when has_more is false.
  • Catalog endpoints (node types, models, presets, effects, capabilities) return the full catalog in one response when limit is omitted — paging there is opt-in (09-catalog.md).

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.