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.
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.
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: nullversion: "4"description: nulltags: []readonly: falsecreatedAt: "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.
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:
{
"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.
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.
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.
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.