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.
Showing lists
Section titled “Showing lists”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.