Skip to content

What lives where

Rungs is not where your business data lives. Keep the record itself (an expense with its lines and receipts, a leave request, a purchase order) in your own tables, and point Rungs at it with the request’s subject. Put in the payload only what the flow’s conditions read.

// Your record, in your table.
await db.expenses.insert(form);
// Its approval, in Rungs: linked by the subject, with only what the conditions read.
await rungs.submit({
flow: "expense",
requesterId: "u_ana",
subject: { type: "expense", id: form.id },
payload: { amount: form.amount },
});

Runs in CI:examples/docs-snippets/src/concepts/host-data.ts

Data Kept by
The record: title, lines, receipts, everything your users edit Your application
The approval: status, steps, tasks, decisions, audit trail Rungs
The link subject: { type, id } on the request
The payload: what conditions read Rungs, as a snapshot at submit
Users, roles, who is active Your application, through callbacks

Why the payload is not the place for business data. It is a snapshot taken at submit and does not follow later edits (EC-36); it is copied into every list result; and it may hold personal data.

Ask Rungs for the approvals and load your records for that page in one query:

// "My expenses": approval status from Rungs, details from your table, one query each.
const page = await rungs.requests({ requesterId: "u_ana", limit: 20 });
const rows = await db.expenses.byIds(page.requests.map((r) => r.subject.id));

Runs in CI:examples/docs-snippets/src/concepts/host-data.ts

rungs.requests() filters by requester, status, subject or flow; rungs.inbox() lists a user’s tasks; rungs.blocked() lists steps waiting for an admin. Rungs does not check who may see a list: show requesters their own requests, approvers their own inbox, and the blocked list to admins.

The example app follows this pattern.