$ cat ./for-engineers.md

you write TypeScript.
we run the rest.

domain-native nodes that hit your DB directly. durable execution without operating a Temporal cluster. workflows as code-reviewable JSON. the visual builder is a debugger — not your source of truth.

domain-native

TS functions touching your DB.

same TypeScript, same ORM, same auth context, same connection pool, same secrets. zero HTTP marshalling between the workflow and your domain logic — the node runs in your worker, in your VPC.

durable execution

7-day waits. replay on crash.

workflows survive deploys, crashes, and 7-day human approvals. deterministic history. built on Temporal so we don't lie about the primitives — but you don't run the cluster.

workflow-as-JSON

diff in PR. roll back like code.

engineer-authored workflows live in your git repo — PR review, CI lint, branch protection, codeowners. customer-authored workflows live in your database with the same RLS as the rest of your tenant data. never locked in our cloud.

decorators for free

cache, retry, rate-limit, audit.

attach to any node as stickers. author once, reuse across the catalog. the engine composes them at execution time — you write business logic, not the framework code that protects it.

validated at every step

errors in the editor. not in prod.

the catalog declares types. the engine validates inputs, outputs, references. the AI agent can only compose with what's typed — so a hallucinated step never reaches your execution plane.

replayable executions

the graph IS the debugger.

every run produces a deterministic history. re-render the graph at any past moment, click a node, inspect its inputs and outputs. no print-statements, no log archaeology.

signal: 30 lines of TypeScript

this is the entire surface area you write to add a node to the catalog

// runs in your worker. your VPC. your DB.
export const chargeCustomer = defineNode({
  id: "charge_customer", category: "payments",
  decorators: [withRetry({ attempts: 3 }), withAudit()],
  inputs: { amount: z.number().int(), customerId: z.string() },
  outputs: { chargeId: z.string(), status: z.enum(["ok","declined"]) },
  async execute({ amount, customerId }, ctx) {
    const charge = await ctx.db.tx((tx) => {
      return stripe.charges.create({ … });
    });
    return { chargeId: charge.id, status: charge.status };
  },
});

push it with `flowget push`, and the node shows up in the catalog. the AI authoring agent can now compose it into customer workflows. no HTTP wrapper, no separate control-plane DB to keep in sync, no marshalling.

the Zod schemas above are the contract end-to-end: the engine infers chargeCustomer's outputs as the typed input of whatever node consumes { chargeId, status } next. no casting, no `any` at the boundary — the editor catches a schema mismatch before the workflow is saveable.

stop writing glue code.
ship business logic.

alpha SDK is open by introduction. talk to us first, ship together — and we'll get you a key.