Pay-outs

A pay-out pays a merchant's QR code. Your user scans a QR in a shop or at an online checkout, your app sends the QR string to Borderli, and Borderli pays the merchant on their local rail. The merchant is paid in their own currency, exactly as if a local customer had paid. Your user is charged in the currency they hold.

Flow

  1. Resolve. POST /v1/resolve decodes the QR into a payment instruction: recipient, amount, currency, scheme and country. Send wallet_currency to get an FX quote in the same response.
  2. Confirm. Show your user the recipient and the amount they will pay. This step is yours.
  3. Execute. POST /v1/execute with the instruction id. You get 202 and a pending transaction.
  4. Outcome. A few seconds later Borderli sends a transaction.completed or transaction.failed webhook. You can also poll GET /v1/transactions/{id}.

If your user does not need a confirmation screen, POST /v1/pay does steps 1 and 3 in one request.

Resolve

curl -X POST https://apidocs.borderli.io/v1/resolve \
  -H "Content-Type: application/json" \
  -H "x-api-key: brly_test_key_001" \
  -d '{ "qr_string": "00020126...", "wallet_currency": "USD" }'
FieldMeaning
qr_stringThe QR content exactly as scanned. Do not trim or re-encode it.
wallet_currencyOptional. The currency your user holds. When it differs from the QR currency, the response includes fx with the rate and the converted amount.

The response is a PaymentInstruction. The fields you will use:

FieldMeaning
idPass it to /v1/execute.
amount, currencyWhat the QR asks for, in minor units. amount is null when the QR carries no amount. See open-amount QRs.
creditor.nameWho gets paid. Show it to your user.
fx.convertedAmountWhat your user pays, in wallet_currency, in minor units.
fx.quoteExpiresAtExecute before this time or the rate is gone.
expiresAtThe instruction lives 30 minutes. After that, resolve again.
confidenceHow sure the decoder is, from 0 to 1. Below 0.8, ask your user to check the details.

Execute

curl -X POST https://apidocs.borderli.io/v1/execute \
  -H "Content-Type: application/json" \
  -H "x-api-key: brly_test_key_001" \
  -H "Idempotency-Key: order-12345" \
  -d '{
    "instruction_id": "ins_01J...",
    "payer_name": "Maria Silva",
    "payer_account": "0001234567",
    "payer_tax_id": "12345678909"
  }'
FieldMeaning
instruction_idFrom resolve.
payer_name, payer_account, payer_tax_idWho is paying. The rail requires all three. The tax id is the CPF in Brazil, the CUIT in Argentina, and so on.
amountOnly for a QR that carries no amount. Minor units.
callback_urlOptional. Borderli sends the outcome webhook to this URL as well. See Webhooks.

The response is 202 with the transaction in pending. Nothing is final yet.

Pay in one call

POST /v1/pay takes the resolve fields and the execute fields together: qr_string, optional wallet_currency, the three payer_* fields, optional amount and callback_url. It returns the same 202 pending transaction. It also needs an Idempotency-Key.

Open-amount QRs

Some QRs carry no amount. The payer types it in. Resolve returns amount: null for these. Ask your user for the amount and send it as amount in execute or pay. If you forget, you get 422 AmountRequired.

Statuses

statusMeaningDetail field
pendingAccepted, not settled yet.
completedThe merchant was paid.result: amount, charged, exchangeRate, settledAt
failedThe rail refused the payment.error.reason and error.code
canceledThe payment never happened. cancellation.reason is expired when the rail's preview lapsed, or declined when the payer's side rejected it.cancellation

error.reason is one of:

error.code is the bank or scheme code when there is one, such as ACCOUNT_BLOCKED.

Coverage

CountryRailCurrency
ArgentinaTransferencias 3.0ARS
BoliviaQR BoliviaBOB
BrazilPixBRL
ColombiaInteroperable QRCOP
PeruInteroperable QRPEN

All of these settle in real time. The scheme and country fields on the instruction tell you which rail a QR belongs to. See Supported Schemes for the identifiers.