Dispersa con Bre-B

Dispersar pagos con Bre-B consiste en entregar dinero en tiempo real a las cuentas de tus usuarios usando únicamente una llave de pago del destinatario — sin necesidad de conocer su número de cuenta bancaria. Si ya cuentas con la llave de tus destinatarios (su cédula, celular, correo, alias o código de comercio), puedes usar tu cuenta de dispersión de Kushki para depositar directamente usando la lógica que prefieras.

Ideal para:

  • Pago a proveedores o vendedores
  • Marketplaces
  • Aplicaciones de delivery
  • Plataformas gig economy
  • Reembolsos y compensaciones

Detalles de operación

El proceso de dispersión con Bre-B en Colombia se realiza siguiendo el flujo: Comercio → Kushki → Banco de la República (SPBVI) → Banco Receptor → Beneficiario.

ParámetroDetalle
Monto mínimo por transacción1 COP
Monto máximo por transacciónEquivalente a 1.000 UVT (Unidades de Valor Tributario, actualizado anualmente por la DIAN). Cada entidad financiera o usuario puede establecer límites inferiores diarios o por transacción.
MonedaCOP
Bancos disponiblesTodos los bancos colombianos (reglamentación del Banco de la República)
Horario de procesamiento API24/7
Tiempo de acreditamientoTiempo real (máximo 30 segundos desde el inicio)
Tipo de respuestaAsíncrona — el resultado final llega por webhook o por consulta al endpoint Get Status

Nota importante: La API de Kushki recibe y procesa las peticiones 24/7. A diferencia de ACH, Bre-B no utiliza ciclos bancarios — el procesamiento es en tiempo real a través del SPBVI del Banco de la República. No es necesario consultar el listado de bancos antes de tokenizar.

Flujo

El flujo que integrarás es el siguiente:

Aquí aprenderás cómo integrar las dispersiones de dinero con Bre-B.

1. Tokeniza la información

El primer paso es obtener un token enviando la llave del destinatario a Kushki. A diferencia de ACH, no necesitas consultar el listado de bancos antes de este paso — todos los bancos colombianos soportan transferencias a llaves por reglamentación del Banco de la República.

Deberás realizar la petición del token utilizando nuestro endpoint de tokenización.

Campos obligatorios para obtener el token

Ten en cuenta los campos requeridos en el cuerpo de la petición de token para Bre-B:

Dispersión con llave Bre-B (accountType = KI, KP, KE, KA, KM)
- accountType
- accountNumber
- totalAmount
- currency

Los campos documentType, documentNumber, bankId y name son opcionales para Bre-B en Colombia.

Tipos de llave (accountType)

accountTypeTipo de llave
KINúmero de identificación
KPNúmero de celular
KECorreo electrónico
KAAlias alfanumérico
KMCódigo de comercio

Formatos válidos para accountNumber

El formato del accountNumber se valida antes de enviar cualquier solicitud a la red de pagos (fail-fast). Un formato inválido retorna HTTP 400 de inmediato, sin llegar a consultar la red.

accountTypeFormato requerido en accountNumber
KIAlfanumérico en mayúsculas. Mínimo 1 carácter. Sin espacios ni caracteres especiales.
KPExactamente 10 dígitos. Debe iniciar con 3, seguido de 9 dígitos.
KEEmail válido. Máximo 30 caracteres antes de @ y 61 después. Debe incluir dominio válido.
KADebe iniciar con @. Solo caracteres alfanuméricos en mayúsculas. Sin espacios ni caracteres especiales.
KMExactamente 10 dígitos. Debe iniciar con 00, seguido de 8 dígitos.
  • Javascript
  • Python
  • PHP
