Mono Colombia

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

StateTerminal?Webhook event
createdNocollection.created
readyNocollection.ready
minimum_paidNocollection.minimum_paid
paidYescollection.paid
discardedYescollection.discarded
failedYescollection.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 cumulative paid_amount reaches or exceeds total_minimum_amount.
  • paid once cumulative paid_amount reaches total_maximum_amount. For single-use collections this happens on the first successful payment.
  • discarded if 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:

  • paid once paid_amount reaches total_maximum_amount.
  • discarded on 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 its expires_at without 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

  1. Treat every non-terminal state as transient. A collection in created is not yet usable — wait for ready before surfacing the QR or key to payers.
  2. 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.
  3. Handle out-of-order delivery. Webhook deliveries are at-least-once. If you see paid before minimum_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.
  4. Distinguish collection state from attempt state. A ready collection can have many collection.attempt_unsuccessful events without leaving the ready state. Surface attempt failures separately from collection state in your UI and reconciliation logic.
  5. Three states are final. paid, discarded, and failed are the only terminal states. Once a collection reaches any of them, it will never move again.

On this page