Skip to content

REST API

Ironflow exposes a REST API for managing functions, emitting events, querying runs, and more.

Base URL: http://localhost:9123/api/v1


All API endpoints (except /health, /ready, /metrics, /api/v1/capabilities, /api/v1/auth/login, /api/v1/auth/validate, and /api/v1/platform/auth/login) require authentication.

API Key (SDK and programmatic access):

Authorization: Bearer ifkey_your_key_here

Dashboard JWT (browser sessions): Dashboard authentication uses the ironflow_dashboard_token cookie set by the login endpoint.

Environment Selection (optional):

X-Ironflow-Environment: env_production

Emit a user event to trigger matching workflows.

Request:

{
"name": "order.placed",
"data": { "orderId": "123", "total": 99.99 },
"metadata": { "source": "checkout", "traceId": "abc-123" }
}

The metadata field is optional. It accepts any key-value pairs and is delivered to function handlers as event.metadata (TypeScript) or ctx.Event.Metadata (Go). Use it to pass tracing IDs, source labels, or any contextual information that shouldn’t be part of the event’s core data schema.

Response:

{
"event_id": "evt_abc123",
"run_ids": ["run_xyz789"]
}

List events with keyset pagination (issue #697). Sorted newest-first by (timestamp, id). The endpoint always scopes to the caller’s environment.

Query Parameters:

ParameterTypeDescription
namestringFilter by event name
sourcestringFilter by source (comma-separated)
sincestringStart time (RFC3339)
untilstringEnd time (RFC3339)
limitintMax results per page (default 20)
cursorstringForward keyset token from a prior next_cursor. Mutex with before.
beforestringBackward keyset token from a prior prev_cursor. Mutex with cursor.

Response:

{
"events": [...],
"count": 20,
"limit": 20,
"next_cursor": "MTcxMjMzNDQ1Njc4OXxldnRfMDFI...",
"prev_cursor": "MTcxMjMzNDQ1NjAwMHxldnRfMDFI...",
"has_next": true,
"has_prev": false,
"approx_total": 12400,
"approx_total_capped": false
}
  • next_cursor / prev_cursor are null/omitted when there is no further page in that direction.
  • has_next and has_prev are direction-aware. On the newest page (no cursor and no before), has_prev is always false. After paging forward with cursor=, has_prev is true. After paging backward with before=, has_next is true.
  • approx_total is the per-environment count from a capped subquery (LIMIT 10001). Below the cap the value is exact; above the cap the value is 10000 and approx_total_capped is true.

Errors:

  • 400 — invalid cursor / before token, or both supplied at once.

Get a single event by ID.


Function registration (POST /ironflow.v1.IronflowService/RegisterFunction) accepts an optional change_reason field (e.g. "deploy v2.1.0") that is recorded in the function’s history for every update. Has no effect on new registrations (no history event is written on first create).

List all registered functions.

Query Parameters:

ParameterTypeDescription
namestringFilter by function name
statusstringFilter by status
modestringFilter by execution mode
limitintMax results (returns all if omitted)
offsetintPagination offset

Directly invoke a function, creating an event and run.

Request:

{
"data": { "orderId": "123" }
}

Headers:

HeaderDescription
Idempotency-KeyOptional. When supplied, a retry with the same key + same function_id in the same environment returns the original run_id + event_id instead of creating a duplicate run. Reusing a key for a different function, or for a key already used by POST /events, returns 409 Conflict — keys are scoped per (environment, key) and not shareable across flows.

Response:

{
"run_id": "run_abc",
"event_id": "evt_xyz"
}

Function registration history is exposed via the ConnectRPC API. Every update, status change, rollback, and deletion is recorded as an immutable snapshot against ironflow:fn:{id} in the entity stream.

See Function Registration History for a full explanation of the flows.

POST /ironflow.v1.IronflowService/ListFunctionHistory

Request:

{
"function_id": "my-function",
"limit": 20,
"from_version": 0
}

Use from_version (exclusive) for keyset pagination — pass the entity_version of the last entry to get the next page.

Response:

{
"entries": [
{
"event_id": "evt_abc",
"entity_version": 3,
"function_id": "my-function",
"function_snapshot": { ... },
"actor_id": "ifkey_xyz",
"change_reason": "deploy v2.1.0",
"change_type": "update",
"recorded_at": "2026-04-12T10:00:00Z"
}
],
"has_more": false
}

change_type values: update, status_change, rollback, delete.

POST /ironflow.v1.IronflowService/GetFunctionAtVersion

Request:

{
"function_id": "my-function",
"version": 2
}

Response:

{
"entry": {
"event_id": "evt_abc",
"entity_version": 2,
"function_snapshot": { ... },
"actor_id": "ifkey_xyz",
"change_type": "update",
"recorded_at": "2026-04-10T08:00:00Z"
}
}
POST /ironflow.v1.IronflowService/RollbackFunction

Restores a function to the configuration captured at version. The rollback is itself recorded as a new history entry with change_type = "rollback". Returns CodeAborted on concurrent modification — retry in that case.

Request:

{
"function_id": "my-function",
"version": 2,
"change_reason": "revert bad deploy"
}

Response:

{
"function": { ... }
}

Cannot roll back to an archived state (CodeInvalidArgument).


List workflow runs with optional filters.

Query Parameters:

ParameterTypeDescription
function_idstringFilter by function
statusstringFilter by status (comma-separated: pending, running, completed, failed, cancelled, paused)
sincestringStart time (RFC3339)
untilstringEnd time (RFC3339)
limitintMax results (default 50)
offsetintPagination offset

Response:

{
"runs": [],
"count": 10,
"total_count": 42
}

Get a single run by ID.

Get all steps executed in a run.

List entity streams that were read from or appended to during this run.

Get the history inspection (audit trail) for a specific run.

Query Parameters:

ParameterTypeDescription
event_typestringFilter by audit event type
fromstringStart timestamp
tostringEnd timestamp
limitintMax results
cursorstringPagination cursor

Cancel a pending, running, or paused run. Routes through the engine’s cancel helper: marks the run terminal with cancellation_cause = "user", deletes any cancel_on_specs rows referencing the run, publishes a cancel event to NATS with a stable msg-id (cancel-{runID}), and notifies pubsub subscribers. ConnectRPC CancelRun produces identical behavior (#716).

Request (optional):

{
"reason": "No longer needed"
}

The reason field is captured into the NATS payload and structured log only — the persisted cancellation_cause column is always "user". Long reasons are truncated server-side (rune-aware, 1024 rune cap).

Responses:

  • 200 OK — run cancelled (idempotent: re-cancelling an already-cancelled run also returns 200 with the cancelled run).
  • 404 Not Found — run id does not exist.
  • 409 Conflict — run reached a different terminal state during the cancel (race).
  • 412 Precondition Failed — run is in a non-cancellable status (already completed/failed/cancelled).
  • 500 Internal Server Error — unexpected store/engine failure.

Resume a paused or failed run, optionally from a specific step. Routes through the engine’s resume helper: flips status to running, publishes a resume event to NATS with a stable msg-id (resume-{runID}), and notifies pubsub subscribers. If the NATS publish fails the run status is reverted to its prior paused/failed value so the run is not stuck in running with no executor (#716).

Request:

{
"run_id": "run_abc",
"from_step": "step_xyz"
}

from_step is optional; omit to resume from the last completed step.

Responses:

  • 200 OK — run resumed; status flipped to running and resume event published.
  • 400 Bad Request — malformed body or missing run_id.
  • 404 Not Found — run id does not exist, OR from_step does not exist, OR from_step belongs to a different run.
  • 412 Precondition Failed — run is in a non-resumable status (must be paused or failed).
  • 500 Internal Server Error — NATS publish failed (run is reverted to its prior status), or other unexpected failure.

Manually override a step’s output (e.g., to fix a failed step before resuming).

Request:

{
"step_id": "step_abc",
"output": { "result": "manual_fix" },
"reason": "Corrected API response"
}

Pause a running workflow at the next step boundary for inspection and injection.

POST /ironflow.v1.IronflowService/PauseRun

Request:

{
"run_id": "run_abc123"
}

Response:

{
"status": "pause_requested"
}

Status is "pause_requested" if the run is running (will pause at next step boundary), or "paused" if the run was already paused (sleep/wait) and was upgraded to injection pause.

Get completed steps and their outputs for an injection-paused run.

POST /ironflow.v1.IronflowService/GetPausedState

Request:

{
"run_id": "run_abc123"
}

Response:

{
"steps": [
{
"id": "step_xyz",
"name": "compute-total",
"output": "{\"total\": 42}",
"injected": false,
"completed_at": "2026-03-08T00:00:00Z"
}
],
"next_step_hint": "send-email",
"pause_reason": "injection"
}

Only works on runs with status = "paused" and pause_reason = "injection".

Modify the output of a completed step while a run is paused for injection.

POST /ironflow.v1.IronflowService/InjectStepOutput

Request:

{
"run_id": "run_abc123",
"step_id": "step_xyz",
"new_output": "{\"total\": 50}",
"reason": "Correcting calculation error"
}

Response:

{
"step_id": "step_xyz",
"previous_output": "{\"total\": 42}"
}

Preserves the original output on first injection. Sets patched_at and patched_by on the step.


List all environments. API keys are masked in the response.

Create a new environment.

Request:

{
"name": "staging"
}

Update an environment (e.g., rename it).

Request:

{
"name": "production"
}

Delete an environment. The default environment cannot be deleted.


List all projects in the organization.

Create a new project.

Request:

{
"name": "payments"
}

Update a project.

Delete a project.


List all entity streams.

Query Parameters:

ParameterTypeDescription
entity_typestringFilter by entity type
searchstringSearch streams
limitintMax results
offsetintPagination offset

Get stream metadata (current version, entity type).

Read events from an entity stream.

Query Parameters:

ParameterTypeDescription
from_versionintStart from version
limitintMax events to return
directionstringRead direction

Append an event to an entity stream with optimistic concurrency.

Request:

{
"entity_type": "order",
"event_name": "order.shipped",
"data": { "tracking": "1Z999" },
"expected_version": 3,
"idempotency_key": "ship-order-123",
"version": 1
}

Response:

{
"event_id": "evt_abc",
"entity_version": 4
}

Get entity state history (reconstructed state at each version).

Create a snapshot of the entity stream’s current state.

Get the latest snapshot for an entity stream.


List all registered projections.

Query Parameters:

ParameterTypeDescription
statusstringFilter by status
modestringFilter by mode
limitintMax results
offsetintPagination offset

Get a projection and its current state.

Query Parameters:

ParameterTypeDescription
partitionstringPartition key (default: __global__)

Get projection processing status.

Response:

{
"name": "order-totals",
"status": "running",
"mode": "managed",
"last_event_seq": 142,
"error_message": null,
"updated_at": "2026-03-06T12:00:00Z"
}

List partitions for a partitioned projection.

Query Parameters:

ParameterTypeDescription
limitintMax results
offsetintPagination offset

Wait until a projection has caught up to a minimum NATS sequence. Long-poll style; returns when the projection is current or the request times out.

Query Parameters:

ParameterTypeDescription
partitionstringPartition key (default: __global__)
minSeqintMinimum NATS sequence to wait for
timeoutMsintMax wait in milliseconds (default: 30000)

Wait for multiple projections to catch up in one call.

Request:

{
"items": [
{ "name": "order-totals", "minSeq": 142, "partition": "__global__" },
{ "name": "user-index", "minSeq": 142 }
],
"timeoutMs": 5000
}

Block until a specific event has been applied by a projection. Useful after AppendEvent when AppendEventResponse.Sequence is 0.

Request:

{
"eventId": "evt_abc",
"projection": "order-totals",
"partition": "__global__",
"timeoutMs": 5000
}

Trigger a full or partial projection rebuild.

Request (optional):

{
"fromEventId": "evt_start",
"toEventId": "evt_end",
"partition": "tenant-1",
"dryRun": false
}

Get the status of an ongoing rebuild job.

Cancel an ongoing rebuild.

Pause a projection (stops consuming events).

Resume a paused projection.

Delete/unregister a projection.


Publish a message to a developer pub/sub topic.

Request:

{
"topic": "notifications.email",
"data": { "to": "user@example.com", "subject": "Hello" },
"idempotency_key": "notif-123"
}

Response:

{
"event_id": "evt_abc",
"sequence": 42
}

Topics with reserved prefixes (events:, system., entity:, public., topic:) are rejected.


Register a new event schema version.

Request:

{
"event_name": "order.placed",
"version": 2,
"schema_json": "{\"type\":\"object\",\"properties\":{...}}",
"description": "Added shipping address field"
}

List all event schemas.

Query Parameters:

ParameterTypeDescription
event_namestringFilter by event name
limitintMax results
offsetintPagination offset

Get the latest version of a schema.

Get a specific version of a schema.

Delete a schema version.

Test event upcasting between schema versions.

Request:

{
"event_name": "order.placed",
"from_version": 1,
"to_version": 2,
"data": { "orderId": "123" }
}

Response:

{
"data": { "orderId": "123", "shipping_address": null },
"steps_applied": [...]
}

Execute an ad-hoc SQL query against the projection database.

Request:

{
"query": "SELECT * FROM order_totals WHERE total > 100",
"timeout_ms": 5000,
"max_rows": 100
}

Response:

{
"columns": ["id", "total"],
"rows": [["order-1", 150]],
"total_rows": 1
}

Create a KV bucket.

Request:

{
"name": "session-cache",
"description": "User session data",
"ttl_seconds": 3600,
"max_value_size": 65536,
"max_bytes": 10485760,
"history": 5
}

List all KV buckets.

Get bucket metadata.

Delete a bucket.

List keys in a bucket.

Query Parameters:

ParameterTypeDescription
filterstringGlob pattern to filter keys

Retrieve a key’s value. Returns an ETag header with the revision.

Write a value. Supports atomic operations via headers:

HeaderEffect
If-None-Match: *Create only (fails if key exists)
If-Match: <revision>Compare-and-swap (fails if revision differs)

Request body is the raw value (any content type).

Delete a key.

Watch a bucket for key changes via WebSocket upgrade.


Set or replace an entire config document.

Request: Any JSON object.

Response:

{
"name": "app-settings",
"revision": 1
}

Shallow merge-patch a config document with CAS retry.

Request: Partial JSON object with fields to merge.

Get a config document.

Response:

{
"name": "app-settings",
"data": { "theme": "dark" },
"revision": 3,
"updatedAt": "2026-03-06T12:00:00Z"
}

List all config documents.

Delete a config document.

Watch a config document for real-time changes via WebSocket.


All secrets endpoints require the X-Ironflow-Environment header. Secret values are write-only and never returned in responses.

List secret metadata.

Create a new secret.

Request:

{
"name": "STRIPE_KEY",
"value": "sk_live_...",
"description": "Production billing key"
}

Get secret metadata (not the value).

Update a secret’s value.

Request:

{
"value": "sk_live_new_...",
"description": "Rotated key"
}

Partial update (description only).

Delete a secret.


Receive a webhook from an external provider (e.g., GitHub, Stripe).

Supports signature verification (HMAC-SHA256/SHA1) and idempotency by external delivery ID.

Response:

{
"status": "accepted",
"delivery_id": "gh_123",
"event_id": "evt_abc",
"run_ids": ["run_xyz"]
}

List all connected REST-polling workers.

Register a worker for HTTP polling.

Request:

{
"worker_id": "worker-1",
"hostname": "host.example.com",
"function_ids": ["my-function"],
"max_concurrent_jobs": 5,
"labels": { "region": "us-east" },
"version": { "sdk": "0.8.0", "runtime": "node-22" }
}

Send a heartbeat to keep the worker connection alive.

Request:

{
"worker_id": "worker-1",
"active_jobs": 2
}

Poll for job assignments. Returns 204 No Content if no jobs are available.

Response (when job available):

{
"job_id": "job_abc",
"run_id": "run_xyz",
"function_id": "my-function",
"attempt": 1,
"event": { ... },
"completed_steps": [ ... ],
"secrets": { ... }
}

Report job completion, failure, or yield.

Request:

{
"status": "completed",
"output": { "result": "done" },
"steps": [ ... ]
}

Status values: completed, failed, yielded.


Create a new API key. Set "platform": true to create a platform key (requires platform:keys:manage).

Request:

{
"name": "CI Pipeline",
"env_id": "env_production",
"role_ids": ["role_admin"],
"expires_in": "720h"
}

Response includes the raw key (only shown once).

List API keys for the caller’s organization. Pass ?platform=true to list platform API keys instead (requires platform:keys:read).

Get a single API key by ID.

Delete/revoke an API key. Returns 204.

Rotate an API key (creates new key, revokes old). Returns the new raw key.

Replace the full set of role assignments on an API key.

Request:

{
"role_ids": ["role_admin", "role_custom_abc"]
}

Response: The updated API key object (without the raw key).


Create a new dashboard user (admin only).

Request:

{
"email": "user@example.com",
"password": "securepassword",
"name": "Jane Doe",
"roles": ["admin"]
}

List all users in the caller’s organization (admin only).

Get a single user (admin or self).

Update user profile (admin only).

Request:

{
"name": "Jane Smith",
"email": "jane@example.com",
"roles": ["admin", "viewer"]
}

Delete a user (admin only, cannot delete self). Returns 204.

Change own password.

Request:

{
"current_password": "old_password",
"new_password": "new_password"
}

Authenticate and receive a JWT token for the dashboard. Public endpoint.

Request:

{
"email": "admin@example.com",
"password": "password"
}

Response:

{
"token": "eyJhbGci..."
}

Validate the current JWT token. Public endpoint — used by the dashboard to detect stale sessions on load.

Response (valid token):

{
"valid": true,
"email": "admin@example.com"
}

Response (invalid/expired): 401 Unauthorized


List all circuit breaker states.

Response:

{
"circuit_breakers": [
{
"key": "fn_abc|https://example.com/api/handler",
"state": "open",
"failures": 5,
"last_failure": "2024-01-15T10:30:00Z"
}
]
}

Reset a circuit breaker to closed state. The key format is {functionID}|{endpointURL} — URL-encode the | as %7C in the path.

Response: 200 OK


Operator endpoints for pending debounce entries (issue #545). Debounce collapses rapid-fire events for a function into a single invocation after a quiet period. See the debounce how-to for background. The CLI ironflow debounce {list|cancel} wraps these endpoints.

List currently-armed debounce entries scoped to the request environment. Empty 200 when the engine is not wired for debounce (e.g., embedded/test mode without JetStream).

Response:

[
{
"environment_id": "env_default",
"function_id": "process-search",
"debounce_key": "user-42",
"event_id": "evt_01HW...",
"function_version": 1,
"period_ms": 5000,
"armed_at": "2026-04-24T12:00:00.000Z",
"fires_at": "2026-04-24T12:00:05.000Z"
}
]

ArmedAt / FiresAt are ISO-8601 UTC millisecond timestamps. DebounceKey is the resolved key value (never base64-encoded in the response body — only the path parameter on cancel is encoded).

DELETE /debounce/entries/{envID}/{fnID}/{key}

Section titled “DELETE /debounce/entries/{envID}/{fnID}/{key}”

Cancel one pending debounce entry without firing it. The key path segment MUST be base64url-encoded (base64.RawURLEncoding) because debounce keys can contain / or . which conflict with URL path semantics.

Path parameters:

ParameterDescription
envIDEnvironment ID owning the entry
fnIDFunction ID
keybase64url-encoded debounce key

Responses:

StatusMeaning
204 No ContentEntry cancelled, or did not exist (idempotent no-op)
400 Bad RequestPath segments missing or key not valid base64url
403 ForbiddenTenant-scoped request whose authenticated env does not match envID in the path (cross-tenant guard)
503 Service UnavailableDebounce manager not configured on this server

Tenant-scoped credentials can only cancel entries within their own environment. Platform-scoped credentials (empty RequestContext.EnvironmentID) may cancel any environment’s entry.


Operator endpoints for the transactional outbox dead-letter table (issue #487, DLQ tooling from #496). Rows appear here after the outbox worker exhausts its retry budget (default 10 attempts). The underlying row in events is untouched — these endpoints only affect the unpublished NATS delivery.

See the outbox explanation and the DLQ runbook for context. The CLI ironflow outbox dlq {list|requeue|discard} wraps these endpoints.

List rows in the outbox dead-letter table for one environment, newest first. The environment must be declared — callers send either the X-Ironflow-Environment header or the env query parameter (the CLI sends both). Missing both returns 400.

Query Parameters:

ParameterTypeDefaultDescription
envstringEnvironment name (required unless X-Ironflow-Environment header is set)
limitint50Max rows to return (1-500, clamped)
offsetint0Row offset for pagination (0-10000)

Response:

{
"items": [
{
"id": "ob_01HW...",
"event_id": "evt_abc123",
"entity_id": "order_42",
"topic": "orders.placed",
"kind": "events",
"environment_id": "env_prod",
"created_at": "2026-04-20T12:00:00Z",
"dead_at": "2026-04-20T12:05:30Z",
"attempts": 10,
"last_error": "nats: max payload exceeded",
"payload": "<base64>",
"metadata": "<base64>"
}
],
"limit": 50,
"offset": 0
}

payload and metadata are base64-encoded to keep the JSON body valid for binary content.

POST /outbox/dead-letter/{eventID}/requeue

Section titled “POST /outbox/dead-letter/{eventID}/requeue”

Move every dead-letter row matching event_id back to the live outbox with attempts=0. The worker picks them up on the next tick.

Response: 200 OK

{ "event_id": "evt_abc123", "result": "requeued" }

404 Not Found if no dead-letter entry exists for the given event_id.

Permanently delete a dead-letter row. The underlying events row is not deleted — only the unpublished outbox entry. Destructive; the CLI prompts for confirmation before calling this.

Response: 200 OK

{ "event_id": "evt_abc123", "result": "discarded" }

404 Not Found if no dead-letter entry exists for the given event_id.


List history inspection (audit) events globally.

Query Parameters:

ParameterTypeDescription
run_idstringFilter by run
function_idstringFilter by function
event_typestringFilter by audit event type
fromstringStart timestamp
tostringEnd timestamp
limitintMax results
cursorstringPagination cursor

Liveness probe. Returns 200 when the database Ping succeeds, 503 otherwise. Does not check NATS — use /ready for full readiness. Public endpoint (no auth required).

Returns server edition, supported features, and transport information. Public endpoint.

Response:

{
"version": "0.8.0",
"edition": "core",
"auth_required": true,
"transports": ["websocket", "grpc-sse", "grpc-bidirectional"],
"features": ["replay", "wildcard-patterns", "cel-filter", "consumer-groups", "prometheus-metrics"]
}

Readiness probe endpoint. Returns 200 when the server is ready to accept traffic (PostgreSQL and NATS connected). Used by Kubernetes readiness probes.

Returns the server route manifest. Used by SDK generation tooling (make sdk-manifest).

Get a comprehensive system overview with stats and uptime.

Returns cluster health status including node information. Available in multi-node cluster mode.

Open a WebSocket connection for real-time event subscriptions.

Prometheus metrics endpoint (only available when metrics are enabled).


Get recently captured HTTP requests (up to 100).

Clear all captured debug requests.


MethodPathDescription
POST/orgsCreate an organization
GET/orgsList organizations
GET/orgs/{id}Get an organization
PATCH/orgs/{id}Update organization name
DELETE/orgs/{id}Delete an organization
MethodPathDescription
POST/rolesCreate a custom role
GET/rolesList roles
GET/roles/{id}Get a role
PATCH/roles/{id}Update role name
DELETE/roles/{id}Delete a role (not built-in)
POST/roles/{id}/policiesAssign a policy to a role
DELETE/roles/{id}/policies/{policy_id}Remove a policy from a role
MethodPathDescription
POST/policiesCreate a CEL authorization policy
GET/policiesList policies
GET/policies/{id}Get a policy
PATCH/policies/{id}Update a policy
DELETE/policies/{id}Delete a policy
POST/policies/dry-runEvaluate a candidate policy against sample subjects without persisting it (self-lockout preflight)
GET/policies/{id}/versionsList version history for a policy
POST/policies/{id}/rollback/{version}Forward-rollback a policy to a prior version (writes a new version+1 snapshot)
MethodPathDescription
GET/policy-templatesList installable CEL policy template bundles
POST/policy-templates/{id}/installInstall a template bundle into the caller’s tenant (runs LintTemplate first)

Policy Request:

{
"name": "allow-read-events",
"effect": "allow",
"actions": "read",
"resources": "events/*",
"condition": "request.environment == 'production'"
}
MethodPathDescription
POST/tenants/provisionProvision a tenant (org + env + admin key)
GET/tenantsList tenants with environment and key counts

Provision Request:

{
"org_name": "Acme Corp",
"env_name": "production"
}

Provision Response:

{
"org": { "id": "org_abc", "name": "Acme Corp" },
"environment": { "id": "env_production", "name": "production" },
"api_key": { "key": "ifkey_...", "roles": ["admin"] }
}

CodeMeaning
200Success
201Resource created
204Success, no content
400Invalid request or validation error
401Authentication required
403Insufficient permissions
404Resource not found
409Conflict (duplicate resource or version mismatch)
412Precondition failed (CAS mismatch)
503Service unavailable