Guía de Integración
Esta guía recorre un escenario B2B real de principio a fin.
Escenario: ACME Corp envía por transferencia bancaria $10,000 USD a Inyo. Ese dinero se divide entre 10 sub-billeteras — una por proveedor — con $1,000 cada una. El proveedor 03 luego ejecuta una preautorización de $500 por una factura pendiente, captura $400 de ella (la factura llegó por un monto menor al esperado), reembolsa $50 al cliente y finalmente anula la retención restante.
Cada dólar permanece trazable hasta la transferencia bancaria original.
Para los ejemplos siguientes, exporta esto una sola vez:
export WALLET_HOST=https://{FQDN}
export TENANT=acme-corp
export API_KEY=your-tenant-api-key
Paso 1 — Crear las Billeteras
Necesitas 11 billeteras: una billetera de "tesorería" que recibe la transferencia y 10 billeteras de proveedores que reciben la división.
curl -sS -X POST "$WALLET_HOST/organizations/$TENANT/wallets" \
-H "X-API-KEY: $API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: acme-treasury-2026-07-14" \
-d '{
"owner_type": "company",
"owner_ref": "acme-corp",
"name": "ACME Treasury"
}'
Respuesta:
{
"id": "f64c49bd-47a3-4cc6-b9d4-265d1fe5ba65",
"name": "ACME Treasury",
"kind": "operational",
"ownerType": "company",
"ownerRef": "acme-corp",
"createdAt": "2026-07-14T04:01:33+00:00"
}
Repite para las 10 billeteras de proveedores con valores diferentes de owner_ref y name, y almacena los UUIDs devueltos. Más abajo, $VENDOR_03_ID se refiere a la billetera del proveedor 03.
Nombrar tu
Idempotency-Key: usa algo significativo y único por operación (p. ej.,acme-vendor-03-2026-07-14). Reutilizar la misma clave devuelve la respuesta original en lugar de crear un duplicado — es seguro reintentar ante fallas de red.
Paso 2 — Depositar y Dividir la Transferencia de $10k
El banco de ACME confirmó que la transferencia llegó a tu cuenta de liquidación. Regístrala en el libro contable y divídela entre los 10 proveedores en una sola operación atómica:
curl -sS -X POST "$WALLET_HOST/organizations/$TENANT/deposits" \
-H "X-API-KEY: $API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: wire-BANKREF-20260714-001" \
-d '{
"source": {
"source_type": "wire",
"external_ref": "BANK-WIRE-98765432",
"asset_code": "USD",
"amount": "10000.00",
"received_at": "2026-07-14T14:30:00Z",
"metadata": {
"originator": "ACME Corp",
"reference": "Q3 vendor payments"
}
},
"splits": [
{"wallet_id": "<VENDOR_01_ID>", "amount": "1000.00"},
{"wallet_id": "<VENDOR_02_ID>", "amount": "1000.00"},
{"wallet_id": "<VENDOR_03_ID>", "amount": "1000.00"},
{"wallet_id": "<VENDOR_04_ID>", "amount": "1000.00"},
{"wallet_id": "<VENDOR_05_ID>", "amount": "1000.00"},
{"wallet_id": "<VENDOR_06_ID>", "amount": "1000.00"},
{"wallet_id": "<VENDOR_07_ID>", "amount": "1000.00"},
{"wallet_id": "<VENDOR_08_ID>", "amount": "1000.00"},
{"wallet_id": "<VENDOR_09_ID>", "amount": "1000.00"},
{"wallet_id": "<VENDOR_10_ID>", "amount": "1000.00"}
]
}'
Respuesta (abreviada — la respuesta completa tiene los 11 movimientos):
{
"id": "419b03c7-7679-4bce-b3d9-d90f75418602",
"kind": "deposit",
"externalRef": "wire-BANKREF-20260714-001",
"metadata": {
"source_uuid": "268eda6f-ed57-4e8c-9a07-1b39f15ec817"
},
"entries": [
{ "assetCode": "USD", "direction": "debit", "amount": "10000.00000000", "lineageRef": null },
{ "assetCode": "USD", "direction": "credit", "amount": "1000.00000000", "lineageRef": [7] }
],
"createdAt": "2026-07-14T14:30:12+00:00"
}
Qué acaba de pasar:
- Se creó un registro de origen (source) que documenta la transferencia bancaria.
- Se escribió un journal de tipo
depositcon 11 movimientos: un débito de $10,000 contra la cuenta de origen externo ("dinero que entra desde fuera del libro contable") y 10 créditos de $1,000 contra las billeteras de los proveedores. - Cada movimiento de crédito lleva un
lineageRefque apunta al origen. - La suma de los débitos es igual a la suma de los créditos — se cumple el invariante de la partida doble.
El source.amount debe ser igual a la suma de las divisiones — de lo contrario, la API devuelve un error LEDGER_IMBALANCE.
Para mantener una porción de la transferencia en una cuenta suspense del sistema (comisiones de liquidación, flotante operativo), pasa
gateway_suspense_amounten el nivel superior. Entoncessum(splits) + gateway_suspense_amountdebe ser igual asource.amount.
Valores aceptados para source_type: wire, ach_debit, card, stablecoin_deposit.
Paso 3 — Verificar los Saldos
curl -sS "$WALLET_HOST/organizations/$TENANT/wallets/$VENDOR_03_ID/balance" \ -H "X-API-KEY: $API_KEY"
[
{
"assetCode": "USD",
"posted": "1000.00000000",
"held": "0.00000000",
"available": "1000.00000000",
"updatedAt": "2026-07-14T14:30:12+00:00"
}
]
El proveedor 03 tiene $1,000 disponibles. Si una billetera mantiene múltiples activos, filtra con ?asset=USD — esto devuelve un array de un solo elemento (o vacío si no hay saldo en ese activo).
Paso 4 — Preautorizar $500
El proveedor 03 tiene una factura por llegar. Reserva $500 antes de conocer el monto exacto para que el dinero no pueda gastarse en otra cosa:
curl -sS -X POST "$WALLET_HOST/organizations/$TENANT/holds" \
-H "X-API-KEY: $API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: preauth-invoice-INV-4521" \
-d "{
\"wallet_id\": \"$VENDOR_03_ID\",
\"asset_code\": \"USD\",
\"amount\": \"500.00\",
\"expires_at\": \"2026-07-21T14:30:00Z\"
}"
Respuesta:
{
"id": "19f60a03-7a09-4184-bf66-23ce30115377",
"assetCode": "USD",
"amount": "500.00",
"status": "open",
"expiresAt": "2026-07-21T14:30:00Z",
"resolvedAt": null,
"createdAt": "2026-07-14T15:00:00+00:00",
"captures": []
}
Guarda el id de la retención — capturarás/anularás contra él. El saldo ahora muestra:
[
{ "assetCode": "USD", "posted": "500.00000000", "held": "500.00000000", "available": "0.00000000" }
]
postedbajó de $1,000 a $500 — la preautorización debitó la billetera y acreditó una subcuenta de retención por billetera.heldmuestra la reserva de $500.availablees $0 — cada dólar restante está reservado.
expires_ates informativo. La retención registra la expiración, pero nada anula automáticamente las retenciones expiradas. Haz seguimiento de los IDs de retención y sus expiraciones por tu lado y anúlalas explícitamente cuando venzan.
Paso 5 — Capturar $400 de la Retención de $500
La factura llegó por $400. Captura ese monto hacia una cuenta de destino — normalmente una billetera de cuentas por pagar/liquidación que creaste previamente:
curl -sS -X POST "$WALLET_HOST/organizations/$TENANT/holds/$HOLD_ID/capture" \
-H "X-API-KEY: $API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: capture-INV-4521" \
-d "{
\"amount\": \"400.00\",
\"destination_account_id\": \"$PAYABLES_WALLET_ID\"
}"
Respuesta:
{
"id": "b092a884-8cc4-479d-80b5-995e668a8cdb",
"kind": "capture",
"metadata": {
"hold_uuid": "19f60a03-7a09-4184-bf66-23ce30115377",
"captured_amount": "400.00"
},
"entries": [
{ "assetCode": "USD", "direction": "debit", "amount": "400.00000000", "lineageRef": [7] },
{ "assetCode": "USD", "direction": "credit", "amount": "400.00000000", "lineageRef": [7] }
]
}
Guarda el
iddel journal (b092a884-...) — este es el UUID del journal de captura, requerido para reembolsar.
Saldo después de la captura: posted 500 / held 100 / available 400. Los $400 fueron liberados de la retención y movidos al destino; los $100 que estaban reservados por encima del monto de la factura permanecen retenidos.
Capturas parciales: puedes capturar múltiples veces contra la misma retención hasta que el monto acumulado sea igual al monto de la retención. El estado transiciona open → partially_captured → captured. Una vez completamente captured, no son posibles más capturas ni anulaciones — solo reembolsos.
Paso 6 — Reembolsar $50 de la Captura de $400
El cliente disputó parte de la factura. El reembolso usa el UUID del journal de captura del paso 5, no el ID de la retención:
curl -sS -X POST "$WALLET_HOST/organizations/$TENANT/captures/$CAPTURE_JOURNAL_UUID/refund" \
-H "X-API-KEY: $API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: refund-INV-4521-partial" \
-d '{ "amount": "50.00" }'
El reembolso debita el destino (billetera de cuentas por pagar) y acredita de vuelta a la billetera de origen (proveedor 03) — el libro contable consulta la retención original para encontrar la billetera de origen. El linaje se preserva a través de la cadena de reversión.
Saldo: posted 550 / held 100 / available 450.
Puedes reembolsar parcialmente múltiples veces hasta que el monto acumulado reembolsado sea igual al monto capturado. Una captura completamente reembolsada rechaza reembolsos adicionales.
Paso 7 — Anular la Retención Restante de $100
La factura está liquidada ($400 capturados, $50 reembolsados). Libera los $100 restantes:
curl -sS -X POST "$WALLET_HOST/organizations/$TENANT/holds/$HOLD_ID/void" \
-H "X-API-KEY: $API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: void-INV-4521-remainder" \
-d '{}'
Saldo: posted 650 / held 0 / available 650.
Posición final del proveedor 03: $1,000 recibidos − $400 capturados + $50 reembolsados = $650. Cada dólar trazable hasta la transferencia bancaria original.
Paso 8 — Transferir Entre Billeteras
Mueve $200 del proveedor 03 al proveedor 07 (misma moneda):
curl -sS -X POST "$WALLET_HOST/organizations/$TENANT/transfers" \
-H "X-API-KEY: $API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: transfer-v03-to-v07-20260714" \
-d "{
\"from_wallet_id\": \"$VENDOR_03_ID\",
\"to_wallet_id\": \"$VENDOR_07_ID\",
\"asset_code\": \"USD\",
\"amount\": \"200.00\"
}"
La respuesta es un journal kind: transfer con dos movimientos. Las transferencias son solo de la misma moneda — para conversiones entre activos, usa POST /fx-conversions con los campos explícitos rate y rate_source (ambos se devuelven en el metadata del journal para auditoría).
Lectura del Libro Contable
Lista cada débito y crédito de una billetera, del más reciente al más antiguo (paginado, 50 por página; filtra por asset, journal_kind y rango de fechas):
curl -sS "$WALLET_HOST/organizations/$TENANT/wallets/$VENDOR_03_ID/entries?asset=USD" \ -H "X-API-KEY: $API_KEY"
Cada movimiento incluye lineageRef — los IDs de origen que financiaron ese movimiento específico — lo que te permite rastrear cualquier débito hacia atrás para demostrar de dónde vino cada dólar.
Obtén una retención con su historial de capturas:
curl -sS "$WALLET_HOST/organizations/$TENANT/holds/$HOLD_ID" \ -H "X-API-KEY: $API_KEY"
No hay un endpoint de listado para las retenciones — almacena los IDs de retención (y sus expiraciones) en tu propio sistema a medida que las creas.
Ver la Referencia de la API para la tabla completa de endpoints, códigos de error y detalles de idempotencia.
