TypeScript · BPMN 2.0 · MIT

BPMN diagrams
from code,
not clicks.

A fluent TypeScript API that generates deployable BPMN 2.0 diagrams with auto-layout — for developers, automation platforms, and AI agents.

Analysts still review and edit everything visually. It replaces the copy-paste-redeploy gap between the diagram and production, not the diagram.

0
deps
ESM
node · deno · bun · edge
MIT
pre-1.0

The panel beside this text cycles through five example scripts, typing out a BPMN Kit builder call and rendering the resulting diagram live. The same builder API is available as static, editable code in the playground below.

01

The same process, two ways.

hand-written BPMN XML · 41 lines
<bpmn:definitions
  xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL"
  xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI"
  xmlns:zeebe="http://camunda.org/schema/zeebe/1.0">
  <bpmn:process id="p" isExecutable="true">
    <bpmn:startEvent id="s">
      <bpmn:outgoing>Flow_1</bpmn:outgoing>
    </bpmn:startEvent>
    <bpmn:serviceTask id="t">
      <bpmn:extensionElements>
        <zeebe:taskDefinition type="worker"/>
      </bpmn:extensionElements>
      <bpmn:incoming>Flow_1</bpmn:incoming>
      <bpmn:outgoing>Flow_2</bpmn:outgoing>
    </bpmn:serviceTask>
    <bpmn:endEvent id="e">
      <bpmn:incoming>Flow_2</bpmn:incoming>
    </bpmn:endEvent>
    <bpmn:sequenceFlow id="Flow_1" sourceRef="s" targetRef="t"/>
    <bpmn:sequenceFlow id="Flow_2" sourceRef="t" targetRef="e"/>
  </bpmn:process>
  <bpmndi:BPMNDiagram>
    <bpmndi:BPMNPlane>
      <bpmndi:BPMNShape bpmnElement="s">
        <dc:Bounds x="152" y="82" width="36" height="36"/>
with BPMN Kit · 13 lines
import { Bpmn } from "@bpmnkit/core";

const xml = Bpmn.export(
  Bpmn.createProcess("my-flow") // fluent API
    .startEvent("start")        // trigger
    .serviceTask("task", {
      name: "Do Something",
      taskType: "my-worker",    // Zeebe type
    })
    .endEvent("end")
    .withAutoLayout()           // Sugiyama
    .build()
);

// ✓ Valid BPMN 2.0 XML
// ✓ Auto-layout applied
// ✓ Zeebe extensions set
02

What you get

Auto-layout

Sugiyama engine with orthogonal edge routing. No coordinate math, ever.

Type-safe

29 guards narrow the BpmnFlowElement union. Errors are instanceof-catchable with machine-readable codes.

isBpmnServiceTask(el)

Roundtrip fidelity

Parse → modify → export with no data loss. Zeebe extensions, custom namespaces and DI preserved.

Zero dependencies

Pure ESM, tree-shakeable. Browsers, Node, Deno, Bun and edge runtimes.

0 third-party deps

DMN & Forms

The same builder pattern for DMN 1.3 decision tables and Camunda form JSON, referenced from the process.

LLM-friendly format

A compact intermediate form fits a whole diagram in one prompt. The SDK validates and renders what the model returns.

compactify(defs) / expand(c)
04

Camunda 8, end to end

A fully typed REST client — 180 methods across 30+ resource classes, three auth modes, retry with backoff — plus casen, a terminal UI for the same API. And 100 built-in OpenAPI specs covering 18,145 endpoints that generate typed Zeebe connector templates. Browse the catalog →

client.ts
import { CamundaClient } from "@bpmnkit/api";

const client = new CamundaClient({
  baseUrl: "https://api.cloud.camunda.io",
  auth: {
    type: "oauth2",
    clientId:     process.env.CAMUNDA_CLIENT_ID!,
    clientSecret: process.env.CAMUNDA_CLIENT_SECRET!,
    tokenUrl:     "https://login.cloud.camunda.io/oauth/token",
    audience:     process.env.CAMUNDA_AUDIENCE!,
  },
});

// Start a new instance of an already-deployed process
const instance = await client.processInstance.createProcessInstance({
  processDefinitionId: "my-flow",
  variables: { orderId: "ord-123" },
});

// React to lifecycle events
client.on("request", (e) => console.log(e.method, e.url));
client.on("error",   (e) => metrics.inc("api.error"));
180
typed methods
100
connector specs
3
auth modes
auto-retry
OAuth2 / Bearer / Basic LRU + TTL cache Exponential backoff TypedEventEmitter ESM tree-shakeable
$ casen

Interactive TUI over the same API: connection profiles for dev/staging/prod, processes, jobs, incidents, decisions and variables, with scrollable tabular output. Full command reference →

Processes Jobs & Workers Decisions Messages Incidents Variables Signals
casen — interactive TUI

This terminal animates a demo session of casen, the BPMN Kit CLI: navigating the main menu, opening the process command group, listing process definitions and viewing a tabular result.

05

Three steps to a deployable diagram

01

Install

$ pnpm add @bpmnkit/core

# optional
$ pnpm add @bpmnkit/api
$ pnpm add -g casen
$ bun add @bpmnkit/core

# optional
$ bun add @bpmnkit/api
$ bun add -g casen
$ npm install @bpmnkit/core

# optional
$ npm install @bpmnkit/api
$ npm install -g casen
$ yarn add @bpmnkit/core

# optional
$ yarn add @bpmnkit/api
$ yarn global add casen
02

