Ana has three unpaid invoices with you: $1,250, $480 and $2,100. Each one should get its own reminders on its own schedule, and when she pays the $480 one, that reminder should stop while the other two carry on.
Until now a journey could not do that. A journey ran once per person: while Ana's run for her first invoice was live, the event for her second invoice was dropped. And a stop event like invoice_paid ended the one run she had, whichever invoice it was about.
Key the journey on the invoice
A journey triggered by an event can now set a run key: the event property that identifies one run. In the builder, open the trigger, pick the event (invoice_created) and type the property under One run per (invoice_id). Over the API or MCP, it is parallelRunsBy: "invoice_id" on journeys_set_trigger.
From then on every distinct invoice_id starts its own run for the same person, and each run carries its own event's data into its messages: that invoice's amount, that invoice's payment link.

Send one event per invoice
Each invoice is one call to the events endpoint:
curl -X POST "$BASE/events" \
-H "Authorization: Bearer $BOOM_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "invoice_created",
"externalId": "inv_1057_created",
"personExternalId": "ana_torres",
"properties": { "invoice_id": "1057", "amount": 480, "pay_url": "https://pay.example.com/1057" }
}'
Send the same call for invoice 1042 and 1063 and Ana is in three runs at once. A few details matter here:
- Use the single-event endpoint. Bulk event recording is treated as backfill and does not start journeys.
externalIdis the idempotency key. Re-sending the same one returnscreated: falseinstead of a duplicate.- The run key must be a top-level property of
properties(invoice_id, notinvoice.id). An event without it is refused rather than enrolled, so no reminder goes out with a blank amount. - A value runs once, ever. A retried or replayed
invoice_createdfor 1057 cannot restart a sequence that already finished.
Cancel only the invoice that was paid
Add invoice_paid to the journey's Stop conditions (metadata.cancelOnEvents over the API). On a journey with a run key, a stop event is matched on that same key, so when Ana pays invoice 1057 you send:
curl -X POST "$BASE/events" \
-H "Authorization: Bearer $BOOM_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "invoice_paid",
"externalId": "inv_1057_paid",
"personExternalId": "ana_torres",
"properties": { "invoice_id": "1057" }
}'

That run ends. The runs for 1042 and 1063 do not notice. Two consequences follow from matching on the key:
- A stop event without
invoice_idstops nothing. It looks like a bug and is deliberate: the alternative is stopping every run the person has. - You cannot turn the matching off on a keyed journey. Setting
matchRunKey: falseis refused at publish, because one payment would cancel the person's other invoices too.
Guard against the race
The triggering event and the stop event are processed independently. If Ana pays within seconds of the invoice being created, invoice_paid can arrive before the run it should stop exists, and the run still starts.
So do not rely on the stop event landing first. Put a Decision step before each send that checks whether the invoice is still pending. The stop event ends the run early in the common case, and the decision catches the rare one.
The journey itself
- Send the invoice, with the amount and link from the event.
- Wait for a weekday, not before 9:00. Nobody gets a payment reminder at 6 a.m. on a Sunday.
- Decision: still unpaid?
- Send the reminder.
A keyed journey can only send. There is no Wait for reply and no AI conversation, because a reply belongs to the person, not to one run: one "ok, thanks" about the first invoice would release the reminders for all three. Runs also start only from the event; adding someone by hand or from a segment is refused, since there is no invoice to take the data from.
Journeys without a run key work exactly as before.
Matching runs back to your system
Each run reports its trigger's externalId on the journey webhooks, so when journey_run.started or journey_run.ended arrives you can tie it to inv_1057_created without storing anything of ours.
Runs in parallel are enabled per organization. If you do not see One run per in the trigger, ask your Boom contact. The full reference is in our documentation.