Collection Lifecycle
State machine for Bre-B collections — transitions, webhooks, and terminal states
A collection is a Bre-B receiving endpoint — typically a QR code or a shareable payment key — that lets one or more payers send funds into your tenant account. From the moment you create it, the collection moves through a deterministic sequence of states. Each transition maps to a webhook event, so your integration can track progress without polling. This guide explains what each state means, what makes a collection move between them, and how to tell terminal success from terminal failure.
State diagram
States at a glance
| State | Terminal? | Webhook event |
|---|---|---|
created | No | collection.created |
ready | No | collection.ready |
minimum_paid | No | collection.minimum_paid |
paid | Yes | collection.paid |
discarded | Yes | collection.discarded |
failed | Yes | collection.failed |
Payment attempts are tracked separately
Individual payment attempts against a ready or minimum_paid collection have their own
state machine (successful, rejected, failed) and emit their own events —
collection.attempt_successful
and
collection.attempt_unsuccessful.
A failed attempt does not move the collection out of its current state — it only
increments the failed_attempts counter on the collection.
Detailed walkthrough
1. created
Initial state. A collection enters this state as soon as
create collection
accepts your request. The collection row exists in our system and has an id
you can use for lookups, but the underlying Bre-B key has not yet been
registered with the central directory (DICE), so it cannot accept payments
yet.
Validation errors never reach this state
If the request fails synchronous validation — for example, the amount limits are invalid or the key format is malformed — the API returns a 4xx response and the collection is not persisted. It will never move through this state machine and no webhook will fire.
What happens next: the system registers the Bre-B key with DICE. This typically completes within seconds.
2. ready
The Bre-B key has been successfully registered with DICE and the collection's
keys array is populated with the active key. The collection is now live and
can accept incoming payments — payers can scan the QR or send to the key.
Next states:
- →
minimum_paid(multi-use collections only) once cumulativepaid_amountreaches or exceedstotal_minimum_amount. - →
paidonce cumulativepaid_amountreachestotal_maximum_amount. For single-use collections this happens on the first successful payment. - →
discardedif the collection is deleted, expires, or is auto-discarded for inactivity before being paid.
3. minimum_paid
Only applies to multiple-use collections that define both a minimum and a
maximum amount. The cumulative paid_amount has crossed
total_minimum_amount, meaning the collection has met its soft target but is
still open for additional payments up to total_maximum_amount.
This state is useful for partial-payment scenarios — for example, a goal-based fundraiser that has hit its goal but still accepts contributions until a hard cap.
Next states:
- →
paidoncepaid_amountreachestotal_maximum_amount. - →
discardedon expiration or inactivity.
4. paid (terminal)
The collection has been fully paid. paid_amount equals
total_maximum_amount. The collection no longer accepts payments and no
further state transitions occur.
5. discarded (terminal)
The collection has been removed from the active set. The state_reason field
tells you why:
deleted— explicitly removed via delete collection.expired— the collection passed itsexpires_atwithout reaching a paid terminal state.inactivity— the collection was idle long enough to be auto-discarded by the system.
No further state transitions occur.
6. failed (terminal)
The collection could not be made usable. This happens when the Bre-B key
registration fails between created and ready. The state_reason field
carries the cause — common values include key_already_registered,
key_registration_failed, and key_canceled.
Failed collections do not accept payments and never transition again. To recover, create a new collection (typically with a different key).
Working with states in your integration
- Treat every non-terminal state as transient. A collection in
createdis not yet usable — wait forreadybefore surfacing the QR or key to payers. - Key off webhooks, not polling. Every state transition emits a webhook
with the new state and, where applicable, a
state_reason. Verify each webhook (see Webhook signature verification) and update your local record idempotently — the same state can be retried by our workers. - Handle out-of-order delivery. Webhook deliveries are at-least-once. If
you see
paidbeforeminimum_paid, keep the later state and ignore the earlier one. A simple rule: if the collection is already in a terminal state locally, ignore non-terminal webhooks. - Distinguish collection state from attempt state. A
readycollection can have manycollection.attempt_unsuccessfulevents without leaving thereadystate. Surface attempt failures separately from collection state in your UI and reconciliation logic. - Three states are final.
paid,discarded, andfailedare the only terminal states. Once a collection reaches any of them, it will never move again.