Build a process

import { Bpmn, exportSvg } from "@bpmnkit/core";

const defs = Bpmn.createProcess("hello")
  .startEvent("start")
  .serviceTask("task", {
    name: "Hello World",
    taskType: "greet",
  })
  .endEvent("end")
  .withAutoLayout()
  .build();

const xml = Bpmn.export(defs); // ✓ BPMN 2.0 XML
const svg = exportSvg(defs);   // ✓ SVG image, zero deps
03

Simulate, then deploy

import { Engine } from "@bpmnkit/engine";

// Simulate the process in-process — for tests and local development.
// Deploying to a real Camunda 8 cluster? See the API client below.
const engine = new Engine();
engine.deploy({ bpmn: defs });

engine.registerJobWorker(
  "greet",
  async (job) => {
    console.log("Hello!");
    job.complete();
  }
);
engine.start("hello");

The engine is an in-process simulator for tests and demos. Production execution runs on Camunda 8 / Zeebe via @bpmnkit/api.

06

Decisions and forms, same builder

DMN 1.3 decision tables and Camunda form JSON come out of the same fluent API, and a BPMN process can reference both.

eligibility.ts
import { Dmn } from "@bpmnkit/core";

// Build a DMN decision table
const dmnDefs = Dmn.createDecisionTable("Eligibility")
  .name("Loan Eligibility")
  .input({ label: "Credit Score", expression: "creditScore", typeRef: "integer" })
  .input({ label: "Income", expression: "income", typeRef: "number" })
  .output({ label: "Eligible", name: "eligible", typeRef: "boolean" })
  .output({ label: "Max Amount", name: "maxAmount", typeRef: "number" })
  .rule({ inputs: [">= 700", ">= 50000"], outputs: ["true", "500000"] })
  .rule({ inputs: [">= 600", ">= 30000"], outputs: ["true", "200000"] })
  .rule({ inputs: ["-",       "-"],       outputs: ["false", "0"] })
  .build();

const xml = Dmn.export(dmnDefs); // ✓ valid DMN 1.3 XML

DMN decision tables

Define inputs, outputs and rules; the SDK emits valid DMN 1.3 XML with auto-computed DMNDI layout. Dmn.createDecisionTable(id) builds it, Dmn.export(defs) serialises it, Dmn.layout(defs) positions it.

Fluent builder DMN 1.3 XML Auto-layout DMNDI Hit policies FEEL type refs
application-form.ts
import { Form } from "@bpmnkit/core";

// Build a Camunda form from code
const form = Form.makeEmpty("ApplicationForm");
// Forms are JSON-based; extend with fields:
// { type: "textfield", key: "applicantName", label: "Applicant Name" }
// { type: "number",    key: "requestAmount", label: "Requested Amount" }
// { type: "select",    key: "loanType",      label: "Loan Type",
//     values: [{ label: "Personal", value: "personal" },
//              { label: "Business", value: "business" }] }
// { type: "submit",    label: "Submit Application" }

const json = Form.export(form); // ✓ valid Camunda form JSON

Camunda forms

Scaffold Camunda form JSON from code. Form.makeEmpty(id) gives a baseline structure; extend it with typed fields for text inputs, numbers and dropdowns.

Form JSON export textfield / number / select Schema version 16 Round-trip fidelity
loan-application.ts
import { Bpmn } from "@bpmnkit/core";

// BPMN process referencing a DMN decision and a Camunda Form
const defs = Bpmn.createProcess("loan-application")
  .name("Loan Application")
  .startEvent("start", { name: "Application Received" })

  // User task linked to a Camunda Form by ID
  .userTask("collect-data", {
    name: "Collect Applicant Data",
    formId: "ApplicationForm",
  })

  // Business rule task evaluated by a DMN table
  .businessRuleTask("check-eligibility", {
    name: "Check Eligibility",
    decisionId: "Eligibility",
    resultVariable: "eligibilityResult",
  })

  .exclusiveGateway("gw", { name: "Eligible?" })
  .branch("approved", (b) =>
    b.condition("= eligibilityResult.eligible")
      .serviceTask("disburse", {
        name: "Disburse Loan",
        taskType: "disburse-loan",
      })
      .endEvent("end-ok", { name: "Loan Approved" }),
  )
  .branch("rejected", (b) =>
    b.defaultFlow()
      .serviceTask("notify", {
        name: "Notify Applicant",
        taskType: "send-rejection-email",
      })
      .endEvent("end-rejected", { name: "Rejected" }),
  )
  .withAutoLayout()
  .build();

BPMN with DMN & form references

Link a process to a decision table via businessRuleTask and to a form via userTask. Both use Zeebe extension attributes, so the process is immediately deployable to Camunda 8.

userTask → formId businessRuleTask → decisionId resultVariable Auto-layout
07

Try the builder in the browser

Write Bpmn, Dmn and Form builder expressions. Press Ctrl+Enter (or ⌘ Enter) to render. Nothing to install.

playground.ts Ctrl+Enter to run
Examples
08 For process teams

Analysts model it. Developers ship it.

Standard BPMN 2.0 files open in any tool, including Camunda Modeler. Nothing here is a lock-in format. Explore use cases →

Faster process changes

Updates ship through code review — versioned, tested, audit-traceable.

AI-drafted workflows

Describe a process in plain language; get a valid diagram analysts review visually.

No license fees

MIT, open source, standard files. Leave whenever you want.

Write the diagram like you write the code.

Or try the builder in the browser playground — nothing to install.