The gateway responds in one of two ways, depending on the type of operation.

Writes: the transaction envelope #

Every operation that changes something (the ones that carry an Idempotency-Key) responds with the same envelope, whatever the module:

JSON
{
  "status": "success",
  "transaction_id": "a1b2c3d4-0000-4a1b-9c2d-000000000001",
  "module": "wallet",
  "data": {
    "transaction_id": "9a8b…",
    "status": "COMPLETED",
    "amount": "30000.00",
    "currency": "COP"
  },
  "idempotent_replay": true
}
FieldWhat it is
statusThe gateway’s result (success). Failures don’t arrive here: they’re problem+json HTTP errors.
transaction_idThe transaction id in the gateway. Store it: you’ll use it to check its status, reconcile and open cases with support.
moduleThe module that processed it (credits, subsidies, wallet, loans, pqrs, health, admin, webhooks…).
dataThe engine’s body, as is: domain ids (application_id, payment_id…), statuses and amounts.
idempotent_replayOnly appears (as true) when the response is the stored replay of an Idempotency-Key that was already used.

Tip

Don’t confuse transaction_id (the gateway’s, in the envelope) with the domain ids inside data (data.application_id, the wallet’s data.transaction_id, a webhook endpoint’s data.id…). To decide on or disburse a credit you use the application_id, not the envelope’s transaction_id.

Reads: the engine’s body #

GETs (and POSTs that only query: resolving a recipient, looking up a bill, quoting FX, calculating taxes) return the engine’s object or list directly, with no envelope. Lists are paginated (Pagination).

How the SDKs expose it #

Each SDK turns the envelope into a Transaction and, where it adds value, turns reads into typed models (Balance, Resolution, AffiliateLookup, WebhookEndpoint, TestClock, Page…). Every model keeps the original body in raw (Raw in .NET and Go) for the fields the SDK doesn’t type.

PythonJavaScript / TSPHP.NETGo
EnvelopeTransactionTransactionCompense\Sdk\Model\TransactionCompense.Sdk.Models.Transaction*compense.Transaction
Gateway idtx.transaction_idtx.transactionId$tx->transactionIdtx.TransactionIdtx.TransactionID
Replaytx.idempotent_replaytx.idempotentReplay$tx->idempotentReplaytx.IdempotentReplaytx.IdempotentReplay
Engine bodytx.data (dict)tx.data (JsonObject)$tx->data (array)tx.Data (JsonObject)tx.Data (map[string]any)
Reads without a modeldictJsonObjectarrayJsonObjectmap[string]any
tx = client.credits.submit_application(
    affiliate={"document_type": "CC", "document_number": "1000000001"},
    product_code="LIBRANZA_12M", amount="3500000", term_months=12,
    external_reference="HORIZONTE-CRED-2026-000123",
)
app_id = tx.data["application_id"]      # domain id, to decide and disburse
save(tx.transaction_id, app_id)         # store both

Useful response headers #

HeaderWhat it’s for
X-Request-IDThe request id; matches the trace_id in errors. Log it.
RateLimit-Limit, RateLimit-Remaining, RateLimit-ResetThe state of your limit (Rate limits).
Retry-AfterOn 429 (and some 5xx): seconds to wait. The SDKs honor it.
Deprecation, Sunset, LinkThe operation is deprecated (Deprecations).