Skip to content

Conventions And Errors

The platform API is easier to use once you understand its common contracts. The route groups cover different product areas, but they share the same broad envelope and error behavior.

Most routes live under:

/api/v1/...

Examples:

  • /api/v1/workflows
  • /api/v1/tasks
  • /api/v1/workspaces
  • /api/v1/logs

Most mutation routes expect JSON and validate input explicitly with Zod. Unsupported keys are usually rejected rather than ignored.

Many mutations accept a request_id field:

{
"request_id": "launch-001"
}

Use it for replay-safe or operator-driven actions such as:

  • workflow launch
  • activation enqueue
  • task completion
  • task retry or request-changes flows

request_id is an application-level idempotency key, not a server trace id. Reuse it only when retrying the same intended mutation.

The API mixes strict control-plane fields with intentionally flexible JSON payloads.

Contract kindHow to treat it
fixed enum or scalarValidate against the documented type and allowed values.
structured objectFollow the documented field shape. Unknown keys may be rejected or ignored depending on the route.
arbitrary JSON objectTreat it as caller-defined data. Only documented subfields have platform meaning.
opaque token or cursorStore and replay it exactly. Do not infer semantics from its format.

When in doubt:

  • treat enum, fixed string, UUID, and scalar labels as strict
  • treat enum-like string as a real value the platform uses today, but not a forever-closed public enum unless the page says so
  • treat server-generated <type> as read-only
  • treat ... or null literally: the field can be present but empty
  • treat record[] labels as nested read models, not arbitrary blobs

The common list pagination contract is:

page=1
per_page=20

Current defaults from code:

  • default page: 1
  • default per_page: 20
  • maximum per_page: 100

Some routes use cursor-style paging or refresh markers:

  • cursor
  • after
  • after_cursor
  • limit

Those are used heavily in logs, event feeds, and workflow operations streams.

{
"data": {
"id": "uuid",
"name": "Example"
}
}
{
"data": [
{
"id": "uuid"
}
],
"meta": {
"total": 12,
"page": 1,
"per_page": 20,
"pages": 1
}
}

Some operations routes return data containing a prebuilt batch packet instead of a single resource or paginated row list. That is especially common in workflow operations and live-console surfaces.

204 No Content usually means one of these:

  • a claim route had nothing to claim
  • a delete or acknowledgement completed without returning a resource
  • a disconnect-style mutation intentionally returns no body

Do not treat 204 as an error on polling or claim routes.

The platform error handler normalizes failures into this shape:

{
"error": {
"code": "VALIDATION_ERROR",
"message": "Validation failed",
"recovery_hint": "optional",
"details": {}
},
"meta": {
"request_id": "generated-or-fastify-id",
"timestamp": "2026-03-31T18:55:00.000Z"
}
}
  • 200 OK: successful read or mutation
  • 201 Created: new resource or enqueue result created
  • 204 No Content: successful claim poll, delete, or acknowledgement with no body
  • 400 Bad Request: validation or domain-level bad input
  • 401 Unauthorized: missing or invalid auth
  • 403 Forbidden: scope or ownership mismatch
  • 404 Not Found: resource missing
  • 409 Conflict: state conflict, duplicate, or invalid transition
  • 422 Unprocessable Entity: schema validation failed
  • 429 Too Many Requests: rate-limited
  • 503 Service Unavailable: temporary service problem

Zod-driven validation failures typically include details.issues.

Representative shape:

{
"error": {
"code": "SCHEMA_VALIDATION_FAILED",
"message": "Invalid request body: name is required",
"details": {
"issues": {
"fieldErrors": {
"name": [
"Required"
]
},
"formErrors": []
}
}
},
"meta": {
"request_id": "req-123",
"timestamp": "2026-03-31T18:55:00.000Z"
}
}

SSE routes generally emit text/event-stream with one JSON object per event.

Representative form:

event: log
data: {"id":"...","operation":"task.complete","status":"completed"}

Heartbeat events are also used on some streams:

event: heartbeat
data: {"ts":"2026-03-31T18:55:00.000Z"}
  • Persist request_id values for mutation replay safety.
  • Treat 204 claim responses as normal control flow.
  • Expect secret-like values in payloads to be redacted in public read models.
  • Keep scope-specific credentials separate instead of sharing one admin token across every integration path.