REST API

Drive MadMax from CI, scripts, or your own services — the same workflows, runs, and lifecycle verbs as the canvas. Paste a token below and every endpoint on this page is live.

base url https://madmax.build/api/v1

Authentication

Every request carries a bearer token minted in Settings → developer. Tokens are pinned to ONE workspace — all reads and writes happen there, no matter what else your account can see. Read-scope tokens may only GET; the build scope unlocks the write verbs. Two headers are mandatory on every call:

Authorization: Bearer mmx_... Content-Type: application/vnd.api+json

Stays in this browser (localStorage) and is sent ONLY as the Authorization header of requests you fire from this page — never to our servers otherwise. Every endpoint card below gets a try it panel.

Conventions

Two response shapes
Resource routes (list / get / patch / delete) speak JSON:API: enveloped {"data": {"type", "id", "attributes"}} objects. The lifecycle verbs (run, activate, export, …) take a flat {"data": {<args>}} body and return plain JSON — zero-argument verbs can be POSTed with no body at all.
Pagination
Lists are keyset-paginated: ?page[limit]=25 (max 100), then follow the links.next URL — or pass page[after]=<cursor> yourself.
Sparse fieldsets
Heavy payloads stay off the wire until asked for: GET /executions/:id?fields[execution]=status,output,error pulls a run's full output; fields[execution-step]=node_id,status,output does the same per step.
Create from a document
POST /workflows accepts an optional document — a madmax/v1 export (from the export endpoint), an n8n export, or a bare React-Flow {"nodes": [...], "edges": [...]} graph — and reports placeholders + connection requirements to finish by hand:
{"data": {"name": "My workflow", "document": {"format": "madmax/v1", ...}}}
Errors
JSON:API error objects: {"errors": [{"status", "title", "detail"}]} — 401 bad/expired token, 403 read-only token on a write, 404 outside the pinned workspace, 400/422 invalid input, 429 rate limited (with retry-after).

Token

GET /api/v1/me returns 200

Token introspection — the authenticated user, the pinned workspace (id, name, slug), and the token's scopes + expiry. The first call to make when wiring up a client.

try it

Store transfer

Whole-store dump and restore. These two routes stream — constant memory at any row count — so they live outside the JSON:API surface. The read routes for stores and rows are under Stores below.

GET /api/v1/stores/:id/export?format=

Streamed dump. format=ndjson (default) is the lossless MadMax dump — a self-describing header line (name, slug, schema), then one row per line; the only format that re-imports. format=csv and format=json are for humans and BI tools.

POST /api/v1/stores/import?slug=

Restore an NDJSON dump (build-scope token). Always creates a NEW store — a taken slug is refused, never merged into. Rows write through schema validation with per-row failures reported. slug= names the new store.

Workflows

List, create (bare or from a document), rename, export, edit the graph, activate/deactivate, run, duplicate, and delete workflows.

GET /api/v1/workflows returns 200

Workflows in the token's workspace, newest first.

param in type required
filter query no
sort query string no
page query object no
include query string no
fields query object no
curl -X GET https://madmax.build/api/v1/workflows \
  -H "Authorization: Bearer $MADMAX_API_TOKEN" \
  -H "Content-Type: application/vnd.api+json"
try it
POST /api/v1/workflows returns 201

Create a workflow — bare (name only) or from a document: a madmax/v1 export, an n8n export, or a bare {nodes, edges} graph. Returns a summary + placeholder/requirement report for document imports.

curl -X POST https://madmax.build/api/v1/workflows \
  -H "Authorization: Bearer $MADMAX_API_TOKEN" \
  -H "Content-Type: application/vnd.api+json" \
  -d '{"data":{"description":"","document":"","name":""}}'
try it
GET /api/v1/workflows/map returns 200

The workspace's call graph laid out as the index's graph view: `systems` (connected groups of ≥2 workflows — columns per call depth, drawn edges, folder label, strays) and `standalone`. Optional `search` / `status` narrow before layout.

param in type required
search query string no
status query string no
curl -X GET https://madmax.build/api/v1/workflows/map \
  -H "Authorization: Bearer $MADMAX_API_TOKEN" \
  -H "Content-Type: application/vnd.api+json"
try it
GET /api/v1/workflows/{id} returns 200

One workflow by id, pinned-workspace only.

param in type required
id path string yes
include query string no
fields query object no
curl -X GET https://madmax.build/api/v1/workflows/{id} \
  -H "Authorization: Bearer $MADMAX_API_TOKEN" \
  -H "Content-Type: application/vnd.api+json"
try it
PATCH /api/v1/workflows/{id} returns 200

