Sandbox
Datos de prueba
Los documentos, montos, ids y casos de error deterministas del sandbox, módulo por módulo: afiliados, créditos y subsidios (1000000001–1000000004 y 9999999999, umbral de 50.000.000), cuota monetaria, billetera, cartera, salud, administrativo, PQRS y webhooks.
En esta página
Todo en el sandbox es determinista: el resultado lo decide el documento que envías, algunos montos y los ids que te devuelve el propio sandbox. Esta página reúne esos valores para que escribas pruebas automáticas que cubran el camino feliz y cada error documentado. Recuerda que el sandbox no guarda estado: el estado viaja en los ids y una escritura no cambia lo que devuelven las lecturas siguientes (Sandbox).
Afiliados, créditos y subsidios #
Cinco documentos (tipo CC) con un perfil fijo. El perfil decide el resultado de cada paso, sin
importar lo que envíes en la decisión.
| Documento | Perfil | Consulta de afiliado¹ | Al radicar | Al decidir | Al desembolsar |
|---|---|---|---|---|---|
1000000001 | Elegible | No simulada | 201 RECEIVED | APPROVED | 200 DISBURSED |
1000000002 | Inelegible | No simulada | 201 REJECTED (NOT_ELIGIBLE) | REJECTED (NOT_ELIGIBLE) | 409 (no desembolsable) |
1000000003 | Revisión manual | No simulada | 201 RECEIVED | IN_REVIEW (MANUAL_REVIEW) | 409 |
1000000004 | Con deudas | No simulada | 201 REJECTED (OUTSTANDING_DEBT) | REJECTED (OUTSTANDING_DEBT) | 409 |
9999999999 | No existe | No simulada | 404 | — | — |
| Cualquier otro | Elegible | — | Como 1000000001 | Como 1000000001 | Como 1000000001 |
¹ La consulta, el cruce y la carga de afiliados (affiliates.get, cross, upload) no se
simulan: con cualquier llave leen y escriben el registro de afiliados real del tenant. Para probar
la consulta, carga antes tus afiliados de prueba con affiliates.cross; un documento que no esté
en tu registro responde 404.
Umbral de monto: 50.000.000. Una solicitud del perfil elegible por más de 50.000.000 queda
REJECTED con AMOUNT_EXCEEDS_LIMIT desde la radicación, y sigue rechazada (y no desembolsable) en
la decisión, la consulta y el desembolso. Úsalo para tu caso de “monto demasiado alto”.
Cómo leer los ids:
- La solicitud recibe un id
sbx-<código>-<hash>. El código es la letra del perfil (Eelegible,Iinelegible,Mrevisión manual,Dcon deudas) más unaXsi superó el umbral (sbx-EX-…). - El mismo tenant y la misma
external_referenceproducen siempre el mismo id (sinexternal_reference, cuenta el documento). - La consulta de una solicitud (
GET) responde el estado decidido (APPROVED,REJECTED,IN_REVIEW), aunque la radicación haya respondidoRECEIVED. - El desembolso de una solicitud aprobada devuelve un
disbursement_iddsb-…. - Las bandejas de créditos y de subsidios listan 4 solicitudes, una por cada documento de
1000000001a1000000004(el inexistente no aparece), y aplican los filtrosstatus,search(documento o nombre) y la paginación.
Los subsidios siguen exactamente las mismas reglas, para cualquier tipo de subsidio de la ruta.
Un test por perfil #
import os
from compense_gateway import CompenseClient, NotFoundError
client = CompenseClient(api_key=os.environ["COMPENSE_API_KEY"], environment="sandbox")
ESPERADO = {"1000000001": "RECEIVED", "1000000002": "REJECTED",
"1000000003": "RECEIVED", "1000000004": "REJECTED"}
for doc, estado in ESPERADO.items():
tx = client.credits.submit_application(
affiliate={"document_type": "CC", "document_number": doc},
product_code="LIBRANZA_12M", amount="3500000", term_months=12,
external_reference=f"HORIZONTE-TEST-{doc}",
)
assert tx.data["status"] == estado, (doc, tx.data)
try:
client.affiliates.get("9999999999")
except NotFoundError:
print("9999999999 no existe, como se esperaba")import { CompenseClient, NotFoundError } from "@compense/sdk";
const client = new CompenseClient({ apiKey: process.env.COMPENSE_API_KEY!, environment: "sandbox" });
const esperado: Record<string, string> = {
"1000000001": "RECEIVED", "1000000002": "REJECTED",
"1000000003": "RECEIVED", "1000000004": "REJECTED",
};
for (const [doc, estado] of Object.entries(esperado)) {
const tx = await client.credits.submitApplication({
affiliate: { document_type: "CC", document_number: doc },
productCode: "LIBRANZA_12M", amount: "3500000", termMonths: 12,
externalReference: `HORIZONTE-TEST-${doc}`,
});
if (tx.data.status !== estado) throw new Error(`${doc}: ${JSON.stringify(tx.data)}`);
}
try {
await client.affiliates.get("9999999999");
} catch (err) {
if (!(err instanceof NotFoundError)) throw err;
console.log("9999999999 no existe, como se esperaba");
}<?php
require __DIR__ . '/vendor/autoload.php';
use Compense\Sdk\CompenseClient;
use Compense\Sdk\Exception\NotFoundException;
use Compense\Sdk\Model\AffiliateRef;
$client = new CompenseClient(apiKey: getenv('COMPENSE_API_KEY'), environment: 'sandbox');
$esperado = ['1000000001' => 'RECEIVED', '1000000002' => 'REJECTED',
'1000000003' => 'RECEIVED', '1000000004' => 'REJECTED'];
foreach ($esperado as $doc => $estado) {
$tx = $client->credits->submitApplication(
affiliate: new AffiliateRef('CC', (string) $doc),
productCode: 'LIBRANZA_12M', amount: '3500000', termMonths: 12,
externalReference: "HORIZONTE-TEST-{$doc}",
);
assert($tx->data['status'] === $estado);
}
try {
$client->affiliates->get('9999999999');
} catch (NotFoundException) {
echo "9999999999 no existe, como se esperaba\n";
}using Compense.Sdk;
using Compense.Sdk.Models;
using var client = new CompenseClient(
Environment.GetEnvironmentVariable("COMPENSE_API_KEY")!, CompenseEnvironment.Sandbox);
var esperado = new Dictionary<string, string>
{
["1000000001"] = "RECEIVED", ["1000000002"] = "REJECTED",
["1000000003"] = "RECEIVED", ["1000000004"] = "REJECTED",
};
foreach (var (doc, estado) in esperado)
{
Transaction tx = await client.Credits.SubmitApplicationAsync(
affiliate: new AffiliateRef("CC", doc),
productCode: "LIBRANZA_12M", amount: "3500000", termMonths: 12,
externalReference: $"HORIZONTE-TEST-{doc}");
if (tx.Data["status"]?.GetValue<string>() != estado) throw new Exception($"{doc}: {tx.Data}");
}
try
{
await client.Affiliates.GetAsync("9999999999");
}
catch (NotFoundException)
{
Console.WriteLine("9999999999 no existe, como se esperaba");
}esperado := map[string]string{
"1000000001": "RECEIVED", "1000000002": "REJECTED",
"1000000003": "RECEIVED", "1000000004": "REJECTED",
}
for doc, estado := range esperado {
tx, err := client.Credits.SubmitApplication(ctx, &compense.CreditApplicationParams{
Affiliate: compense.AffiliateRef{DocumentType: "CC", DocumentNumber: doc},
ProductCode: "LIBRANZA_12M",
Amount: "3500000",
TermMonths: 12,
ExternalReference: "HORIZONTE-TEST-" + doc,
})
if err != nil {
log.Fatal(err)
}
if tx.Data["status"] != estado {
log.Fatalf("%s: %v", doc, tx.Data)
}
}
if _, err := client.Affiliates.Get(ctx, "9999999999"); compense.IsNotFound(err) {
fmt.Println("9999999999 no existe, como se esperaba")
}for DOC in 1000000001 1000000002 1000000003 1000000004; do
curl -s -X POST https://sandbox.dev.compense.co/gw/v1/gateway/credits/application/ \
-H "X-API-Key: $COMPENSE_API_KEY" \
-H "Idempotency-Key: horizonte-test-$DOC" \
-H "Content-Type: application/json" \
-d "{\"afiliado\": {\"document_type\": \"CC\", \"document_number\": \"$DOC\"},
\"product_code\": \"LIBRANZA_12M\", \"amount\": \"3500000\", \"term_months\": 12,
\"external_reference\": \"HORIZONTE-TEST-$DOC\"}" | jq -r --arg d "$DOC" '"\($d) \(.data.status)"'
doneCuota monetaria #
| Documento | Al generar | Al pagar la cuota generada |
|---|---|---|
1000000001 (o cualquier otro) | 201 GENERATED, eligible: true, monto 50.000 | 200 DISBURSED |
1000000003 | 201 GENERATED, eligible: true, monto 50.000 | 409 (el perfil queda en revisión) |
1000000002 | 201 NOT_ELIGIBLE (NOT_ELIGIBLE), monto 0 | 409 |
1000000004 | 201 NOT_ELIGIBLE (OUTSTANDING_DEBT), monto 0 | 409 |
9999999999 | 404 | — |
- La cuota recibe un
disbursement_idmad-<código>-<hash>; el mismo afiliado y período dan el mismo id. - Sin
period, se usa el mes en curso (AAAA-MM). - El listado devuelve una sola fila
GENERATEDde 50.000 para el período.
Billetera #
Saldo, P2P, Bre-B, facturas y recargas #
Moneda: COP. El saldo disponible depende del perfil del titular (el document de la petición):
| Documento del titular | Saldo | Comportamiento |
|---|---|---|
1000000001 | 500.000 | Todo funciona. |
1000000003 | 500.000 | Todo funciona. |
1000000002 | 0 | 422 INSUFFICIENT_FUNDS en cualquier envío. |
1000000004 | 100 | 422 INSUFFICIENT_FUNDS en casi cualquier envío. |
9999999999 | — | 404 MEMBER_NOT_FOUND. |
| Cualquier otro | 500.000 | Como 1000000001. |
| Caso | Valor de prueba | Resultado |
|---|---|---|
| Resolver alias P2P o llave Bre-B inexistente | 9999999999 | 200 con {"found": false} |
| P2P a uno mismo | alias igual al document del pagador | 422 SELF_TRANSFER |
| Enviar sin resolver antes | sin resolution_token | 400 RESOLUTION_REQUIRED |
| Token de otro flujo | token de Bre-B en P2P o al revés | 400 RESOLUTION_INVALID |
| Rechazo del banco destino (Bre-B) | monto 11111 | 422 TRANSFER_REJECTED (reason_code AC04) |
| Monto cero o negativo | 0 | 422 INVALID_AMOUNT |
| Factura ya pagada | referencia que termina en 0 | 409 BILL_ALREADY_PAID |
| Convenio desconocido | otro biller_code | 404 BILLER_NOT_FOUND |
| Pagar un monto distinto al consultado | cualquier otro monto | 422 AMOUNT_MISMATCH |
| Celular inválido | que no tenga 10 dígitos o no empiece por 3 | 400 INVALID_PHONE |
| Operador desconocido | fuera de la lista | 404 OPERATOR_NOT_FOUND |
| Recarga fuera de rango | menos de 1.000 o más de 200.000 | 400 INVALID_AMOUNT |
- Convenios simulados:
EPM(energía),ACUEDUCTO(agua),VANTI(gas) yETB(internet). El valor de cada factura está entre 50.000 y 95.000 según la referencia, vence el 2026-11-15 y es del período2026-10: consúltala antes de pagarla para conocer el monto exacto. - Operadores de recarga:
CLARO,MOVISTAR,TIGOyWOM, de 1.000 a 200.000. - Movimientos: 3 fijos por titular (1, 5 y 10 de octubre de 2026: un abono de subsidio de
150.000, una transferencia de 20.000 y una recarga de 10.000), paginables y filtrables por fecha;
su
idsirve para consultar el detalle de la transacción. - Los ids de transacción son
wtx-…y se repiten con la misma petición.
Bolsillos, tarjetas y FX #
| Caso | Resultado |
|---|---|
| Listar cuentas | Una billetera principal (con el saldo del perfil) y un bolsillo en 0. |
| Moneda por defecto | DOP si no envías currency. |
Emitir tarjeta a 1000000002 | 422 NOT_ELIGIBLE. |
| Cambiar estado de tarjeta | ACTIVE, FROZEN o BLOCKED; otro valor → 422 INVALID_STATUS. |
| Movimientos de una tarjeta | Una compra (PURCHASE) de 1.000, SETTLED. |
| Cotizar o convertir FX | Tasas fijas: DOP→USD 0,017 · USD→DOP 58,5 · DOP→COP 68,0 · COP→DOP 0,0147. Otro par → 422 UNSUPPORTED_CURRENCY_PAIR. |
| Convertir más que el saldo | 422 INSUFFICIENT_FUNDS. |
Cartera #
| Documento | Préstamo simulado |
|---|---|
1000000001 | 1 préstamo ACTIVE, al día (cuotas 1 a 3 pagadas). |
1000000003 | 1 préstamo IN_ARREARS: cuota 4 en mora de 12 días. |
1000000004 | 1 préstamo DEFAULTED: cuotas 3 a 5 en mora (95 días). |
1000000002 | Sin préstamos (lista vacía). |
9999999999 | 404 (el afiliado no existe). |
- Todos los préstamos de prueba son de 5.000.000 a 12 cuotas, tasa del 1,8 % mensual, con la primera cuota el 15 de julio de 2026 (fechas fijas).
- El
loan_id(lna-…) sale de listar los préstamos del documento. - Los recaudos validan como el motor:
channelenCASH,ACH_LIBRANZA,BANK_FILEoMANUAL(también acepta alias comoEFECTIVO,LIBRANZA,TRANSFERENCIAoCHEQUE; otro valor →422 INVALID_CHANNEL),referenceobligatoria (422 REFERENCE_REQUIRED), monto positivo (422 INVALID_AMOUNT) y no mayor que la deuda (422 AMOUNT_EXCEEDS_DEBT), monto del pago total igual a la cotización (422 PAYOFF_AMOUNT_MISMATCH) ymodedel abono enreduce_termoreduce_installment(422 INVALID_MODE). - La misma
referencedevuelve el mismoreceipt_id, yreplayedsiempre esfalse. Registrar un pago no cambia lo que devuelven las lecturas.
Salud #
| Caso | Valor de prueba | Resultado |
|---|---|---|
| Profesionales | D1 (medicina general, Sede Sandbox Centro) y D2 (pediatría, Sede Sandbox Norte) | En el catálogo y la disponibilidad |
| Cupos | slt-<médico>-<AAAAMMDD>-<HHMM>, horas 08:00, 08:20, 08:40, 10:00, 10:20 y 10:40 | La disponibilidad no lista los de minuto 40 |
| Cupo ya tomado | minuto 40 (p. ej. slt-D1-20261103-0840) | 409 SLOT_NOT_AVAILABLE al agendar o reprogramar |
| Disponibilidad en fecha pasada | ayer o antes | 400 |
| Paciente inexistente | documento 9999999999 | 404 HEALTH_PATIENT_NOT_FOUND; con new_patient se “registra” |
| Cancelar sin motivo | reason de menos de 5 caracteres | 422 HEALTH_REASON_REQUIRED |
| Asistencia | completed / no_show | Cita futura → 422 HEALTH_STATUS_NOT_ALLOWED; pasada → 200 |
| Facturas | inv-R-120000-… (RADICADA, $120.000) e inv-C-53500-… (CUV_OK, $53.500) | Las dos facturas de prueba |
| Pago del pagador | medio TRANSFERENCIA, CONSIGNACION, GIRO_DIRECTO o RETENCION | Otro medio → 422 HEALTH_PAYMENT_METHOD |
| Pago sin referencia | reference vacía | 422 HEALTH_PAYMENT_REFERENCE |
| Pago mayor que el neto | más de $120.000 o $53.500 | 422 PAYMENT_EXCEEDS_BALANCE |
Las citas tienen ids aps-<médico>-<AAAAMMDD>-<HHMM>-<hash>; reprogramar devuelve otro id.
Administrativo #
| Caso | Valor de prueba | Resultado |
|---|---|---|
| Colaboradores existentes | CC 1000000001 (Laura Sandbox, Operaciones) y CC 1000000002 (Andrés Sandbox, Tesorería) | Un upsert responde created: false |
| Colaborador nuevo | cualquier otro documento | Se “crea” |
| Colaborador inexistente | CC 9999999999 | 404 en las lecturas |
| Datos bancarios en el upsert | bank_name, bank_account_type, bank_account_number | 422 |
| Periodo abierto | per-O-… (octubre de 2026) | Recibe novedades |
| Periodo cerrado | per-C-… (septiembre de 2026, aprobado) | 409 PERIOD_CLOSED al cargar novedades |
| Conceptos que calcula el motor | SALARIO, SALUD, PENSION, PRIMA, CESANTIAS… | 422 |
| Comprobantes | cualquiera | Siempre DRAFT; mismo external_id → mismo id; post: true → 422 |
| Tercero existente | NIT 900000001 | created: false |
| Tesorería | recaudos, egresos, cuentas de cobro | DRAFT; valor <= 0 → 422; el cruce automático responde matched: 0 |
PQRS #
| Caso | Resultado |
|---|---|
Radicar con document de 1000000001 a 1000000004 | 201; el mismo cuerpo produce el mismo id y radicado. |
Radicar con document 9999999999 | 422 AFFILIATE_NOT_FOUND. |
Responder la PQRS PQRS-2026-999999 (cerrada) | 409. |
Cambiar de estado PQRS-2026-999999 | 409, salvo a closed (200, sin cambios). |
Id que no es pqr-… ni un radicado | 404 PQRS_NOT_FOUND. |
| Listar y estadísticas | Bandeja fija de 5 PQRS de los documentos de prueba (aplica filtros y paginación). |
Las transiciones permitidas son las mismas del motor (opened → in_progress, escalated,
resolved o closed; resolved solo → closed). Una escritura devuelve el estado nuevo, pero un
GET posterior vuelve a opened.
Webhooks #
| Caso | Resultado |
|---|---|
| Crear un endpoint | Id whe_sbx_<hash> (misma URL → mismo id) y secreto whsec_sbx_<hash>. |
| Rotar el secreto | Otro whsec_sbx_… fijo. |
Cualquier id whe_sbx_… | Existe (detalle, editar, borrar, rotar, probar). |
| Cualquier otro id de endpoint | 404 WEBHOOK_ENDPOINT_NOT_FOUND. |
URL http://, localhost o IP privada | 422 WEBHOOK_INVALID_URL. |
events vacío o con formato inválido | 422 WEBHOOK_INVALID_EVENTS (solo se valida el formato recurso.evento). |
| Enviar prueba | delivery_id whd_sbx_test_…, event_id test-sbx-…, "simulated": true. |
Entrega whd_sbx_ok_… | success, credits.decision, 2026-10-01; replay → 409. |
Entrega whd_sbx_fail_… | failed (HTTP 500), wallets.cashin.confirmed, 2026-10-02; replay → 201 (whd_sbx_retry_…). |
Entrega whd_sbx_aban_… | abandoned (HTTP 503), credits.disbursed, 2026-10-03; replay → 201 (whd_sbx_retry_…). |
| Catálogo | 9 eventos de muestra con "sample": true. |
No hay envío HTTP real: para recibir entregas firmadas usa una llave de producción en dev
(Probar tus webhooks).
Relojes y simuladores #
| Herramienta | Valores |
|---|---|
| Relojes de prueba | Id tcl-…; avance de 0 a 63 días (5.443.200 s) por llamada; nunca hacia atrás. |
| Simuladores | Monto 22.22 → FAILED; 33.33 → RETURNED; 11.11 o cualquier otro → SETTLED / DISBURSED. |
Prefijos de ids del sandbox #
| Prefijo | Qué es |
|---|---|
sbx- | Solicitud de crédito o subsidio |
dsb- | Desembolso de crédito |
mad- | Cuota monetaria |
wtx- | Transacción de billetera (P2P, Bre-B, recarga, movimiento) |
crd- | Tarjeta |
lna- | Préstamo de cartera |
slt- / aps- / inv- | Cupo, cita y factura de salud |
per-O- / per-C- | Periodo de nómina abierto / cerrado |
pqr- | PQRS |
whe_sbx_ / whd_sbx_ | Endpoint y entrega de webhooks |
tcl- | Reloj de prueba |