Smart Collections is currently in Beta. Some contracts on this page are still being confirmed with our engineering team, so treat them as provisional and expect refinements before general availability, including:
- Webhook payloads and event codes
- API reference updates for the Collection Instructions, Payment Authorization, and Charge endpoints
- Field names and documentation improvements (terminology, diagrams)
Contact your Belvo account manager to join the Beta program.
In Phase 1 of Smart Collections, you delegate balance monitoring to Belvo. You define Collection Instructions, setting out the amounts you want to collect and the balance conditions that must be met, and Belvo watches the linked account in the background. When an account has enough money, Belvo notifies you so you can collect it in Phase 2.
Balance checking is part of Belvo's aggregation product: it relies on the OFDA consent and banking data from the user's Link. As such, you must complete the Smart Collections prerequisites first, as you need an OFDA Link (link_id) to create Collection Instructions and a webhook endpoint to receive balance events.
Phase 1 covers three steps: you create a Collection Instruction, Belvo monitors the account balance against it, and Belvo sends you a webhook once there are enough funds to collect.
A Collection Instruction indicates how much you want to collect from your user (link_id) and over what period of time. Belvo uses this information to monitor the link's account balance and once the balance condition is met, you will receive a webhook event indicating that you can pull funds for a given amount.
Each Collection Instruction item in the items array refers to a singular collection that you want to act on. Most integrations create one Collection Instruction per debt: a single item describing that debt, with one or more collection policies. (An instruction can hold multiple items if you want to batch several debts under one call, but one-item-per-debt is the common pattern.)
To create one, make a POST Create a collection instruction request:
{
"link_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"items": [
{
"amount": "150.50",
"external_id": "inv-2026-001",
"collection_policies": [
{
"start_date": "2026-11-01",
"end_date": "2026-11-10",
"amount": "150.50"
},
{
"start_date": "2026-11-21",
"end_date": "2026-11-30",
"amount": "150.50"
}
]
}
]
}A collection policy defines a window of time during which Belvo checks the account. Add more than one policy to an item to control when monitoring happens. In the example above, Belvo checks during the first ten days of November, pauses, then checks again in the last ten days. No checks happen during the gap between windows.
Both start_date and end_date are inclusive, so a window runs to the very end of its end_date.
Belvo notifies you as soon as the available balance of any one of the user's accounts is greater than the item's amount, and then stops monitoring that item. Balances across accounts aren't added together.
Collection windows run from 00:00 UTC on start_date to 23:59:59 UTC on end_date, not in the payer's local time. In Brasília time, a window opens at 21:00 on the day before start_date and closes at 20:59 on end_date.
Within a window, Belvo checks the balance about seven times a day, at varying times. A balance that is only briefly high enough may be missed.
Belvo uses the account's available balance as reported by the bank, which can include an overdraft limit (cheque especial). A user can therefore trigger item_ready_to_charge with less money of their own than the item's amount.
| Parameter | Required | Description |
|---|---|---|
link_id | true | The id of the OFDA Link for the user whose account balance you want to monitor. It must be an active Link that belongs to your organization, otherwise Belvo responds with 400 - Bad Request. |
items | true | Array of items, with at least one entry. Each item is one amount you want to collect, with one or more collection policies. Most Collection Instructions have exactly one item, one debt per instruction. |
items[].amount | true | The amount you want to collect for this item, as a string. Belvo notifies you once the available balance of one of the user's accounts is greater than this amount. |
items[].external_id | optional (strongly recommended) | Your own reference for this item. Belvo does not link Collection Instructions to the Payment Authorization or Charge you create in Phase 2, so this is how you tie a webhook back to your records. Set it unless you have a reason not to. Since most instructions hold one item per debt, this is typically your reference for the debt as a whole. |
items[].collection_policies | true | Array of policies, with at least one entry. Each policy defines a window of time (start_date to end_date) during which Belvo checks the account. Use several policies to control when monitoring happens. |
items[].collection_policies[].start_date | true | The first date on which Belvo checks the account, in YYYY-MM-DD format. Inclusive. |
items[].collection_policies[].end_date | true | The last date on which Belvo checks the account, in YYYY-MM-DD format. Evaluated in UTC, see the note below. |
items[].collection_policies[].amount | true | The amount for this window, as a string. Set it to the amount you want to collect for the item. See the note below. |
Every policy requires an amount, but today Belvo compares the balance with the item's amount in every window, and a policy's amount has no effect. Set each policy's amount to the same value as the item's amount.
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"link_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"status": "PROCESSING",
"created_at": "2026-10-25T14:22:10.123456Z",
"updated_at": "2026-10-25T14:22:10.123456Z",
"items": [
{
"id": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
"amount": "150.50",
"external_id": "inv-2026-001",
"status": "PENDING",
"created_at": "2026-10-25T14:22:10.234567Z",
"updated_at": "2026-10-25T14:22:10.234567Z",
"collection_policies": [
{
"id": "c3d4e5f6-a7b8-9012-cdef-123456789012",
"start_date": "2026-11-01",
"end_date": "2026-11-10",
"amount": "150.50",
"created_at": "2026-10-25T14:22:10.345678Z"
},
{
"id": "d4e5f6a7-b8c9-0123-defa-234567890123",
"start_date": "2026-11-21",
"end_date": "2026-11-30",
"amount": "150.50",
"created_at": "2026-10-25T14:22:10.345678Z"
}
]
}
]
}The instruction starts in PROCESSING and each item in PENDING. See Statuses and webhooks for the full lifecycle.
Store the instruction id and each item id from this response. You need the instruction id to check or cancel the instruction, and you need both IDs to cancel a single item. No endpoint resolves an item id back to its parent instruction. If you lose an instruction id, you can find it again by listing your instructions.
When the balance condition is met, Belvo sends a single webhook and stops monitoring that item. The item moves to CHARGE_SETTLED, which only means the webhook was sent: no Charge exists yet.
{
"webhook_id": "e29b41d4a7164a4fae1112b8c9d3f5a2",
"webhook_type": "COLLECTIONS",
"webhook_code": "item_ready_to_charge",
"process_type": null,
"link_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"external_id": null,
"data": {
"item_id": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
"external_id": "inv-2026-001",
"amount": "150.50"
}
}The payload identifies the matched item (data.item_id) and its external_id, along with the amount to collect. This is an aggregation webhook, so it has no V1/V2 versioning. For a full field-by-field breakdown, see Statuses and webhooks.
Belvo doesn't link a Collection Instruction to the Payment Authorization or Charge you create next, so use data.external_id (or data.item_id) to correlate this webhook with the Charge you trigger in Phase 2.
When you receive item_ready_to_charge, proceed to Phase 2 and charge data.amount against the user's Payment Authorization.
If none of an item's windows find enough balance by their end_date, the item moves to POLICY_EXPIRED silently, with no webhook sent. The same happens if Belvo can't read the user's accounts (CHARGE_REJECTED). Don't wait indefinitely for item_ready_to_charge: track your items' last end_date and reconcile any that never produced a Charge on your side.
Belvo only sends a webhook when an item reaches CHARGE_SETTLED. To find out how any other item ended, or to recover if you missed a webhook, get the instruction's details and read each item's status:
curl --request GET 'https://api.belvo.com/api/collection-instructions/{instruction_id}/' \
-u SECRET_ID:SECRET_PASSWORDBelvo responds with 200 - OK and the same object you received when you created the instruction, with the current status of each item. See Get a collection instruction's details. The instruction's own status stays PROCESSING until you cancel it, even after every item reaches a final status, so always read the items' statuses.
To find an instruction when you don't have its id, make a GET List collection instructions request:
curl --request GET 'https://api.belvo.com/api/collection-instructions/?created_after=2026-11-01&created_before=2026-12-01' \
-u SECRET_ID:SECRET_PASSWORDThe response is an array of summaries (id, link_id, status, and timestamps) that doesn't include the items. Get the details of an instruction to see them.
| Query parameter | Description |
|---|---|
status | Return only instructions with this status (PROCESSING or CANCELLED). |
created_after | Return only instructions created on or after this date (YYYY-MM-DD), from 00:00 UTC. |
created_before | Return only instructions created before this date (YYYY-MM-DD). The date itself is excluded. |
The consent_id filter is also available, see the API reference.
The list isn't paginated and its order isn't guaranteed. Always send created_after and created_before, so that the response only covers the period you want to reconcile. A filter value that isn't valid, for example an unknown status or a date that isn't in YYYY-MM-DD format, returns a generic 500 - Internal Server Error.
Use these requests to reconcile, not as your main signal: the item_ready_to_charge webhook is how you learn that an item is ready. Belvo checks balances about seven times a day, so asking more often doesn't surface anything sooner. A good moment to check an item is after its last end_date, to find items that ended as POLICY_EXPIRED, CHARGE_REJECTED, or FAILED.
An instruction_id that doesn't exist or doesn't belong to your organization returns a generic 500 - Internal Server Error. An instruction_id that isn't a UUID returns 404 - Not Found.
Monitoring for an item stops permanently once Belvo sends item_ready_to_charge, or once its windows end without enough funds. A Collection Instruction does not resume monitoring on its own, including after a Charge you create later is rejected. To try to collect again, create a new Collection Instruction. Reusing the same external_id on the new item is fine.
If you collect an item's amount some other way before Belvo finds enough balance, for example because your user pays you directly, mark the item as complete. Belvo stops monitoring it, and the item's status becomes COMPLETED, so your records and Belvo's agree on why monitoring ended.
Make a PATCH Mark a collection instruction item as complete request, and identify the item by its id or by the external_id you gave it:
curl --request PATCH 'https://api.belvo.com/api/items/complete/' \
-u SECRET_ID:SECRET_PASSWORD \
-H 'Content-Type: application/json' \
--data '{
"item_id": "b2c3d4e5-f6a7-8901-bcde-f12345678901"
}'| Parameter | Required | Description |
|---|---|---|
item_id | one of the two | The id of the item, from the Collection Instruction response. |
external_id | one of the two | The external_id you set on the item. It must match exactly one item in your organization. |
Send exactly one of item_id or external_id. Belvo responds with 200 - OK and the item:
{
"id": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
"amount": "150.50",
"external_id": "inv-2026-001",
"status": "COMPLETED",
"created_at": "2026-10-25T14:22:10.234567Z",
"updated_at": "2026-11-05T09:12:44.104311Z",
"collection_policies": [
{
"id": "c3d4e5f6-a7b8-9012-cdef-123456789012",
"start_date": "2026-11-01",
"end_date": "2026-11-10",
"amount": "150.50",
"created_at": "2026-10-25T14:22:10.345678Z"
},
{
"id": "d4e5f6a7-b8c9-0123-defa-234567890123",
"start_date": "2026-11-21",
"end_date": "2026-11-30",
"amount": "150.50",
"created_at": "2026-10-25T14:22:10.345678Z"
}
]
}You can only mark an item as complete while its status is PENDING. If a balance check is in progress (PROCESSING), try again a few moments later. Items in any other status can't be completed.
At the moment, every rejected request (an item that isn't PENDING, an unknown item_id, or an external_id shared by several of your items) returns a generic 500 - Internal Server Error. Use the item id when you can, to avoid ambiguous matches.
The difference from cancelling is the reason you record: COMPLETED means the amount was collected outside Belvo, and CANCELLED means you no longer want to collect it.
To stop Belvo from monitoring because you no longer want to collect, for example if the debt was renegotiated or written off, cancel the whole instruction or a single item with a PATCH. (Cancellation is a PATCH rather than a DELETE so that Belvo retains an audit trail.)
| Action | Endpoint | Behavior |
|---|---|---|
| Cancel the whole instruction | PATCH /api/collection-instructions/{instruction_id}/ | Cancels every item that is still PENDING or PROCESSING, and sets the instruction to CANCELLED. Returns the instruction. |
| Cancel a single item | PATCH /api/collection-instructions/{instruction_id}/items/{item_id}/ | Only while the item is PENDING or PROCESSING. Returns the item, now CANCELLED. |
Neither request takes a body. See Cancel a collection instruction and Cancel a collection instruction item. Belvo responds with 200 - OK.
Keep these points in mind:
- Cancelling can't be undone. To monitor again, create a new Collection Instruction.
- Final statuses stay as they are. Items that already reached a final status, such as
CHARGE_SETTLEDorCOMPLETED, keep their status when you cancel their instruction. - Cancelling an item doesn't change its instruction. The instruction stays
PROCESSING, even if you cancelled its last active item. - Cancelling the same instruction twice is safe. Belvo responds with
200 - OKagain.
Belvo returns a generic 500 - Internal Server Error when the instruction or item doesn't exist, doesn't belong to your organization, or isn't in a status that allows the cancellation (for an item, PENDING or PROCESSING). An item that doesn't belong to the instruction in the path is rejected the same way. An ID that isn't a UUID returns 404 - Not Found. Check an item's status with Get a Collection Instruction before you cancel it.