Rename and/or set the description. Nothing else is PATCHable.

param in type required
id path string yes
include query string no
fields query object no
curl -X PATCH https://madmax.build/api/v1/workflows/{id} \
  -H "Authorization: Bearer $MADMAX_API_TOKEN" \
  -H "Content-Type: application/vnd.api+json" \
  -d '{"data":{"attributes":{},"id":"{id}","type":"workflow"}}'
try it
DELETE /api/v1/workflows/{id} returns 200

/workflows/:id operation on workflow resource

param in type required
id path string yes
include query string no
fields query object no
curl -X DELETE https://madmax.build/api/v1/workflows/{id} \
  -H "Authorization: Bearer $MADMAX_API_TOKEN" \
  -H "Content-Type: application/vnd.api+json"
try it
POST /api/v1/workflows/{id}/activate returns 201

Freeze the canvas into a snapshot and start firing triggers.

param in type required
id path string yes
curl -X POST https://madmax.build/api/v1/workflows/{id}/activate \
  -H "Authorization: Bearer $MADMAX_API_TOKEN" \
  -H "Content-Type: application/vnd.api+json"
try it
GET /api/v1/workflows/{id}/calls returns 200

The call graph around one workflow: `callers` (workflows whose call_workflow steps target it) and `callees` (workflows it calls). Distinct, pinned-workspace only.

param in type required
id path string yes
curl -X GET https://madmax.build/api/v1/workflows/{id}/calls \
  -H "Authorization: Bearer $MADMAX_API_TOKEN" \
  -H "Content-Type: application/vnd.api+json"
try it
POST /api/v1/workflows/{id}/deactivate returns 201

Stop firing triggers; the snapshot survives for audit.

param in type required
id path string yes
curl -X POST https://madmax.build/api/v1/workflows/{id}/deactivate \
  -H "Authorization: Bearer $MADMAX_API_TOKEN" \
  -H "Content-Type: application/vnd.api+json"
try it
POST /api/v1/workflows/{id}/duplicate returns 201

Copy into the same workspace as "<name> (copy)"; connection bindings reset.

param in type required
id path string yes
curl -X POST https://madmax.build/api/v1/workflows/{id}/duplicate \
  -H "Authorization: Bearer $MADMAX_API_TOKEN" \
  -H "Content-Type: application/vnd.api+json"
try it
GET /api/v1/workflows/{id}/export returns 200

The workflow as a portable madmax/v1 document (env ids stripped).

param in type required
id path string yes
curl -X GET https://madmax.build/api/v1/workflows/{id}/export \
  -H "Authorization: Bearer $MADMAX_API_TOKEN" \
  -H "Content-Type: application/vnd.api+json"
try it
PUT /api/v1/workflows/{id}/graph returns 200

Replace the live canvas graph (same write path as autosave). Runs auto-layout afterwards unless layout: false — positions in the payload are then kept.

param in type required
id path string yes
curl -X PUT https://madmax.build/api/v1/workflows/{id}/graph \
  -H "Authorization: Bearer $MADMAX_API_TOKEN" \
  -H "Content-Type: application/vnd.api+json" \
  -d '{"data":{"graph":{},"layout":""}}'
try it
PATCH /api/v1/workflows/{id}/move returns 200

Move a workflow into a folder (folder_id, null = root) and/or reorder it (before_id / after_id).

param in type required
id path string yes
include query string no
fields query object no
curl -X PATCH https://madmax.build/api/v1/workflows/{id}/move \
  -H "Authorization: Bearer $MADMAX_API_TOKEN" \
  -H "Content-Type: application/vnd.api+json" \
  -d '{"data":{"attributes":{},"id":"{id}","type":"workflow"}}'
try it
POST /api/v1/workflows/{id}/run returns 201

Execute now. Active workflows run their pinned snapshot; draft: true runs the live canvas.

param in type required
id path string yes
curl -X POST https://madmax.build/api/v1/workflows/{id}/run \
  -H "Authorization: Bearer $MADMAX_API_TOKEN" \
  -H "Content-Type: application/vnd.api+json" \
  -d '{"data":{"draft":""}}'
try it

Executions

Run history. Heavy payloads (inputs, output, error) are opt-in via ?fields[execution]=status,output,error.

GET /api/v1/executions returns 200

Executions across the token's workspace, newest first.

param in type required
filter query no
sort query string no
page query object no
include query string no
fields query object no
curl -X GET https://madmax.build/api/v1/executions \
  -H "Authorization: Bearer $MADMAX_API_TOKEN" \
  -H "Content-Type: application/vnd.api+json"
try it
GET /api/v1/executions/{id} returns 200