var request = require('request');
// Ejemplo con llave de celular (KP)
var options = {
'method': 'POST',
'url': 'https://api-uat.kushkipagos.com/payouts/transfer/v1/tokens', // Ambiente de prueba
'headers': {
'Public-Merchant-Id': '', // Reemplaza con tu Public Key
'Content-Type': 'application/json'
},
body: JSON.stringify({
"accountType": "KP",
"accountNumber": "3001234567",
"totalAmount": 1000,
"currency": "COP"
})
};
request(options, function (error, response) {
if (error) throw new Error(error);
var jsonData = JSON.parse(response.body);
console.log('Token:', jsonData.token);
// Muestra jsonData.keyResolution al usuario para confirmar el destinatario
console.log('Destinatario:', jsonData.keyResolution.ownerName);
});
import requests
import json
# Ejemplo con llave de celular (KP)
url = "https://api-uat.kushkipagos.com/payouts/transfer/v1/tokens" # Ambiente de prueba
payload = json.dumps({
"accountType": "KP",
"accountNumber": "3001234567",
"totalAmount": 1000,
"currency": "COP"
})
headers = {
'Public-Merchant-Id': '', # Reemplaza con tu Public Key
'Content-Type': 'application/json'
}
response = requests.request("POST", url, headers=headers, data=payload)
data = response.json()
print('Token:', data['token'])
# Muestra data['keyResolution'] al usuario para confirmar el destinatario
print('Destinatario:', data['keyResolution']['ownerName'])
$client = new http\Client;
$request = new http\Client\Request;
// Ejemplo con llave de celular (KP)
$request->setRequestUrl('https://api-uat.kushkipagos.com/payouts/transfer/v1/tokens'); // Ambiente de prueba
$request->setRequestMethod('POST');
$body = new http\Message\Body;
$body->append(json_encode([
"accountType" => "KP",
"accountNumber" => "3001234567",
"totalAmount" => 1000,
"currency" => "COP"
]));
$request->setBody($body);
$request->setOptions(array());
$request->setHeaders(array(
'Public-Merchant-Id' => '', // Reemplaza con tu Public Key
'Content-Type' => 'application/json'
));
$client->enqueue($request)->send();
$data = json_decode($client->getResponse()->getBody(), true);
echo 'Token: ' . $data['token'];
// Muestra $data['keyResolution'] al usuario para confirmar el destinatario
echo 'Destinatario: ' . $data['keyResolution']['ownerName'];

La respuesta incluye el token y el objeto keyResolution con los datos del destinatario resueltos por la red de pagos:

{
"token": "53de1cb6bbb54011a0a98053d48677e0",
"keyResolution": {
"keyValue": "3001234567",
"ownerName": "M**** G**** L****"
}
}
CampoDescripción
tokenToken de la transacción. Expira en 30 minutos. De un solo uso, independientemente del resultado.
keyResolution.keyValueValor exacto de la llave, sin transformaciones.
keyResolution.ownerNameNombre del titular, retornado enmascarado por la red de pagos (ej: M**** G**** L****).

2. Confirma el destinatario

Ya que has obtenido el token y el objeto keyResolution, debes mostrar al operador o usuario final la información del destinatario para confirmar que los datos son correctos antes de inicializar la transacción. Este paso es requerido por el Banco de la República.

Muestra al operador o usuario:

  • El valor de la llave: keyResolution.keyValue
  • El nombre del titular: keyResolution.ownerName (enmascarado, ej: M**** G**** L****)

3. Inicializa la transacción

Una vez confirmados los datos del destinatario, debes enviar el token obtenido e iniciar el proceso de dispersión con Kushki. Deberás realizar una llamada a nuestro endpoint de inicialización para iniciar el pago. Este paso descuenta el saldo inmediatamente de tu cuenta de dispersión.

  • Javascript
  • Python
  • PHP
