Event flows

An event flow runs by itself, shortly after items change: when a vote is created, bump the post's vote count; when an order is paid, take the stock; when a user renames themselves, copy the new name onto their profile. It's the same kind of flow as an endpoint, with the same nodes, but nothing calls it.

An event flow runs after the change has been saved, not as part of it. The write that caused it has already succeeded, and isn't slowed down or undone by the flow. If you need two writes to happen together or not at all, use an endpoint flow instead.

What starts a flow

In the editor's Flow panel, choose After an item event, then:

  • eventsAny of item.created, item.updated, item.restored and item.deleted.
  • listsThe lists to watch, by path name or id.
  • whenOptional: an expression that must be true for the flow to run. Runs where it's false are dropped, and not kept in the run log.
  • ignoreUserModeDon't run for changes you make yourself in the dashboard, only for changes made with API tokens (and by other flows). Handy when you fix data by hand.

Changes made any way start event flows: with an API token, by an identity, in the dashboard, and by other flows.

What the flow is given

The flow's input is the event, in the same shape a webhook receives it:

{
type: "item.updated"
eventId: "6c1f0e4a-2b7d-4f64-9d0a-8d1c52b7e5a3"
list: {
id: "0f1d2b3c-4e5f-4a6b-8c7d-9e0f1a2b3c4d"
name: "Users"
pathName: "users"
}
item: {
id: "a3b4c5d6-e7f8-4a9b-8c0d-1e2f3a4b5c6d"
identityId: null
version: "4"
description: null
tags: [
]
readonly: false
createdAt: "2026-09-20T09:00:00.000Z"
updatedAt: "2026-09-27T14:03:13.000Z"
data: {
displayName: "Alice B"
profileId: "p1"
}
}
previous: {
id: "a3b4c5d6-e7f8-4a9b-8c0d-1e2f3a4b5c6d"
version: "3"
data: {
displayName: "Alice"
profileId: "p1"
}
}
actor: {
authMode: "token"
tokenId: "7d8e9f0a-1b2c-4d3e-8f4a-5b6c7d8e9f0a"
identityId: null
}
}

previous is the item before an update or restore, and null otherwise. For a deletion, item is the item as it was. actor says who made the change: authMode is token (with the identityId, if an identity made it), user (you, in the dashboard) or flow (with the flowId and runId).

Two more names make comparing easy: old is the item's data before the change, and new is its data after. The change functions from write rules work on them, so a when can be as simple as:

1changed("/status")
2new.status == "paid" && old.status != "paid"
3input.type == "item.deleted" || new.voteCount > 100

The flow's identity and token are the ones that made the change, if there were any.

To try an event flow in the editor's Test tab, click Use a sample event for an input in this shape, and edit it.

When and in what order

  • Each event is queued, and runs usually start within a second or two.
  • Events for the same item run one at a time, in the order they happened, for each flow. Events for different items can run at the same time.
  • Each run is one transaction, like an endpoint flow's, and can take up to 30 seconds.
  • A flow's page shows the runs waiting to start, as well as the ones that have finished.

Retries and running twice

A run that couldn't finish for a reason that might go away (it ran out of time, it lost a race for an item, an index was still being built, or something went wrong on our side) is tried again after 10 seconds, a minute, 5 minutes and 30 minutes. While it waits, later events for the same item wait too, so the order still holds. A run that fails for a reason that won't go away (a require node, an item that isn't there, a JSON schema) isn't tried again.

So an event can occasionally run more than once: a run whose transaction didn't commit is simply run again, and that's always safe, but a run that committed just as our server stopped could be run again too. For writes that must happen exactly once, use flow.eventId, which is the same every time an event is tried. Record it, and check for it:

receipt-per-order.flow.json
12345678910111213141516171819202122232425262728293031323334{ "version": 1, "name": "receipt-per-order", "entry": { "type": "event", "events": ["item.created"], "lists": ["orders"] }, "nodes": [ { "id": "existing", "type": "find-items", "list": "receipts", "index": "event-id", "value": "flow.eventId", "limit": 1 }, { "id": "first_time", "type": "condition", "when": "length(steps.existing) == 0" }, { "id": "receipt", "type": "create-item", "list": "receipts", "data": "{ eventId: flow.eventId, orderId: input.item.id, total: new.total }" } ], "edges": [ ["existing", "first_time"], ["first_time", "receipt", "true"] ] }

Here receipts has an index called event-id on /eventId, so find-items can look the event up. A run that commits only does so once the receipt is written, so a second run finds it and does nothing.

Or make the write itself safe to repeat: set a field to a value rather than adding to it, or check the item's state with a require first.

Flows that start flows

An event flow's writes are item events too, so they can start other event flows (or the same one): an order takes the stock, and the stock running low sends an alert. Each run has a flow.depth: 0 for a run started by an ordinary change, and one more for a run started by a flow's write. A run at depth 3 doesn't start any more, which stops a flow that writes to a list it watches from running forever. The editor warns you about a flow like that; give it a when that stops it.

The run log shows how deep each run was, which run started it, and when a run's writes would have started more but the chain was too long. Webhooks for a flow's writes have { "authMode": "flow", "flowId": ..., "runId": ... } as their actor, so your server can tell them apart.

When a flow keeps failing

If 20 runs of an event flow in a row fail (not counting require nodes, which are the flow deciding not to act), JSONPad turns the flow off, and its page says why. Events that happen while a flow is off don't start it. Fix the flow, and turn it back on.

Cost

An event run is one request, however many times it's tried, and each item it reads or writes is one more, as for an endpoint flow. Runs whose when is false are free. If your account runs out of requests and credits, event runs fail until the allowance resets.

An event flow can't respond to anyone, so it has no respond node. To tell your own server about what it did, use an emit node, which sends an event to one of your webhooks.

2026-09-27