# 1. Resolver llave
RECIPIENT=$(curl -s -X POST https://consola.mouvlatam.com/api/transfers/resolve-key \
-H "Authorization: Bearer mvk_..." \
-H "Content-Type: application/json" \
-d '{ "keyValue": "3115551234" }')
# 2. Cotizar
QUOTE=$(curl -s -X POST https://consola.mouvlatam.com/api/transfers/quote \
-H "Authorization: Bearer mvk_..." \
-H "Content-Type: application/json" \
-d '{ "amount": 100000, "keyValue": "3115551234" }')
# 3. Enviar
curl -X POST https://consola.mouvlatam.com/api/transfers/send \
-H "Authorization: Bearer mvk_..." \
-H "Content-Type: application/json" \
-d "$(jq -n \
--arg name "$(echo $RECIPIENT | jq -r .recipient.fullName)" \
--arg doc "$(echo $RECIPIENT | jq -r .recipient.idValue)" \
--arg keyType "$(echo $RECIPIENT | jq -r .keyType)" \
'{
amount: 100000,
destination: { brebKey: { type: $keyType, value: "3115551234" } },
targetName: $name,
targetDocument: $doc,
reference: "factura-001"
}')"
// Flujo end-to-end BREB
async function sendBreb(keyValue, amount, reference) {
const headers = {
'Authorization': `Bearer ${process.env.MOUV_API_KEY}`,
'Content-Type': 'application/json',
};
// 1. Resolver
const resolve = await fetch('https://consola.mouvlatam.com/api/transfers/resolve-key', {
method: 'POST', headers, body: JSON.stringify({ keyValue }),
}).then(r => r.json());
if (!resolve.found) throw new Error('Llave no encontrada');
// 2. Cotizar
const quote = await fetch('https://consola.mouvlatam.com/api/transfers/quote', {
method: 'POST', headers, body: JSON.stringify({ amount, keyValue }),
}).then(r => r.json());
if (!quote.canAfford) throw new Error(`Saldo insuficiente: ${quote.message}`);
// 3. Enviar
const send = await fetch('https://consola.mouvlatam.com/api/transfers/send', {
method: 'POST', headers, body: JSON.stringify({
amount,
destination: { brebKey: { type: resolve.keyType, value: keyValue } },
targetName: resolve.recipient.fullName,
targetDocument: resolve.recipient.idValue,
reference,
}),
}).then(r => r.json());
return send;
}
{
"id": "8e39f393-...-uuid",
"status": "PENDING",
"amount": 100000,
"totalFee": 1100,
"ivaAmount": 209,
"netAmount": 98900,
"currency": "COP",
"rail": "BREB",
"targetName": "JUAN PEREZ",
"targetDocument": "1018485810",
"reference": "factura-001",
"createdAt": "2026-05-25T20:00:00.000Z"
}
{
"id": "8e39f393-...",
"status": "AWAITING_APPROVAL",
"approvalId": "appr_...",
"threshold": 2,
"expiresAt": "2026-05-26T20:00:00.000Z",
"amount": 100000,
"rail": "BREB"
}
Retiros (Outflows)
POST /transfers/send
Ejecutar retiro BREB o ACH
POST
/
api
/
transfers
/
send
# 1. Resolver llave
RECIPIENT=$(curl -s -X POST https://consola.mouvlatam.com/api/transfers/resolve-key \
-H "Authorization: Bearer mvk_..." \
-H "Content-Type: application/json" \
-d '{ "keyValue": "3115551234" }')
# 2. Cotizar
QUOTE=$(curl -s -X POST https://consola.mouvlatam.com/api/transfers/quote \
-H "Authorization: Bearer mvk_..." \
-H "Content-Type: application/json" \
-d '{ "amount": 100000, "keyValue": "3115551234" }')
# 3. Enviar
curl -X POST https://consola.mouvlatam.com/api/transfers/send \
-H "Authorization: Bearer mvk_..." \
-H "Content-Type: application/json" \
-d "$(jq -n \
--arg name "$(echo $RECIPIENT | jq -r .recipient.fullName)" \
--arg doc "$(echo $RECIPIENT | jq -r .recipient.idValue)" \
--arg keyType "$(echo $RECIPIENT | jq -r .keyType)" \
'{
amount: 100000,
destination: { brebKey: { type: $keyType, value: "3115551234" } },
targetName: $name,
targetDocument: $doc,
reference: "factura-001"
}')"
// Flujo end-to-end BREB
async function sendBreb(keyValue, amount, reference) {
const headers = {
'Authorization': `Bearer ${process.env.MOUV_API_KEY}`,
'Content-Type': 'application/json',
};
// 1. Resolver
const resolve = await fetch('https://consola.mouvlatam.com/api/transfers/resolve-key', {
method: 'POST', headers, body: JSON.stringify({ keyValue }),
}).then(r => r.json());
if (!resolve.found) throw new Error('Llave no encontrada');
// 2. Cotizar
const quote = await fetch('https://consola.mouvlatam.com/api/transfers/quote', {
method: 'POST', headers, body: JSON.stringify({ amount, keyValue }),
}).then(r => r.json());
if (!quote.canAfford) throw new Error(`Saldo insuficiente: ${quote.message}`);
// 3. Enviar
const send = await fetch('https://consola.mouvlatam.com/api/transfers/send', {
method: 'POST', headers, body: JSON.stringify({
amount,
destination: { brebKey: { type: resolve.keyType, value: keyValue } },
targetName: resolve.recipient.fullName,
targetDocument: resolve.recipient.idValue,
reference,
}),
}).then(r => r.json());
return send;
}
{
"id": "8e39f393-...-uuid",
"status": "PENDING",
"amount": 100000,
"totalFee": 1100,
"ivaAmount": 209,
"netAmount": 98900,
"currency": "COP",
"rail": "BREB",
"targetName": "JUAN PEREZ",
"targetDocument": "1018485810",
"reference": "factura-001",
"createdAt": "2026-05-25T20:00:00.000Z"
}
{
"id": "8e39f393-...",
"status": "AWAITING_APPROVAL",
"approvalId": "appr_...",
"threshold": 2,
"expiresAt": "2026-05-26T20:00:00.000Z",
"amount": 100000,
"rail": "BREB"
}
Ejecuta un retiro. Un único endpoint maneja ambos rails (BREB + ACH) — el sistema discrimina por la forma del campo
Pollea el estado vía
destination:
destination.brebKey→ BREB (debita wallet BREB, dispatcha a red Bre-B)destination.bankAccount→ ACH (debita wallet ACH, dispatcha a banco destino)
WRITE
Body común
number
required
Monto BRUTO en centavos.
object
required
Una de:
brebKey o bankAccount (mutuamente exclusivos).string
Opcional. Tu identificador interno (max 100 chars). Actúa como idempotency key dentro de 60s.
string
Opcional. Memo human-readable (max 255 chars).
Body BREB
string
required
PHONE, EMAIL, ALPHANUM, o NRIC. Usá el keyType que retornó /transfers/resolve-key.string
required
El valor de la llave.
string
required
Nombre completo del titular. Sacalo de
/transfers/resolve-key → recipient.fullName.string
required
Número de documento del titular. Sacalo de
/transfers/resolve-key → recipient.idValue. SARLAFT obligatorio.Body ACH
number
required
ID del banco. Consultalo en
GET /dictionaries/banks/ach.number
required
20 = Ahorros, 21 = Corriente, 22 = Depósito electrónico.string
required
4-32 dígitos.
number
required
1 = CC, 4 = CE, 5 = Pasaporte, 8 = NIT.string
required
5-15 dígitos.
string
required
Nombres del titular (max 64 chars).
string
required
Apellidos del titular (max 64 chars).
Response 201
string
UUID de la transacción
string
PENDING (en flight), COMPLETED (confirmado), FAILED, o AWAITING_APPROVAL (multi-sig)number
Monto bruto (centavos)
number
Comisión cobrada (centavos)
number
IVA cobrado (centavos)
number
amount - totalFee (lo que recibe el destinatario)string
"COP"string
"BREB" o "ACH" — el rail que se dispatchóstring
Titular destino
string
Documento destino
string
Tu reference (si lo proveíste)
string
ISO 8601 timestamp
Ejemplos
Retiro BREB
# 1. Resolver llave
RECIPIENT=$(curl -s -X POST https://consola.mouvlatam.com/api/transfers/resolve-key \
-H "Authorization: Bearer mvk_..." \
-H "Content-Type: application/json" \
-d '{ "keyValue": "3115551234" }')
# 2. Cotizar
QUOTE=$(curl -s -X POST https://consola.mouvlatam.com/api/transfers/quote \
-H "Authorization: Bearer mvk_..." \
-H "Content-Type: application/json" \
-d '{ "amount": 100000, "keyValue": "3115551234" }')
# 3. Enviar
curl -X POST https://consola.mouvlatam.com/api/transfers/send \
-H "Authorization: Bearer mvk_..." \
-H "Content-Type: application/json" \
-d "$(jq -n \
--arg name "$(echo $RECIPIENT | jq -r .recipient.fullName)" \
--arg doc "$(echo $RECIPIENT | jq -r .recipient.idValue)" \
--arg keyType "$(echo $RECIPIENT | jq -r .keyType)" \
'{
amount: 100000,
destination: { brebKey: { type: $keyType, value: "3115551234" } },
targetName: $name,
targetDocument: $doc,
reference: "factura-001"
}')"
// Flujo end-to-end BREB
async function sendBreb(keyValue, amount, reference) {
const headers = {
'Authorization': `Bearer ${process.env.MOUV_API_KEY}`,
'Content-Type': 'application/json',
};
// 1. Resolver
const resolve = await fetch('https://consola.mouvlatam.com/api/transfers/resolve-key', {
method: 'POST', headers, body: JSON.stringify({ keyValue }),
}).then(r => r.json());
if (!resolve.found) throw new Error('Llave no encontrada');
// 2. Cotizar
const quote = await fetch('https://consola.mouvlatam.com/api/transfers/quote', {
method: 'POST', headers, body: JSON.stringify({ amount, keyValue }),
}).then(r => r.json());
if (!quote.canAfford) throw new Error(`Saldo insuficiente: ${quote.message}`);
// 3. Enviar
const send = await fetch('https://consola.mouvlatam.com/api/transfers/send', {
method: 'POST', headers, body: JSON.stringify({
amount,
destination: { brebKey: { type: resolve.keyType, value: keyValue } },
targetName: resolve.recipient.fullName,
targetDocument: resolve.recipient.idValue,
reference,
}),
}).then(r => r.json());
return send;
}
Retiro ACH
curl -X POST https://consola.mouvlatam.com/api/transfers/send \
-H "Authorization: Bearer mvk_..." \
-H "Content-Type: application/json" \
-d '{
"amount": 5000000,
"destination": {
"bankAccount": {
"bankId": 27,
"accountTypeId": 20,
"accountNumber": "59455583201",
"documentTypeId": 1,
"documentNumber": "1018485810",
"holderFirstName": "Juan",
"holderLastName": "Pérez"
}
},
"reference": "nomina-mayo-2026"
}'
{
"id": "8e39f393-...-uuid",
"status": "PENDING",
"amount": 100000,
"totalFee": 1100,
"ivaAmount": 209,
"netAmount": 98900,
"currency": "COP",
"rail": "BREB",
"targetName": "JUAN PEREZ",
"targetDocument": "1018485810",
"reference": "factura-001",
"createdAt": "2026-05-25T20:00:00.000Z"
}
{
"id": "8e39f393-...",
"status": "AWAITING_APPROVAL",
"approvalId": "appr_...",
"threshold": 2,
"expiresAt": "2026-05-26T20:00:00.000Z",
"amount": 100000,
"rail": "BREB"
}
Status lifecycle
| Status | Significado |
|---|---|
PENDING | Reservado en wallet, esperando confirmación del rail |
AWAITING_APPROVAL | Multi-sig activo (threshold≥2), espera firmas |
EXECUTING | En ejecución (multi-sig quorum alcanzado) |
COMPLETED | Confirmado, irreversible |
FAILED | Falló, fondos devueltos al wallet |
EXPIRED | TTL 24h agotado sin quorum (multi-sig) |
GET /wallets/transactions/:id o esperá webhooks salientes (próximamente).
Idempotencia
El camporeference actúa como idempotency key dentro de 60s. Reenviá la misma request con el mismo reference para safely retry tras 5xx.
Defensa SARLAFT
targetDocumentes obligatorio para BREB (no se persiste null en la transacción)- Mouv valida el documento del cliente contra blacklist AML antes de dispatchar
- Counterparty limits (tope diario/mensual por tercero) se verifican antes del send
- Transactions quedan persistidas con
apiKeyIdpara audit trail