Pay-ins

A pay-in collects money from a payer. You ask Borderli for a QR code, show it to the payer, and the payer scans it with any bank or wallet app in their country. The funds settle to your account with our payment partner. Borderli never holds them.

Flow

  1. Issue. POST /v1/collect returns a pending transaction and the QR payload in qr.data.
  2. Show. Render qr.data as a QR image, or offer it as a copy-and-paste string. It can be paid for 10 minutes, until qr.expires_at.
  3. Outcome. When the payer pays, Borderli sends a transaction.completed webhook within seconds. If nobody pays, the transaction becomes canceled and you get transaction.canceled.
  4. Refund if you need to. POST /v1/transactions/{id}/refund sends money back. A transaction.refunded webhook confirms it.

Issue a QR

curl -X POST https://apidocs.borderli.io/v1/collect \
  -H "Content-Type: application/json" \
  -H "x-api-key: brly_test_key_001" \
  -H "Idempotency-Key: order-98765" \
  -d '{
    "amount": 1000,
    "local_currency": "BRL",
    "local_country": "BR",
    "qr_from": "BR",
    "merchant_tax_id": "12345678000195"
  }'
FieldMeaning
amountMinor units of local_currency. 1000 is R$10.00.
local_currency, local_countryThe merchant's currency and country. ISO 4217 and ISO 3166-1 alpha-2.
qr_fromThe country of the wallet that will scan. Use the same value as local_country for a domestic payment.
merchant_tax_idOptional. The tax id of the merchant collecting: CNPJ or CPF in Brazil, CUIT in Argentina, and so on. When omitted, the tax id on your tenant is used. The request is rejected with 422 missing_tax_id when neither is set.

The response is 201:

{
  "transaction": { "id": "txn_01J...", "status": "pending", "direction": "payin" },
  "qr": {
    "data": "00020101021126...",
    "charged": { "value": 1000, "currency": "BRL" },
    "exchange_rate": null,
    "expires_at": "2026-09-14T10:25:00.000Z"
  },
  "request_id": "..."
}

qr.charged is what the payer will pay in their own currency. For a domestic payment it equals amount. For a cross-border payment the rail converts it and exchange_rate holds the rate.

Cancel a QR

POST /v1/transactions/{id}/cancel withdraws a QR that has not been paid. It works only while the transaction is pending; otherwise you get 409.

The response still shows pending. The transaction moves to canceled once the payment partner confirms, by webhook or by Borderli's status poller, usually within a few minutes. The transaction.canceled webhook tells you it is done.

Refund

curl -X POST https://apidocs.borderli.io/v1/transactions/txn_01J.../refund \
  -H "Content-Type: application/json" \
  -H "x-api-key: brly_test_key_001" \
  -d '{ "partial": false, "amount": 1000, "reason": "customer request" }'

A refund is allowed on a completed transaction, once per transaction, for at most the collected amount. Set partial to true when amount is less than what was collected.

The response is an acknowledgement, normally { "status": "PENDING" }. The refund is confirmed by the transaction.refunded webhook. The transaction keeps status: completed; result.refund is added with kind set to full or partial.

Statuses

statusMeaningDetail field
pendingThe QR is open and can be paid.
completedThe payer paid. A refund does not change this status.result
canceledNobody paid.cancellation.reason
failedThe payment did not go through. This QR is now closed. Issue a new one and the payer can try again.error

cancellation.reason is one of:

error.reason is rejected or rail_failure. Which one you get depends on the country: each local rail reports failures differently, and Borderli's payment partner passes that through. Argentina can report rejected; the other countries report rail_failure. Treat both the same way: the collection is over, issue a new QR. error.code carries the bank's own code when the bank supplies one. Most pay-in failures do not have one.

Errors

On a 409 or 422 the code is in details.reason. On a 502 it is the top-level reason.

reasonHTTPMeaning
collect_not_enabled422Your account is not set up for pay-ins. Contact Borderli.
unsupported_qr_from422No supported QR scheme covers this qr_from. The countries are listed by GET /v1/schemes.
missing_tax_id422No merchant_tax_id in the request and no tax id on your tenant. Send one, or ask Borderli to set yours.
corridor_unavailable502This qr_from and local_country pair is not enabled.
fx_unavailable502No exchange rate for this qr_from and local_currency pair.
not_cancelable409The QR is no longer open. It was paid, expired or already canceled.
not_refundable422The transaction is not completed.
amount_exceeds_collected422The refund amount is more than what was collected.
refund_already_requested409A refund was already requested for this transaction. If your earlier refund request ended in a 502, the request is kept as in progress until we confirm with the payment partner. Contact Borderli.

A malformed field, such as a qr_from that is not a two-letter country code, returns 400 before any of these checks run.

Reconciliation

GET /v1/transactions?direction=payin lists your collections with their status and result. There are no chargebacks on Pix or interoperable QR. A completed pay-in can only be reversed by a refund you request.