Mono Colombia

Collect with intents

One intent per payment — dynamic QR, retries, and exact reconciliation.

This flow declares every expected payment before the money moves: for each order or invoice, your backend creates an intent, Mono mints a dedicated Bre-B key and a dynamic QR for it, and the payment that arrives is linked back to the intent automatically. Reconciliation stops being detective work — you ask "was intent order-1042 paid?" and the API answers.

Use it for e-commerce checkout, invoice payment, or any case where amount and payer are known per payment and must match exactly.

Before you start

You will need:

  • A Mono account with Core and the payins feature enabled for your program. See Methods — program configuration.
  • A collection template created with method_params.co_breb.key_per: "intent" — or the decision to create templateless intents directly against a subaccount.
  • A webhook endpoint subscribed to the payin events.
  • The Intents concept fresh in mind.

Sequence overview

Steps

  1. Create one intent per payment. Idempotent on external_id — use your order ID, so a double-click cannot create two intents:

    {
      "external_id": "order-1042",
      "template_id": "<key_per intent template>",
      "method": "co_breb",
      "merchant_amount": { "amount": 8990000, "currency": "COP" },
      "method_params": { "create_qr": true }
    }

    Send exactly one of merchant_amount or payer_amount. With a templateless intent, replace template_id with subaccount_id.

  2. Wait for payin_intent_ready_to_pay. It delivers payment_tokens — the per-intent key and a single-use QR carrying the exact amount — and expires_at: for Bre-B, 30 minutes. Show the QR in your checkout with a countdown.

  3. The customer pays. The amount is enforced: a payment for anything other than the intent's amount is rejected (intent_amount_mismatch).

  4. Handle the three endings.

    • Paidpayin_intent_successful (plus the payin's own subaccount_credited). Confirm the order.
    • Attempt failedpayin_intent_attempt_failed: the payment bounced but the intent re-armed back to ready_to_pay. Keep the QR on screen; the customer can retry against the same key while the window lasts.
    • Expiredpayin_intent_expired: the window closed unpaid. The external_id is freed, so offering a fresh checkout for the same order is safe.
  5. Reconcile without storing Mono IDs. GET /v1/core/payins/intents/find_successful_by?external_id=order-1042 returns the successful intent for your identifier, or nothing — a one-call answer to "was this paid?".

Template or templateless?

  • With a key_per: "intent" template — intents inherit limits, expected payers, and payin_data; you manage one campaign configuration in one place.
  • Templateless — each intent stands alone against a subaccount, carrying its own configuration. Fewer moving parts when there is no shared campaign to speak of.

Next steps

On this page