One execution by id, pinned-workspace only.

param in type required
id path string yes
include query string no
fields query object no
curl -X GET https://madmax.build/api/v1/executions/{id} \
  -H "Authorization: Bearer $MADMAX_API_TOKEN" \
  -H "Content-Type: application/vnd.api+json"
try it
POST /api/v1/executions/{id}/cancel returns 201

Terminal stop for a pending/running/waiting execution — flips the row to :cancelled and kills the underlying Oban jobs (same path as the canvas Stop button). Fail-closed: the execution is re-fetched under the actor + pinned-workspace lens first.

param in type required
id path string yes
curl -X POST https://madmax.build/api/v1/executions/{id}/cancel \
  -H "Authorization: Bearer $MADMAX_API_TOKEN" \
  -H "Content-Type: application/vnd.api+json"
try it
GET /api/v1/workflows/{workflow_id}/executions returns 200

Executions for one workflow, newest first, pinned-workspace only.

param in type required
workflow_id path string yes
filter query no
sort query string no
page query object no
include query string no
fields query object no
curl -X GET https://madmax.build/api/v1/workflows/{workflow_id}/executions \
  -H "Authorization: Bearer $MADMAX_API_TOKEN" \
  -H "Content-Type: application/vnd.api+json"
try it

Execution steps

Per-node results within one run — status, timing, and (opt-in) input/output.

GET /api/v1/executions/{workflow_execution_id}/steps returns 200

/executions/:workflow_execution_id/steps operation on execution-step resource

param in type required
workflow_execution_id path string yes
filter query no
sort query string no
page query object no
include query string no
fields query object no
curl -X GET https://madmax.build/api/v1/executions/{workflow_execution_id}/steps \
  -H "Authorization: Bearer $MADMAX_API_TOKEN" \
  -H "Content-Type: application/vnd.api+json"
try it

Step templates

The primitive catalog (webhook, http, agent, code, …) with each template's config schema — everything needed to author graph nodes.

GET /api/v1/step-templates returns 200

Globally readable. Sorts by `sort_order` so the catalog renders in the same order as the canvas palette.

param in type required
filter query no
sort query string no
include query string no
fields query object no
curl -X GET https://madmax.build/api/v1/step-templates \
  -H "Authorization: Bearer $MADMAX_API_TOKEN" \
  -H "Content-Type: application/vnd.api+json"
try it

Folders

Organize workflows into folders (one level deep). Move a workflow with PATCH /workflows/:id/move; export/import a whole folder as one madmax-folder/v1 document.

GET /api/v1/folders returns 200

Folders in the token's workspace.

param in type required
filter query no
sort query string no
include query string no
fields query object no
curl -X GET https://madmax.build/api/v1/folders \
  -H "Authorization: Bearer $MADMAX_API_TOKEN" \
  -H "Content-Type: application/vnd.api+json"
try it
POST /api/v1/folders returns 201

Create a folder (one level deep — parent_id is optional).

param in type required
include query string no
fields query object no
curl -X POST https://madmax.build/api/v1/folders \
  -H "Authorization: Bearer $MADMAX_API_TOKEN" \
  -H "Content-Type: application/vnd.api+json" \
  -d '{"data":{"attributes":{},"relationships":{},"type":""}}'
try it
GET /api/v1/folders/{id} returns 200

Folders in the token's workspace.

param in type required
id path string yes
include query string no
fields query object no
curl -X GET https://madmax.build/api/v1/folders/{id} \
  -H "Authorization: Bearer $MADMAX_API_TOKEN" \
  -H "Content-Type: application/vnd.api+json"
try it
PATCH /api/v1/folders/{id} returns 200

Rename a folder.

param in type required
id path string yes
include query string no
fields query object no
curl -X PATCH https://madmax.build/api/v1/folders/{id} \
  -H "Authorization: Bearer $MADMAX_API_TOKEN" \
  -H "Content-Type: application/vnd.api+json" \
  -d '{"data":{"attributes":{},"id":"{id}","type":"workflow-folder"}}'
try it
DELETE /api/v1/folders/{id} returns 200

Delete a folder; its workflows move back to the root.

param in type required
id path string yes
include query string no
fields query object no
curl -X DELETE https://madmax.build/api/v1/folders/{id} \
  -H "Authorization: Bearer $MADMAX_API_TOKEN" \
  -H "Content-Type: application/vnd.api+json"
try it
PATCH /api/v1/folders/{id}/reorder returns 200

Reorder a folder among its siblings — before_id / after_id (the neighbouring folders at the drop point) compute the sparse position server-side; a raw position is also accepted. parent_id reparents (one level deep).

