Skip to content

Quick Start

Build and run your first durable AI workflow in a few minutes.


1. Install

Install the core Invariant packages:

bash
npm install @invariant/sdk @invariant/runtime @invariant/postgres @invariant/anthropic zod

Start PostgreSQL locally with Docker:

bash
docker run \
  --name invariant-postgres \
  -e POSTGRES_PASSWORD=postgres \
  -e POSTGRES_DB=invariant \
  -p 5432:5432 \
  -d postgres:17

Set your environment variables:

bash
export DATABASE_URL=postgres://postgres:postgres@localhost:5432/invariant
export ANTHROPIC_API_KEY=your_anthropic_api_key

2. Create the Runtime

Initialize invariant with a storage adapter and model provider configuration:

ts
// src/runtime.ts
import { invariant } from "@invariant/sdk";
import { postgres } from "@invariant/postgres";
import { anthropic } from "@invariant/anthropic";

export const app = invariant({
  store: postgres({
    connectionString: process.env.DATABASE_URL!,
  }),

  models: {
    default: anthropic({
      apiKey: process.env.ANTHROPIC_API_KEY!,
      model: "claude-sonnet-4-5",
    }),
  },
});

Invariant is model-agnostic. The default model is automatically used by .reason() boundaries unless a step explicitly chooses another configured model.


3. Define a Workflow

Define a Support Ticket Workflow. Workflows declared via app.workflow() are automatically bound to the application instance:

ts
// src/ticket.ts
import { z } from "zod";
import { app } from "./runtime";

export const supportWorkflow = app
  .workflow("support-ticket", {
    inputSchema: z.object({
      message: z.string(),
      userTier: z.enum(["STANDARD", "VIP"]),
    }),
  })

  // 1. Probabilistic Reasoning (AI)
  .reason("classify-ticket", {
    prompt: ({ input }) =>
      `Classify this support message: "${input.message}"`,

    schema: z.object({
      category: z.enum(["BILLING", "TECHNICAL", "ACCOUNT", "OTHER"]),
      sentiment: z.enum(["POSITIVE", "NEUTRAL", "NEGATIVE"]),
    }),
  })

  // 2. Deterministic Application Logic (Code)
  .step("set-priority", ({ state, input }) => ({
    priority:
      input.userTier === "VIP" || state.sentiment === "NEGATIVE"
        ? "HIGH"
        : "NORMAL",
  }))

  // 3. Controlled Side-Effect (External Action)
  .capability("create-ticket", {
    idempotencyKey: ({ execution }) => `ticket:${execution.id}`,

    handler: async ({ state, input }) => {
      console.log(`🎫 Creating ticket [${state.priority}] (${state.category})`);
      return {
        ticketId: `tkt_${Date.now()}`,
        status: "OPEN",
        assignedTeam: state.category === "BILLING" ? "Finance" : "Support",
      };
    },
  });

The three primitives represent a clear execution sequence:

text
reason()      Probabilistic interpretation    ──► "What does this mean?"
step()        Deterministic application logic ──► "What should our code derive from it?"
capability()  Controlled external effect      ──► "Do something in the external world"

4. Start the Runtime & Run

Start the background worker loop and execute your workflow:

ts
// src/index.ts
import { app } from "./runtime";
import { supportWorkflow } from "./ticket";

async function main() {
  await app.start();

  const run = await supportWorkflow.start({
    message: "I was double charged on my subscription and need an urgent refund!",
    userTier: "VIP",
  });

  console.log(`🚀 Started workflow run: ${run.id}`);

  const result = await run.result();
  console.log("Execution Result:", result);

  await app.stop();
}

main().catch(console.error);

Run it using npx tsx:

bash
npx tsx src/index.ts

What Just Happened?

text
User message

  reason()       ──►  classify-ticket (AI interprets sentiment & category)

   step()        ──►  set-priority (Code calculates priority based on VIP tier & sentiment)

 capability()    ──►  create-ticket (External system creates ticket)

You just combined probabilistic reasoning, deterministic application logic, and an external side effect in one durable execution.

  1. .reason() asked the model one bounded semantic question.
  2. .step() applied deterministic application logic without model involvement.
  3. .capability() crossed into the external world through a transactional outbox.
  4. Invariant preserved execution state durably between every boundary.

Next Steps

  • Mental Model — Understand how Workflows, Agents, and Executions fit together.
  • Agents & Actions — Learn how an app.agent() can evaluate user intent to drive workflows automatically.
  • Crash Recovery Example — Test what happens when you kill a worker process mid-execution.

Invariant Durable Execution Engine.