Skip to content

SDK Comparison

This page records what each Ironflow SDK can do — use it to pick an SDK, or to check whether a capability you need is reachable from the one you already have. For the exhaustive per-capability view — every transport, test and documentation status, and gap classification — see the SDK Parity Matrix.

The tables record capabilities, not transport. An SDK may reach a capability over ConnectRPC or a WebSocket instead of the equivalent REST path: the Go SDK resumes a run over ConnectRPC, the Browser SDK watches config over a WebSocket topic. ✅ means you can do it, ❌ means you cannot, and N/A means it is deliberately excluded — as secrets are from the browser. The Agents table reads by a narrower rule, stated above it.

SDKPackageTierWhat it is
Gogithub.com/sahina/ironflow-go/ironflow1Worker runtime + client, for servers and workers
Node@ironflow/node1Worker runtime + client, for servers, workers and handlers
Browser@ironflow/browserOutside the tiers: hand-written like Tier 1 (it shares @ironflow/core with Node), client-only like Tier 2, plus real-time subscriptions
Pythonironflow (distribution ironflow-py on PyPI)2Generated REST + ConnectRPC clients, no worker runtime

The dividing line between the tiers is which direction the call goes.

YOUR APPLICATIONIRONFLOW SERVERCLIENTpublish, query, read streamsCheap · Generated · Every languageWORKER RUNTIMEruns your steps, memoizes, resumes after a crashExpensive · Hand-written · Go + TS onlyengineyou call itit calls you

Tier 1 — worker runtime + client (Go, Node)

Section titled “Tier 1 — worker runtime + client (Go, Node)”

Tier-1 SDKs carry the worker runtime: the engine that runs your function code under Ironflow’s control — durable-step memoization, crash-resume replay, step.sleep / waitForEvent suspension, saga compensation, and pull mode (long-running workers, no timeout). The client methods are fused to that runtime, so a Tier-1 SDK is hand-written end to end and never generated.

Go and TypeScript are the only Tier-1 languages, and that limit is deliberate. No additional Tier-1 languages are planned.

The two Tier-1 SDKs are peers. A capability added to one is incomplete until the other has it. The two implementations do not need to land in the same commit or release. If one ships first, its peer gap must be filed before the release notes are published and those notes must link it. This also applies when a Tier-2 SDK reaches a capability before either Tier-1 SDK. The release may proceed, but the missing Tier-1 work cannot remain implicit.

scripts/check-tier1-sdk-parity.py enforces this mechanically for the public fields of SubscribeOptions, the option type that exposed the first concrete drift. An unmatched field fails unless tier1-sdk-parity.json names the missing SDK and links its implementation issue. The check also rejects an exception after the field reaches both SDKs. This is intentionally narrow. sdk-coverage.json records which routes SDK source calls, not which capabilities or options the SDK exposes, so it is not evidence of Tier-1 capability parity. Broader parity remains a review rule until another concrete case supports a reliable source-level check.

Published blog posts, tutorials, and how-to guides follow the same peer model. When material demonstrates a Tier-1 capability with code, it includes both Go and TypeScript examples. Browser-only and Tier-2 capabilities are exempt. This is an editorial review rule rather than a code-fence-counting gate, because a document may also contain SQL, shell, frontend, or transport-specific examples. The existing blog posts that teach Tier-1 worker capabilities have been backfilled; new material follows this rule from its first publication.

