Dos operaciones transversales que no pertenecen a ningún módulo de negocio, pero que toda integración usa:

OperaciónMétodo SDK (Python)ScopePara qué
GET/v1/gateway/transactions/{tx_id}/client.transactions.get(tx_id)ninguno (cualquier llave válida)Estado y traza de una escritura que hiciste por el gateway.
GET/v1/gateway/health/client.system.health()ningunoValidar la llave: tenant y scopes. Health check de tu monitoreo.

En los demás SDK: client.transactions.get / client.system.health() (TypeScript), $client->transactions->get / $client->system->health() (PHP), client.Transactions.GetAsync / client.System.HealthAsync() (.NET) y client.Transactions.Get / client.System.Health (Go).

Estado de una transacción #

Cada escritura que pasa por el gateway (radicar, decidir, desembolsar, registrar un recaudo, cargar afiliados…) deja un registro de transacción y te devuelve su transaction_id en el sobre de respuesta. GET/v1/gateway/transactions/{tx_id}/ lee ese registro.

CampoModelo SDKQué es
transaction_idsíEl id que te devolvió la escritura.
modulesícredits, subsidies, affiliates, loans, wallet…
statussíPROCESSING, COMPLETED o FAILED (ver abajo).
referencesíLa referencia de negocio que quedó asociada: el application_id en créditos y subsidios, el disbursement_id en cuota monetaria, el receipt_id en cartera.
historysíLos hitos por los que pasó la llamada, en orden (p. ej. ["received", "submitted"]).
operationen rawLa operación interna (application, decision:<id>, disbursement:<id>…).
dataen rawLa respuesta que se guardó (la misma data del sobre).
erroren rawEl motivo, si status es FAILED.
created_at, updated_aten rawMarcas de tiempo ISO 8601.

Los modelos tipados (TransactionStatus) exponen los cinco primeros; el resto está en raw (status.raw, $status->raw, status.Raw).

Qué significa cada estado #

stateDiagram-v2
  [*] --> PROCESSING: el gateway acepta la escritura
  PROCESSING --> COMPLETED: el motor la completó
  PROCESSING --> FAILED: el motor la rechazó o falló
  PROCESSING --> PROCESSING: la operación de negocio sigue en curso
  • PROCESSING: la escritura se aceptó y la operación de negocio sigue abierta. Es el estado normal de una radicación de crédito o subsidio (la solicitud queda en manos del motor y del analista) y de una carga de afiliados con filas rechazadas.
  • COMPLETED: la operación terminó (una decisión, un desembolso, un recaudo aplicado, una carga sin rechazos).
  • FAILED: el motor rechazó la petición o no respondió; raw.error trae el motivo.

El registro describe la llamada al gateway, no la vida completa del objeto de negocio: el estado de una solicitud de crédito (aprobada, desembolsada…) se consulta en su módulo (credits.get_application) y llega por webhooks. Usa transactions.get para soporte, auditoría y conciliación de tus propias llamadas.

status = client.transactions.get("a1b2c3d4-0000-4a1b-9c2d-000000000001")
print(status.module, status.status, status.reference, status.history)
print(status.raw.get("operation"), status.raw.get("error"))
JSON
{
  "transaction_id": "a1b2c3d4-0000-4a1b-9c2d-000000000001",
  "module": "credits",
  "operation": "application",
  "status": "PROCESSING",
  "reference": "sbx-E-2cf1b69c533d",
  "history": ["received", "submitted"],
  "data": {"application_id": "sbx-E-2cf1b69c533d", "status": "RECEIVED"},
  "error": null,
  "created_at": "2026-11-02T14:03:11.204Z",
  "updated_at": "2026-11-02T14:03:11.612Z"
}

Solo ves las transacciones de tu entidad: un transaction_id de otro tenant (o inexistente) responde 404.

Esperar un resultado sin webhooks #

Lo recomendado es reaccionar a los webhooks, pero si un proceso por lotes necesita confirmar sus propias escrituras puedes consultar con espera creciente hasta que el estado deje de ser PROCESSING. Ten presente que una radicación se queda en PROCESSING a propósito: espera así solo operaciones que terminan (decisiones, desembolsos, recaudos, cargas).

import time

def esperar(tx_id: str, intentos: int = 6) -> str:
    espera = 1.0
    for _ in range(intentos):
        st = client.transactions.get(tx_id)
        if st.status != "PROCESSING":
            return st.status
        time.sleep(espera)
        espera *= 2
    return "PROCESSING"

¿Y si nunca recibiste el transaction_id?

Si la escritura se cortó antes de llegar la respuesta, no tienes transaction_id que consultar. Reenvía la misma petición con la misma Idempotency-Key: el gateway te devuelve la respuesta original (con su transaction_id e idempotent_replay: true) o, si sigue en curso, un 409 que puedes reintentar. Ver Idempotencia.

Validar la llave y sus scopes #

GET/v1/gateway/health/ no toca el motor: confirma que la llave es válida y devuelve el tenant al que pertenece y la lista de scopes. Úsalo en dos momentos:

  1. Al arrancar tu servicio: verifica que la llave tiene todos los scopes que tu integración necesita y falla rápido (con un mensaje claro) si falta alguno, en vez de descubrirlo con un 403 en la primera operación de un afiliado.
  2. Como health check de monitoreo: un 200 dice que el gateway responde y que tu llave sigue vigente (no fue revocada ni rotada).
from compense_gateway import AuthError

REQUERIDOS = {"credits:submit", "credits:read", "loans:read", "loans:collect"}

try:
    health = client.system.health()
except AuthError as e:
    raise SystemExit(f"Llave inválida o revocada ({e.status}, trace_id={e.trace_id})")

faltan = REQUERIDOS - set(health.scopes)
if faltan:
    raise SystemExit(f"A la llave de {health.tenant} le faltan scopes: {sorted(faltan)}")
print("OK", health.tenant, health.status)
RespuestaSignificado
200Llave válida: revisa tenant y scopes.
401Falta la cabecera X-API-Key.
403Llave inválida, revocada, de otro entorno (una llave de sandbox contra producción o al revés) o una IP fuera de la lista permitida de la llave (title: IP not allowed).

Para monitoreo, una consulta por minuto es suficiente. Un 200 no garantiza que el motor de un módulo esté arriba (este endpoint no lo toca): para eso vigila los 502/504 de tus operaciones reales y los límites de tasa de tus escrituras.

trace_id y X-Request-ID #

Cada respuesta del gateway lleva la cabecera X-Request-ID. En los errores, el cuerpo application/problem+json trae el mismo valor como trace_id: es lo que debes citar a soporte, porque con él se encuentra la causa exacta en los registros del gateway (que nunca se filtra en la respuesta).

  • Los SDK generan un X-Request-ID por cada operación lógica y lo mantienen igual en todos sus reintentos automáticos; en las excepciones lo exponen como trace_id (Python), traceId (TypeScript, PHP), TraceId (.NET) y TraceID (Go).
  • Con cURL o tu propio cliente HTTP puedes enviar tu propio X-Request-ID (por ejemplo, el id de la petición en tu sistema) y el gateway lo devuelve.
  • En una respuesta exitosa tu llave de correlación es el transaction_id del sobre.

Registra siempre juntos transaction_id, trace_id (si hubo error), tu external_reference y la Idempotency-Key: con esos cuatro datos cualquier caso de soporte se resuelve en minutos. Más en Errores y Soporte.

Lo que sigue #