Skip to main content

HTTP API

Every Orchestate capability is available over a plain JSON REST API. The TypeScript SDK is a thin wrapper around these endpoints — use the raw API when you are working from a language without an SDK, from a shell script, or from another backend service.

Base URL and authentication

All endpoints live under the /api path on the app origin. On the hosted service that is:

https://orchestate.wentzel.ai/api

Every request (except GET /api/health and the WebSocket inspector, which are noted below) must include an API key as a bearer token:

Authorization: Bearer orch_YOUR_API_KEY

API keys are created in the console under Settings → API Keys and have the form orch_ followed by 48 hex characters. The raw key is shown once at creation and stored only as a SHA-256 hash, so copy it immediately. See Auth & API keys for the full lifecycle.

Request and response bodies are JSON. Send Content-Type: application/json on any request with a body. CORS is enabled and OPTIONS preflight requests are supported, so the API can be called directly from the browser.

POST /api/machines

Publish a machine definition. Each publish creates a new immutable version under the given slug. See Machine definitions for the definition schema.

FieldTypeRequiredDescription
slugstringYesStable identifier, matches ^[a-z0-9-]+$, up to 80 characters.
namestringNoHuman-readable display name.
definitionobjectYesThe machine definition: { initial, states } (optional context).
curl -X POST https://orchestate.wentzel.ai/api/machines \
  -H "Authorization: Bearer orch_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "slug": "checkout",
    "name": "Checkout flow",
    "definition": {
      "initial": "cart",
      "states": {
        "cart": { "on": { "SUBMIT": "paid" } },
        "paid": { "type": "final" }
      }
    }
  }'

201 Created

{
  "id": "mch_8c1f0a2b3d4e5f6a",
  "version": 1
}

GET /api/machines

List the published machines for your organization.

curl https://orchestate.wentzel.ai/api/machines \
  -H "Authorization: Bearer orch_YOUR_API_KEY"

200 OK

[
  {
    "id": "mch_8c1f0a2b3d4e5f6a",
    "slug": "checkout",
    "name": "Checkout flow",
    "currentVersion": 1,
    "updatedAt": "2026-06-09T12:00:00.000Z"
  }
]

GET /api/machines/{idOrSlug}

Fetch a single machine by its id or slug, scoped to your organization.

curl https://orchestate.wentzel.ai/api/machines/checkout \
  -H "Authorization: Bearer orch_YOUR_API_KEY"

200 OK

{
  "id": "mch_8c1f0a2b3d4e5f6a",
  "slug": "checkout",
  "name": "Checkout flow",
  "currentVersion": 1,
  "definition": { "initial": "cart", "states": { "cart": {}, "paid": {} } },
  "createdAt": "2026-06-09T12:00:00.000Z",
  "updatedAt": "2026-06-09T12:00:00.000Z"
}

Returns 404 if no machine with that id or slug exists in your organization.

POST /api/instances

Create a running instance of a published machine. The instance starts in the machine's initial state. The runtime executes in a Cloudflare Durable Object.

FieldTypeRequiredDescription
machineSlugstringYesSlug of the machine to instantiate.
contextobjectNoInitial context merged into the machine's default context.
curl -X POST https://orchestate.wentzel.ai/api/instances \
  -H "Authorization: Bearer orch_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "machineSlug": "checkout",
    "context": { "cartId": "cart_123" }
  }'

201 Created

{
  "id": "ins_5a6b7c8d9e0f1a2b",
  "currentState": "cart",
  "status": "active",
  "machineVersion": 1,
  "createdAt": "2026-06-09T12:01:00.000Z"
}

Returns 404 if the machine slug does not exist and 502 if the runtime fails to start the instance.

GET /api/instances

List instances, newest first. At most 200 are returned.

curl https://orchestate.wentzel.ai/api/instances \
  -H "Authorization: Bearer orch_YOUR_API_KEY"

200 OK

[
  {
    "id": "ins_5a6b7c8d9e0f1a2b",
    "currentState": "cart",
    "status": "active",
    "machineVersion": 1,
    "createdAt": "2026-06-09T12:01:00.000Z",
    "updatedAt": "2026-06-09T12:01:00.000Z"
  }
]

GET /api/instances/{id}

Fetch a single instance by id, including its current state and context snapshot.

curl https://orchestate.wentzel.ai/api/instances/ins_5a6b7c8d9e0f1a2b \
  -H "Authorization: Bearer orch_YOUR_API_KEY"

