If your users do not have a credit card or prefer to use the available balance in their bank accounts to make an online purchase, wire transfer is the ideal payment option.Transfer In lets your customers pay directly from their bank account — no card required. In Colombia 🇨🇴 there are two flows on the same set of endpoints: ACH, a secure redirect to the customer's bank through PSE (Pagos Seguros en Línea), and Bre-B, a real-time payment the customer authorizes by scanning a QR code. ACH is the default; Bre-B is opt-in per request.Due to our risk policies, the available payment methods and the integration type may vary once you complete the affiliation. We will tell you how to proceed if this process applies to your merchant.
Payment Flow#
A Transfer In payment in Colombia consists of 5 sequential steps: bank list retrieval, tokenization, initialization, bank redirect, and status confirmation.
Get the Bank List
Before requesting a token, your backend must call the
Get Bank List endpoint using your
Public Merchant ID to retrieve the available PSE banks.
⚠️ This step is required in Colombia. Unlike other countries, you must always call this endpoint and present the list to your customer so they can select their bank before proceeding.
Display the bank list to the customer and store the selected code — you will pass it as bankId in the token request.
Request a Transfer In Token
Your backend calls the token endpoint using your
Public Merchant ID. You must include the transaction amount, the customer's document data, the selected
bankId, and a
callbackUrl — the URL where the customer will land after completing the payment on the bank side.
Token rules: Tokens expire in 30 minutes and are single-use. If the transaction fails or the token expires, you must request a new one.
Required fields for Colombia:| Field | Description |
|---|
bankId | Bank code selected by the customer from the Bank List. Required in Colombia. |
amount | Object with subtotalIva, subtotalIva0, and iva |
callbackUrl | Redirect URL after bank confirmation |
userType | 0 = Natural Person · 1 = Company |
documentType | CC, NIT, CE, TI, or PP (see below) |
documentNumber | Customer's document number |
email | Customer's email |
currency | Always COP for Colombia |
Document types accepted in Colombia:| Value | Document |
|---|
CC | Cédula de Ciudadanía 🇨🇴 |
NIT | Número de Identificación Tributaria 🇨🇴 |
CE | Cédula de Extranjería 🇨 🇴 |
TI | Tarjeta de Identidad 🇨🇴 |
PP | Passport 🇨🇴 |
Init Transaction
Using your
Private Merchant ID, call the Init Transaction endpoint with the
token obtained in the previous step. Kushki validates the token and returns a
redirectUrl.
The redirectUrl is single use — redirect your customer to this URL immediately after receiving it. The customer will be taken to PSE's interface to authorize the transfer with their bank.Response fields for Colombia:| Field | Description |
|---|
redirectUrl | Single-use URL to redirect the customer to PSE |
trazabilityCode | Also called CUS — unique PSE payment reference code assigned by PSE. |
bankId | Code of the bank selected for the transaction |
bankName | Name of the bank selected for the transaction |
transactionReference | Unique reference for this transaction |
Customer Completes Payment at PSE
The customer is redirected to PSE and then to their bank's portal, where they authorize (or decline) the transfer. This step happens entirely on the PSE/bank side — no action is required from your backend.Once the customer finishes, PSE redirects them back to your callbackUrl.
Get Transaction Status
After the customer lands on your
callbackUrl, call the
Get Status endpoint using the original
token as a path parameter to confirm the final transaction result.
Possible statuses in Colombia:| Status | Meaning |
|---|
initializedTransaction | Transaction was created but not yet completed |
approvedTransaction | Transfer authorized — funds are on their way |
declinedTransaction | Transfer was rejected |
The response also includes the trazabilityCode (CUS) which you can use to reconcile the transaction with PSE records.
Amount Object#
The amount object is required in the Token request. Use the following structure depending on whether the transaction has taxes:{
"amount": {
"subtotalIva": 100000,
"subtotalIva0": 0,
"iva": 10000
}
}
Set subtotalIva to the taxable base amount and iva to the tax value. Set subtotalIva0 to 0. All amounts in COP.
Webhook Notifications#
You can receive real-time transaction notifications by including the webhooks object in your Init Transaction request. This is independent of any webhooks configured in the Kushki Console — both channels will fire simultaneously.{
"webhooks": [
{
"events": ["approvedTransaction", "declinedTransaction"],
"headers": [
{ "label": "Authorization", "value": "Bearer your-token" }
],
"urls": [
"https://merchant.example.com/webhooks/transfer-in"
]
}
]
}
🇨🇴 Bre-B — real-time QR payments#
Bre-B is Colombia's immediate low-value payment system, operated by the Banco de la República.
It is an alternative flow inside the same Transfer In endpoints: instead of collecting the
customer's bank details and redirecting them to their bank, Kushki returns a QR code that the
customer scans from their banking app to authorize the payment in real time.The ACH flow is unchanged — existing integrations need no modifications.Bre-B is available for Colombia only, and only on merchant IDs with the Bre-B processor
enabled. Contact your account executive to activate it.
ACH vs Bre-B#
| ACH (PSE) | Bre-B |
|---|
| How the customer pays | Redirected to their bank's site | Scans a QR in their banking app |
| Activation | Default — send no flowType | flowType: "BRE_B" in the token request |
| Bank list required | Yes | No |
Result of init | redirectUrl | qr (base64 PNG) |
| Validity window | Token: 30 minutes | QR: 10 minutes |
| Settlement | Deferred | Real time |
| Cancellable | No | Yes, while not in a final state |
| Webhook and error codes | — | Identical to ACH |
Required fields also differ: in Bre-B only amount, currency and flowType are required, so
bankId, callbackUrl, userType, documentType, documentNumber, paymentDescription and
email become optional.The flow, step by step#
1 · Request the token. Same endpoint as ACH, plus flowType. No bank list needed.{
"amount": { "subtotalIva0": 1000, "subtotalIva": 0, "iva": 0 },
"currency": "COP",
"flowType": "BRE_B"
}
The token request accepts two shapes — pick Bre-B in the request-body selector to see the
fields this flow actually requires: only amount, currency and flowType.2 · Generate the QR. Call Init Transaction with that token, repeating the same amount
you sent in step 1:{
"token": "A3pKwX200000yzQR9146362rJMCBSt7n",
"amount": { "subtotalIva0": 1000, "subtotalIva": 0, "iva": 0 }
}
The response carries qr instead of redirectUrl:{
"qr": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAZAAAAGQ...",
"transactionReference": "f2110170-8eec-4214-b2d0-38970d44f8e1"
}
Add "fullResponse": "v2" to the init request to also receive the details object.3 · Show the QR. The qr field is a complete Data URI, so on web it goes straight into an
img tag:In native apps, decode the base64 and render the bitmap with your platform's image component.4 · Wait for the result. The customer scans and authorizes; the outcome arrives
asynchronously. Configure a webhook, or poll Get Status with the same token. The QR expires in
10 minutes — after that, request a new token and init again.5 · Cancel if needed. While the transaction has no final status, Cancel Transaction
invalidates the active QR. It returns 204 No Content on success, or 400 with code T023 if the
transaction already reached a final state.Testing in UAT#
The sandbox picks a scenario from transaction_amount — the sum of every field in the amount
object (subtotalIva0 + subtotalIva + iva). To hit scenario 1000, send
subtotalIva0: 500, subtotalIva: 500, iva: 0, or subtotalIva0: 1000 on its own.These amounts are scenario identifiers in the sandbox. They carry no economic meaning — never use
them in production.
transaction_amount | HTTP | Result | Webhook |
|---|
1000 | 201 | Success | Fires — payment approved |
9999 | 201 | Success | Does not fire; the transaction stays initialized |
11000 | 500 | Error QR-CODE-0001 | Does not fire |
15000 | — | The request times out; the transaction stays initialized | Does not fire |
99999999999 | 400 | Error QR-CODE-0059 — amount out of range | Does not fire |
| Any other value | 201 | Success | Does not fire |
Certification checklist#
Before going live, confirm that:The amounts add up correctly across subtotalIva, subtotalIva0 and iva.
flowType is sent as "BRE_B" in the token request.
The QR from the qr field renders correctly for the customer.
Cancel Transaction is wired up for the cases where the customer abandons the payment.
On-screen messages reflect Kushki's responses.
Webhook notifications are answered with HTTP 200.
The pay button is disabled after the first click, to prevent double submission.
Every Kushki response is stored and logged — required for support.
The Kushki logo is visible.
Every required field is sent, per the API reference.