SDK reference
@orchestate/sdk is the official TypeScript client for Orchestate. It wraps the HTTP API with a small, typed surface for publishing machine definitions, creating instances, sending events, and subscribing to live state. Every method maps to a single REST call (or, for subscribe, a WebSocket), so the SDK stays a thin, predictable layer over the wire protocol.
Installation
Install the package from npm:
npm install @orchestate/sdkThe client uses the global fetch and WebSocket APIs. It runs in modern Node.js (18+), Cloudflare Workers, Deno, and the browser without a polyfill.
Initialization
Create a client by passing your API key. The class is named OrchestateClient:
import { OrchestateClient } from '@orchestate/sdk';
const client = new OrchestateClient({
apiKey: process.env.ORCHESTATE_API_KEY!, // orch_<48 hex chars>
});API keys are created in the console under Settings → API Keys and are sent on every request as Authorization: Bearer orch_…. See Auth & API keys for the full key lifecycle.
The constructor accepts an optional baseUrl. It defaults to https://orchestate.wentzel.ai/api — the managed service. Override it only when you run the Orchestate runtime in your own Cloudflare account on the Offline tier:
const client = new OrchestateClient({
apiKey: process.env.ORCHESTATE_API_KEY!,
baseUrl: 'https://orchestate.example.com/api', // self-hosted origin
});Note — baseUrl should point at the /api path on your app origin, with no trailing slash. See Self-hosting for deploying the runtime.
Methods at a glance
Every method below maps to a single endpoint on the HTTP API:
| Method | Endpoint | Returns |
|---|---|---|
machines.publish(input) | POST /api/machines | { id, version } |
machines.list() | GET /api/machines | PublishedMachine[] |
machines.get(idOrSlug) | GET /api/machines/{idOrSlug} | PublishedMachine |
instances.create(input) | POST /api/instances | Instance |
instances.list(params?) | GET /api/instances | Instance[] |
instances.send(id, event) | POST /api/instances/{id}/events | TransitionResult |
instances.get(id) | GET /api/instances/{id} | Instance |
instances.subscribe(id, cb) | GET /api/inspect/{instanceId} (WebSocket) | unsubscribe () => void |
See HTTP API for the raw request/response contract behind each method.
Machines
client.machines.publish(input)
Publishes a machine definition. If the slug already exists, a new version is created. The slug must match ^[a-z0-9-]+$ and be 80 characters or fewer.
const machine = await client.machines.publish({
slug: 'order',
name: 'Order lifecycle',
definition: {
initial: 'pending',
states: {
pending: { on: { PAY: 'paid', CANCEL: 'cancelled' } },
paid: { on: { SHIP: 'shipped' } },
shipped: { type: 'final' },
cancelled: { type: 'final' },
},
},
});
// machine => { id, version }The definition follows the machine definition schema. Returns { id, version } where version increments on each republish of the same slug.
client.machines.list()
Lists every published machine in your organization.
const machines = await client.machines.list();
for (const m of machines) {
console.log(m.slug, m.version);
}
// machines => PublishedMachine[] -> [{ id, slug, version }, ...]client.machines.get(idOrSlug)
Fetches a single machine by its id or slug, scoped to your organization.
const machine = await client.machines.get('order');
// machine => { id, slug, name, currentVersion, definition, createdAt, updatedAt }Throws if no machine with that id or slug exists in your organization.
Instances
client.instances.create(input)
Creates a running instance of a machine. The optional contextseeds the instance's context object.
const instance = await client.instances.create({
machineSlug: 'order',
context: { orderId: 'ord_123', total: 4200 },
});
// instance => { id, currentState, status, machineVersion, createdAt }
// currentState is the machine's initial state; status is 'active'client.instances.list(params?)
Lists instances, newest first. Both filters are optional; pass machineSlug and/or status to narrow the result.
const active = await client.instances.list({
machineSlug: 'order',
status: 'active',
});
// active => Instance[]The status filter accepts one of active, done, error, or cancelled.
client.instances.send(instanceId, event)
Sends an event to an active instance, driving a transition. The event type must match an entry in the current state's on map.
const result = await client.instances.send(instance.id, {
type: 'PAY',
payload: { method: 'card' },
});
// result => { fromState, toState, final }
// e.g. { fromState: 'pending', toState: 'paid', final: false }
if (result.final) {
console.log('Instance reached a final state — status is now "done".');
}Note — sending an event to an instance that is not active returns a 409, which the SDK surfaces as a thrown Error. Reaching a final state moves the instance status to done. A transition whose guard evaluates false is rejected with 422 and code: "GUARD_REJECTED".
Pass an optional idempotencyKey to make retries safe — a replayed key returns the original result (flagged replayed: true) without re-running the transition:
// derive the key from your domain entity so retries reuse it
const key = `submit-${order.id}`;
await client.instances.send(instance.id, { type: 'SUBMIT' }, { idempotencyKey: key });client.instances.get(instanceId)
Fetches a single instance by id, scoped to your organization. Returns the current state and context snapshot persisted in the database.
const instance = await client.instances.get('ins_5a6b7c8d');
// instance => { id, currentState, context, status, machineVersion, createdAt, updatedAt }For a continuously-updating view rather than a one-shot read, use instances.subscribe() below.
client.instances.history(instanceId, params?)
Returns the instance's transition log, newest first — the audit trail of every event the instance has processed. Paginate with limit (default 50, max 200) and before (pass the createdAt of the last row from the previous page).
const events = await client.instances.history(instance.id, { limit: 20 });
// events => [{ id, eventType, payload, fromState, toState, createdAt }, ...]Live updates
client.instances.subscribe(instanceId, callback) opens a WebSocket to the live inspector and invokes your callback on every transition. It returns an unsubscribe function that closes the connection.
const unsubscribe = client.instances.subscribe(instance.id, (state) => {
// state => { value, context }
console.log('now in', state.value, 'with', state.context);
});
// later, when you're done:
unsubscribe();Note — the inspector WebSocket accepts either your API key (the SDK appends it to the socket URL automatically) or a logged-in browser session cookie, so subscribe() works from backends and dashboards alike. The callback receives { value, context }, where value is the new state name.
Types
The package exports the following types: MachineDefinition, Instance, and TransitionResult.
import type {
MachineDefinition,
Instance,
TransitionResult,
} from '@orchestate/sdk';
// MachineDefinition — the JSON you publish:
// { initial: string; states: Record<string, unknown>; context?: object }
// Instance — a running machine instance:
// { id: string; currentState: string; context: object; status: string }
// TransitionResult — the outcome of instances.send():
// { fromState: string; toState: string; final: boolean }For the full MachineDefinition shape — states, transitions, and actions — see Machine definitions.
See also
- HTTP API — the raw endpoints behind every SDK method.
- Auth & API keys — issuing and revoking the keys the client uses.