Tier 2 — generated client only (Python today; Rust, C#, Java later)

Section titled “Tier 2 — generated client only (Python today; Rust, C#, Java later)”

Tier-2 SDKs call the Ironflow REST API and its ConnectRPC surface, and contain no worker runtime. Each is built in two parts: a generated surface, machine-generated from the server’s REST API so the client tracks it as the API changes, and a small hand-written transport core supplying retry, backoff, auth, and the error taxonomy.

A Tier-2 SDK is a smaller SDK, not a lesser one. Applications in Tier-2 languages still get their functions executed by Ironflow via push mode: the server POSTs to an HTTP endpoint you own, which needs no SDK runtime at all. What Tier 2 does not get is pull mode and durable steps — workflows that need crash-resume, long sleeps, or sagas are written in Go or TypeScript.

The same REST surface is published as an OpenAPI 3.1 spec at GET /api/v1/openapi.json, so a language with no SDK starts from a machine-readable contract rather than from scratch.

Two protocols, and why Tier 2 only reaches one

Section titled “Two protocols, and why Tier 2 only reaches one”

The server speaks two protocols to clients:

  • REST — plain HTTP paths under /api/v1, published with a machine-readable description and schemas for most of them.
  • ConnectRPC — a protobuf protocol. Paths look like POST /ironflow.v1.WebhookService/CreateSource. These sit outside the REST contract and carry no schema in it.

The client generator reads the REST description only, so a Tier-2 client holds REST methods only. Tier-1 SDKs are unaffected: they are hand-written and call whichever protocol fits. Run resume and step patch now use ConnectRPC in all SDKs. Their duplicate REST routes have been removed (#1972).

Ironflow serverREST APImachine-readable descriptionwith schemasConnectRPC methodsprotobuf definitionsare the schemaREST generatorfrom the route registryBuf + connect-pyfrom the protobufsIronflowClientgenerated REST methodsIronflowRPC90 capability methodsTwo pipelines, two clients. Each carries what its protocol serves.

The generated REST client covers the remaining REST routes, including environment CRUD, event reads, secrets, and projection deletion and partition listing.

The migrated capabilities are delivered by IronflowRPC — 90 capability methods generated from the protobuf definitions (86 unary, 4 server streams), covering webhook management, agent tools, time travel, pub/sub consumer groups, function versioning, executable deployments, raw SQL, and the environment compatibility RPCs. The two clients are not supersets of each other; each carries what its protocol serves.

One gap remains:

Still out of reachWhy
PubSubService/SubscribeBidirectionalServed, but the handler returns Unimplemented. Deliberately not exposed in any SDK

IronflowRPC retries a unary call only when the protobuf method is annotated idempotency_level = NO_SIDE_EFFECTS. It reconnects Subscribe when the caller positions it with start_after_sequence, and no other stream — the rest are positioned by the server on a durable consumer, so re-issuing the call resumes them (#1848). Which of the 95 served RPCs the Python SDK exposes, and why, is recorded row by row in sdk/python/rpc-capabilities.yaml.

A language with no SDK is not locked out. It reaches almost everything in this document over plain HTTP — the REST API under /api/v1, plus the ironflow CLI and the MCP server (ironflow mcp) as no-codegen alternatives. But “generate a client from the OpenAPI spec and you are done” is not true, for the same reason Tier 2 needs two pipelines: the spec describes REST only.

Some ✅ rows above have no REST route at all. They are ConnectRPC, so a client generated from api/openapi.json will not contain them:

CapabilityConnectRPC methodWhat REST gives you
Register functionsIronflowService/RegisterFunctionNo REST sibling
Create projectionProjectionService/RegisterProjection, CreateSQLProjectionDeletion and partition listing, but no create
TriggerSync (blocking)IronflowService/TriggerSyncNo REST sibling
Invoke one function and blockIronflowService/InvokeFunctionSyncNo REST sibling. IronflowService/InvokeFunction returns IDs immediately; InvokeFunctionSync waits for the result (ADR 0067)
Webhook mutation (11 RPCs)WebhookService/*The redacted GET /api/v1/flow/webhooks view
Join a consumer groupPubSubService/JoinConsumerGroupNothing

The unary ones cost you a second path shape, not a second toolchain: a Connect unary call is an ordinary POST /ironflow.v1.Service/Method carrying proto3 JSON, which any HTTP client can send. What you give up is the schema — these sit outside the REST contract, and the field names are camelCase where REST is snake_case. Use Ironflow from any language walks the registration call end to end.

Registration being RPC-only has a knock-on effect: an OpenAPI-only client has nothing to invoke until something else registers a function, and no scheduled work either — a cron is a field on the function, so api/openapi.json contains no cron at all.

Three carve-outs, not two. Durable Step Primitives and agent authoring run inside a worker and have no equivalent outside one. The third is receiving pushed events, and it is not a worker feature: PubSubService/Subscribe and JoinConsumerGroup are server streams, SubscribeBidirectional returns Unimplemented by design, and the OpenAPI artifact carries no streaming media type — no text/event-stream, no WebSocket upgrade. The GET /ws and KV/config /watch upgrades exist on the server and are documented in the REST API reference, but they are absent from the generated spec, so codegen never sees them. Inbound from generated code means push mode: an HTTP endpoint you own, which the engine calls.


CapabilityGo SDKNode SDKBrowser SDKPython SDK
Emit events
Trigger workflows
TriggerSync (blocking)
Trigger a batch of events
List stored events
Get a stored event
List event names
Offline write queue (durable outbox)N/AN/AN/A

CapabilityGo SDKNode SDKBrowser SDKPython SDK
Define functionsN/A
Register functionsN/A
List functions
Get function detail
Enable or disable function
Delete function
List function version history
Get function at version
Roll back function version
Invoke function directly

“Invoke function directly” is the client-level call, keyed by function id: InvokeSync in Go, invoke() in Node and the browser, rpc.functions.invoke or rpc.runs.invoke_function_sync in Python. All four block for a result except the Python REST call, which answers with a run_id you poll — see ADR 0067. It is a different thing from step.invoke / step.invokeAsync, which invoke a function from inside a workflow and memoize — see Durable Step Primitives below.


These run inside a function handler, under Ironflow’s control. They are the worker runtime, so only Tier-1 SDKs have them.

CapabilityGo SDKNode SDKBrowser SDKPython SDK
Memoized step (step.run)N/A
Sleep / sleep-untilN/A
Durable wait for eventN/A
Parallel branchesN/A
Map over itemsN/A
Saga compensationN/A
Branch-scoped steps (nested fan-out, invoke, publish inside a branch)N/A
Invoke another function (sync / async)N/A
Publish to a topic from a stepN/A

CapabilityGo SDKNode SDKBrowser SDKPython SDK
Get run details
List runs
Get run steps
Get entity streams touched by a run
Cancel run
Resume run
Get audit trail (run events)

Scoped Injection (Hot Patching & History Editing)

Section titled “Scoped Injection (Hot Patching & History Editing)”
CapabilityGo SDKNode SDKBrowser SDKPython SDK
Pause run
Get paused state
Inject step output
Patch step (modify outputs)

The Python entry is rpc.runs.patch_step(PatchStepRequest(...)).


CapabilityGo SDKNode SDKBrowser SDKPython SDK
Get run state at timestamp
Get run timeline
Get step output at timestamp

CapabilityGo SDKNode SDKBrowser SDKPython SDK
Append event to stream
Read stream events
Get stream info
Create snapshot
Get snapshot
List all streams
Get entity event history

CapabilityGo SDKNode SDKBrowser SDKPython SDK
List projections
Get projection
Get projection status
List projection partitionsN/A
Create projectionN/A
Rebuild projectionN/A
Get rebuild job statusN/A
Create SQL projectionN/AN/A
Query SQL projectionN/A
Execute raw SQLN/A
Pause projectionN/A
Resume projectionN/A
Cancel rebuildN/A
Delete projectionN/A
Wait for catch-up (read-your-writes)

CapabilityGo SDKNode SDKBrowser SDKPython SDK
Publish to topic
List topicsN/A
Get topic statisticsN/A
Join a consumer group (subscribe with acks)
Manage consumer groups (create/get/list/update/delete)

CapabilityGo SDKNode SDKBrowser SDKPython SDK
Create bucket
List buckets
Get bucket
Delete bucket
Get key
Put key
Create key (fails if present)
Update key (compare-and-set)
Delete key
Purge key (drop history)
List keys
Watch keys

Exclusive create and compare-and-set both ride on conditional request headers. The generated Python kv_update_buckets_keys() method exposes them as if_none_match and if_match keyword arguments.


CapabilityGo SDKNode SDKBrowser SDKPython SDK
Get config (read)
List configs
Set config (full replacement)N/A
Patch config (shallow merge)N/A
Delete configN/A
Watch config (real-time)

CapabilityGo SDKNode SDKBrowser SDKPython SDK
Get secretN/A
Set secretN/A
Replace secret valueN/A
Patch secret name or descriptionN/A
Delete secretN/A
List secretsN/A

CapabilityGo SDKNode SDKBrowser SDKPython SDK
Register schema
List schemas
Get schema (latest)
Get schema version
Delete schema version
Test upcast

Authoring an agent is worker-runtime work, so it is Tier-1 only. The Browser SDK carries the client half — start a run, follow it, read its memory.

This table is the one place that records named SDK helpers rather than raw reachability. An agent’s memory is an ordinary entity stream behind an ordinary projection, and an agent run starts from an ordinary event, so any client that can append an event, read a projection, or emit can do those three things by hand. For the Python column, ❌ on the memory and agent-invoke rows means the SDK ships no method named for the agent use of them — not that the data is out of reach. The note under the table says the same thing about Go and Node for the last two rows.

CapabilityGo SDKNode SDKBrowser SDKPython SDK
Define an agent handlerN/A
Define a toolN/A
Human approval gateN/A
LLM call wrapperN/A
Spawn a sub-agentN/A
Expose tools over MCPN/A
Write agent memory
Read agent memory
Invoke an agent from a client
Subscribe to agent run events
List visible agent toolsN/A

The last two rows are a client convenience the Browser SDK has and the others do not. From Go or Node you start an agent by emitting its trigger event, and follow it with an ordinary run subscription.


CapabilityGo SDKNode SDKBrowser SDKPython SDK
Create webhook source
Get webhook source
List webhook sources
Update webhook source
Delete webhook source
Rotate verify secret
Expire previous secret
Disable signature verification
Rotate ingest token
Test verify config
List webhook deliveries

Webhook mutation is ConnectRPC-only. Python reaches all of it through rpc.webhookscreate_source, get_source, rotate_secret and the rest — which is the capability IronflowRPC was added for. IronflowClient still sees only the redacted flow-map view (names, prefixes, delivery counts).

Test verify config is a Dashboard-facing diagnostic. Python wraps it because the generated facade covers every WebhookService method; Go, Node and Browser have no wrapper for it.


CapabilityGo SDKNode SDKBrowser SDKPython SDK
Create API key
List API keys
Get API key
Delete API key
Rotate API key

CapabilityGo SDKNode SDKBrowser SDKPython SDK
Create org
List orgs
Get org
Update org
Delete org

CapabilityGo SDKNode SDKBrowser SDKPython SDK
Create role
List roles
Get role
Update role
Delete role
Assign policy to role
Remove policy from role
List policies assigned to role
Create policy
List policies
Get policy
Update policy
Delete policy

CapabilityGo SDKNode SDKBrowser SDKPython SDK
Manage projectsN/A
Manage environmentsN/A
Manage usersN/A
Change own passwordN/A
List workers
List tenants
Provision tenant

CapabilityGo SDKNode SDKBrowser SDKPython SDK
List / requeue / discard outbox dead lettersN/AN/AN/A
List circuit breakers, reset a breakerN/AN/AN/A
List / clear debounce entriesN/AN/A
Inspect capacity (lanes, queue, leases, credits, sessions)N/AN/A
List / install policy templatesN/AN/AN/A
Rotate the cluster join tokenN/AN/AN/A
Read the org-wide audit log

The Tier-2 client remains widest here because its methods are generated from the whole REST surface. Go, Node, and Browser expose the environment-wide audit log; for the other operational routes, use the ironflow CLI or call REST directly.

Platform-tier administration — platform keys, tenant provisioning, impersonation — is a separate surface; see the Platform API reference.


CapabilityGo SDKNode SDKBrowser SDKPython SDK
Check liveness
Check readiness
Discover server capabilities

CapabilityGo SDKNode SDKBrowser SDKPython SDK
Dry-run a policyN/AN/AN/A
List policy versions and roll backN/AN/AN/A
Replace an API key’s rolesN/AN/AN/A
Read authentication and authorization auditN/AN/AN/A

These are intentionally outside the curated Go, Node, and Browser clients. They are not license-gated: the generated Python client reaches them because it covers the full REST and mounted ConnectRPC admin surface. Operators can also use the dashboard, CLI where available, or the underlying endpoint.


FeatureGo SDKNode SDKBrowser SDKPython SDK
Push mode (serverless functions)N/A
Pull mode (long-running workers)N/A
Subscriptions (real-time events)
WebSocket support
ConnectRPC support
HTTP/REST support

Push mode ❌ for Python means the SDK has no handler runtime. A Python application still receives push invocations: the server POSTs to an HTTP endpoint you write yourself, which needs no SDK at all — see SDK Tiers.

Python real-time subscriptions use the tested IronflowRPC.pubsub.subscribe() ConnectRPC server stream. Python still has no WebSocket client and no agents.subscribe() convenience for following an agent run, which are the narrower gaps recorded in those rows.


  • Building high-performance backend services
  • Implementing complex state machines or sagas
  • Integrating with existing Go applications
  • Need the broadest coverage of the workflow, event-sourcing and pub/sub surface
  • Building serverless functions (AWS Lambda, Vercel)
  • Using JavaScript/TypeScript throughout your stack
  • Need full control over step execution and error handling
  • Prefer working in TypeScript for type safety
  • Building real-time frontends with live event subscriptions
  • Implementing interactive dashboards or monitoring tools
  • Need lightweight client-side integrations
  • Want to display projection data and KV store values in UI
  • Building data pipelines or scripts that interact with Ironflow
  • Integrating Ironflow with Python-based AI/ML workflows
  • Running operational tasks — it is the only SDK that reaches the outbox DLQ, circuit breakers, and capacity views
  • Want to manage Ironflow resources from Jupyter notebooks or CLI tools

Note: The Python SDK is Tier 2 — client API methods only, no worker runtime, by design (see SDK Tiers). For durable step execution, use the Go or Node SDK.


Features are implemented incrementally, and these tables reflect the current state of each SDK.

The Python SDK is Tier 2 — generated client methods only, no worker runtime. It installs from PyPI as ironflow-py (import name ironflow) from v0.33.0. See the Python SDK reference for its full surface.