var request = require('request');
var options = {
'method': 'POST',
'url': 'https://api-uat.kushkipagos.com/payouts/transfer/v1/init', // Ambiente de prueba
'headers': {
'Private-Merchant-Id': '', // Reemplaza con tu Private Key
'Content-Type': 'application/json'
},
body: JSON.stringify({
"token": "53de1cb6bbb54011a0a98053d48677e0", // Reemplaza con el token recibido
"amount": {
"subtotalIva": 0,
"subtotalIva0": 1000,
"iva": 0
},
"webhooks": [
{
"events": ["approvedTransaction", "declinedTransaction"],
"urls": ["https://tu-endpoint.com/webhook"]
}
]
})
};
request(options, function (error, response) {
if (error) throw new Error(error);
var jsonData = JSON.parse(response.body);
console.log('ticketNumber:', jsonData.ticketNumber);
console.log('status:', jsonData.status);
// Guarda el ticketNumber para consultar el estado más adelante
});
import requests
import json
url = "https://api-uat.kushkipagos.com/payouts/transfer/v1/init" # Ambiente de prueba
payload = json.dumps({
"token": "53de1cb6bbb54011a0a98053d48677e0", # Reemplaza con el token recibido
"amount": {
"subtotalIva": 0,
"subtotalIva0": 1000,
"iva": 0
},
"webhooks": [
{
"events": ["approvedTransaction", "declinedTransaction"],
"urls": ["https://tu-endpoint.com/webhook"]
}
]
})
headers = {
'Private-Merchant-Id': '', # Reemplaza con tu Private Key
'Content-Type': 'application/json'
}
response = requests.request("POST", url, headers=headers, data=payload)
data = response.json()
print('ticketNumber:', data['ticketNumber'])
print('status:', data['status'])
# Guarda el ticketNumber para consultar el estado más adelante
$client = new http\Client;
$request = new http\Client\Request;
$request->setRequestUrl('https://api-uat.kushkipagos.com/payouts/transfer/v1/init'); // Ambiente de prueba
$request->setRequestMethod('POST');
$body = new http\Message\Body;
$body->append(json_encode([
"token" => "53de1cb6bbb54011a0a98053d48677e0", // Reemplaza con el token recibido
"amount" => [
"subtotalIva" => 0,
"subtotalIva0" => 1000,
"iva" => 0
],
"webhooks" => [
[
"events" => ["approvedTransaction", "declinedTransaction"],
"urls" => ["https://tu-endpoint.com/webhook"]
]
]
]));
$request->setBody($body);
$request->setOptions(array());
$request->setHeaders(array(
'Private-Merchant-Id' => '', // Reemplaza con tu Private Key
'Content-Type' => 'application/json'
));
$client->enqueue($request)->send();
$data = json_decode($client->getResponse()->getBody(), true);
echo 'ticketNumber: ' . $data['ticketNumber'];
echo 'status: ' . $data['status'];
// Guarda el ticketNumber para consultar el estado más adelante

La respuesta entrega un número de ticket y el estado de la transacción:

{
"status": "INITIALIZED",
"ticketNumber": "1234567890",
"transactionReference": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}

4. Consulta el estado de la transacción

Ahora que la dispersión está en proceso, puedes conocer el estado de la transacción utilizando webhooks o consultando manualmente utilizando nuestra API.

Webhook

El estado de la transacción se notifica automáticamente en el momento en que la transferencia es aprobada o declinada. El payload incluye el objeto keyResolution con keyValue y ownerName.

API

Puedes utilizar nuestro endpoint Get Status para consultar manualmente el estado de una transacción específica.

  • Javascript
  • Python
  • PHP
var request = require('request');
var options = {
'method': 'GET',
'url': 'https://api-uat.kushkipagos.com/payouts/transfer/v1/transaction/{{ticketNumber}}', // Reemplaza {{ticketNumber}}
'headers': {
'Private-Merchant-Id': '' // Reemplaza con tu Private Key
}
};
request(options, function (error, response) {
if (error) throw new Error(error);
console.log(response.body);
});
import requests
ticket_number = "1234567890" # Reemplaza con el ticketNumber recibido
url = f"https://api-uat.kushkipagos.com/payouts/transfer/v1/transaction/{ticket_number}"
headers = {
'Private-Merchant-Id': '' # Reemplaza con tu Private Key
}
response = requests.request("GET", url, headers=headers)
print(response.text.encode('utf8'))
$client = new http\Client;
$request = new http\Client\Request;
$ticketNumber = "1234567890"; // Reemplaza con el ticketNumber recibido
$request->setRequestUrl("https://api-uat.kushkipagos.com/payouts/transfer/v1/transaction/{$ticketNumber}");
$request->setRequestMethod('GET');
$request->setOptions(array());
$request->setHeaders(array(
'Private-Merchant-Id' => '' // Reemplaza con tu Private Key
));
$client->enqueue($request)->send();
$response = $client->getResponse();
echo $response->getBody();