200 OK

{
  "id": "ins_5a6b7c8d9e0f1a2b",
  "currentState": "paid",
  "context": { "cartId": "cart_123" },
  "status": "active",
  "machineVersion": 1,
  "createdAt": "2026-06-09T12:01:00.000Z",
  "updatedAt": "2026-06-09T12:02:00.000Z"
}

Returns 404 if the instance does not exist in your organization.

POST /api/instances/{id}/events

Send an event to a running instance. If the current state defines a transition for the event type, the instance moves to the target state and any assign actions on the transition are applied.

FieldTypeRequiredDescription
typestringYesEvent name, matched against the current state's on map.
payloadobjectNoArbitrary data carried with the event.
curl -X POST https://orchestate.wentzel.ai/api/instances/ins_5a6b7c8d9e0f1a2b/events \
  -H "Authorization: Bearer orch_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "SUBMIT",
    "payload": { "amount": 4200 }
  }'

200 OK

{
  "fromState": "cart",
  "toState": "paid",
  "final": true
}

When toState is a final state, final is true and the instance status becomes done. Returns 404 if the instance does not exist, 409 if the instance is no longer active, and 422 if the current state has no transition for the event or a guard rejected it (code: "GUARD_REJECTED").

Idempotency — send an Idempotency-Key header to make retries safe. A replayed key returns the original transition result with "replayed": true and does not re-run the transition or append to the event log.

GET /api/instances/{id}/events

The instance's transition history, newest first. Each row records the event type, payload, and the from/to states — an audit trail for the instance. Query params: limit (default 50, max 200) and before (ISO timestamp or epoch ms cursor; pass the createdAt of the last row from the previous page).

curl "https://orchestate.wentzel.ai/api/instances/ins_5a6b7c8d9e0f1a2b/events?limit=20" \
  -H "Authorization: Bearer orch_YOUR_API_KEY"

// 200 OK
[
  {
    "id": "…",
    "eventType": "SUBMIT",
    "payload": { "amount": 4200 },
    "fromState": "cart",
    "toState": "paid",
    "createdAt": "2026-06-10T12:18:32.000Z"
  }
]

Webhooks

Transitions whose machine definition carries an emit action POST a signed JSON payload to every enabled webhook endpoint configured for your organization. Verify the X-Orchestate-Signature header: sha256=<hex>, the HMAC-SHA256 of the raw request body keyed with the endpoint secret. The payload carries type: "machine.transition", the instance and machine ids, the event type, from/to states, final, the post-transition context, the emit action params, and occurredAt. Deliveries are fire-and-forget with a 5-second timeout — receivers should respond 2xx quickly and process asynchronously.

GET /api/inspect/{instanceId}

A WebSocket upgrade endpoint for the live inspector. Once connected, the server pushes a JSON message on every transition:

{ "type": "transition", "toState": "paid", "context": { "cartId": "cart_123" } }

Note — This endpoint is authenticated by the logged-in browser session cookie, not by an API key, so it is intended for use from an authenticated browser context. The SDK's client.instances.subscribe() opens this socket (it appends a ?key= parameter that the server currently ignores in favor of the session) — see the SDK reference.

GET /api/health

Liveness probe. Requires no authentication.

curl https://orchestate.wentzel.ai/api/health

200 OK — the endpoint always answers 200 with the platform health envelope; consumer dashboards inspect bindings and checks for degradation instead of relying on the status code.

{
  "service": "orchestate",
  "version": "<git sha>",
  "deployed_at": "<iso-8601>",
  "bindings": { "D1": "ok", "ANALYTICS_ENGINE": "ok", "KV_RATE_LIMIT": "ok" },
  "checks": { "d1_ping": { "ok": true, "latency_ms": 7 } }
}

Rate limits

Limits are applied per organization, per minute. Exceeding a limit returns a 429 response.

EndpointLimit
POST /api/machines100 requests / minute / org
POST /api/instances60 requests / minute / org

Errors

Errors are returned with the appropriate HTTP status and a JSON body of the shape { "error": "<message>" }.

StatusMeaning
400Invalid request body.
401Missing or invalid API key.
404Machine or instance not found.
409Instance is not active (already done, error, or cancelled).
429Rate limit exceeded.
502Upstream runtime error while starting or stepping an instance.
503Service unavailable — a dependency is unhealthy (returned by GET /api/health).

See also