param in type required
id path string yes
include query string no
fields query object no
curl -X PATCH https://madmax.build/api/v1/folders/{id}/reorder \
  -H "Authorization: Bearer $MADMAX_API_TOKEN" \
  -H "Content-Type: application/vnd.api+json" \
  -d '{"data":{"attributes":{},"id":"{id}","type":"workflow-folder"}}'
try it

Stores

Workspace data tables your workflows fill (the Save to store step) and your systems read and write. Row writes over the API go through the same validated path as the step (schema, row cap, workspace). :id is the store's UUID, shown in Settings → stores.

GET /api/v1/stores returns 200

Stores in the token's workspace, oldest first.

param in type required
filter query no
sort query string no
page query object no
include query string no
fields query object no
curl -X GET https://madmax.build/api/v1/stores \
  -H "Authorization: Bearer $MADMAX_API_TOKEN" \
  -H "Content-Type: application/vnd.api+json"
try it
POST /api/v1/stores returns 201

/stores operation on store resource

param in type required
include query string no
fields query object no
curl -X POST https://madmax.build/api/v1/stores \
  -H "Authorization: Bearer $MADMAX_API_TOKEN" \
  -H "Content-Type: application/vnd.api+json" \
  -d '{"data":{"attributes":{},"relationships":{},"type":""}}'
try it
GET /api/v1/stores/{id} returns 200

Store descriptor — name, slug, schema shape, row count.

param in type required
id path string yes
include query string no
fields query object no
curl -X GET https://madmax.build/api/v1/stores/{id} \
  -H "Authorization: Bearer $MADMAX_API_TOKEN" \
  -H "Content-Type: application/vnd.api+json"
try it
PATCH /api/v1/stores/{id} returns 200

/stores/:id operation on store resource

param in type required
id path string yes
include query string no
fields query object no
curl -X PATCH https://madmax.build/api/v1/stores/{id} \
  -H "Authorization: Bearer $MADMAX_API_TOKEN" \
  -H "Content-Type: application/vnd.api+json" \
  -d '{"data":{"attributes":{},"id":"{id}","type":"store"}}'
try it
DELETE /api/v1/stores/{id} returns 200

/stores/:id operation on store resource

param in type required
id path string yes
include query string no
fields query object no
curl -X DELETE https://madmax.build/api/v1/stores/{id} \
  -H "Authorization: Bearer $MADMAX_API_TOKEN" \
  -H "Content-Type: application/vnd.api+json"
try it

Store rows

Keyed rows within a store, ascending by update time. Poll by following links.next (a durable keyset cursor), or restart from a timestamp with ?updated_since=. ?workflow_id= filters to rows a workflow last wrote.

GET /api/v1/stores/{store_id}/rows returns 200

Rows ascending by update time. Poll by following links.next, or restart from a timestamp with ?updated_since=.

param in type required
store_id path string yes
filter query no
sort query string no
page query object no
include query string no
fields query object no
updated_since query no
workflow_id query string no
curl -X GET https://madmax.build/api/v1/stores/{store_id}/rows \
  -H "Authorization: Bearer $MADMAX_API_TOKEN" \
  -H "Content-Type: application/vnd.api+json"
try it
GET /api/v1/stores/{store_id}/rows/{key} returns 200

One row by its caller-defined key (unique per store).

param in type required
store_id path string yes
key path string yes
include query string no
fields query object no
curl -X GET https://madmax.build/api/v1/stores/{store_id}/rows/{key} \
  -H "Authorization: Bearer $MADMAX_API_TOKEN" \
  -H "Content-Type: application/vnd.api+json"
try it
PUT /api/v1/stores/{store_id}/rows/{key} returns 200

Insert or replace one row by key. `row` must satisfy the store's schema; the row cap applies.

param in type required
key path string yes
store_id path string yes
curl -X PUT https://madmax.build/api/v1/stores/{store_id}/rows/{key} \
  -H "Authorization: Bearer $MADMAX_API_TOKEN" \
  -H "Content-Type: application/vnd.api+json" \
  -d '{"data":{"row":{}}}'
try it
DELETE /api/v1/stores/{store_id}/rows/{key} returns 200

Delete one row by key. 404 when the key doesn't exist in that store.

param in type required
key path string yes
store_id path string yes
curl -X DELETE https://madmax.build/api/v1/stores/{store_id}/rows/{key} \
  -H "Authorization: Bearer $MADMAX_API_TOKEN" \
  -H "Content-Type: application/vnd.api+json"
try it

machine-readable spec: /api/docs/openapi.json (public, OpenAPI 3 — imports into Postman/Insomnia)