5. Prueba tu integración

El ambiente UAT de Bre-B opera sobre un sandbox de la red de pagos. Las respuestas son simuladas y replican el comportamiento productivo, pero no generan movimientos reales de dinero ni operaciones en la red.

Resolución exitosa de llave

Usa estos valores para obtener un token con keyResolution resuelto. Todos retornan una transacción aprobada.

accountTypeaccountNumber de pruebakeyResolution.ownerName simulado
KP3001234567JUAN PEREZ
KEUSUARIO@CORREO.COMMARIA LOPEZ
KA@COLOMBIAANA MARTINEZ
KM0012345678COMERCIO PRINCIPAL SAS
KICC12345678LUIS GOMEZ

Errores en tokenización (PT060)

Usa estos valores para simular una llave inválida o suspendida durante el paso de tokenización. Retornan PT060 antes de que se inicie la transacción.

accountTypeaccountNumber de pruebaEscenario simulado
KEBLOCKED@TEST.COMLlave suspendida
KM0011111111Llave suspendida por el participante
KP3000005001Error en la resolución de la llave
KIERRDICE9994Error inesperado durante la resolución

Simulación de escenarios de pago por monto

Para simular respuestas de error en el procesamiento de pago, envía el valor exacto indicado en el campo totalAmount del request de tokenización. Si el monto no coincide con ningún valor de la tabla, la transacción resultará aprobada.

Errores síncronos — llegan directamente en la respuesta del endpoint /init, sin necesidad de esperar el webhook:

totalAmountresponseCode KushkiDescripción
4001002Ha ocurrido un error inesperado
4002068Monto Inválido
5001073Transacción no permitida
5003074Banco o cuenta no inscrita

Errores asíncronos — llegan a través del webhook o al consultar Get Status, después de que el /init retorna INITIALIZED:

totalAmountresponseCode KushkiDescripción
6001076Error al efectuar el pago
4016045Fondos insuficientes
4017077Pendiente respuesta de otros bancos
4019077Pendiente respuesta de otros bancos
4020041Ocurrió un error en el sistema. Por favor, vuelve a intentarlo
9999002Ha ocurrido un error inesperado
4006080Cuenta destino inválida
4007052Número de cuenta inválido
4010088Cuenta no existe
4011091Cuenta con bloqueo transaccional
4012071Valor de traslado inválido
4013071Valor de traslado inválido

6. Prepara tu certificación

Toma en consideración las siguientes pautas para aprobar la certificación técnica (requerida para obtener credenciales productivas):

  • Los cálculos de los montos son correctos (subtotalIva, subtotalIva0, iva).
  • Mostrar mensajes en pantalla de acuerdo con las respuestas de Kushki.
  • Guardar y registrar todas las respuestas de Kushki (requeridas en caso de necesitar soporte).
  • Mostrar el objeto keyResolution al operador o usuario y requerir confirmación explícita antes de inicializar la transacción.
  • El procesador configurado en la Consola corresponde a Bre-B.
  • Si no se requiere enviar comprobante al correo del cliente, no se debe especificar el email en la solicitud del token.
  • Si se reciben notificaciones por webhook correctamente, responder a la solicitud con un status 200.
  • El botón de dispersión se deshabilita después del primer clic para evitar doble envío.
  • El logo de Kushki debe ser visible para el cliente. Puedes encontrar nuestro logo en varios formatos aquí.
  • Asegurarse de que en el cuerpo de la petición se envíen todos los campos requeridos en la referencia API.

Acepta webhooks

Maneja eventos post-dispersión de la manera correcta.

Códigos de error

Consulta los códigos de error para dispersiones con Bre-B.