Query Cash-Out by ID
Queries the details and status of a PIX transaction by transaction_id.
Endpoint
GET /api/external/transactions/:idHeaders
| Header | Type | Required | Description |
|---|---|---|---|
Authorization | String | Yes | ApiKey {client_id}:{client_secret} |
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
id | String | Yes | Transaction ID (transaction_id) |
Example
curl -X GET https://api.owem.com.br/api/external/transactions/PIXOUT20260309a1b2c3d4e5f6 \
-H "Authorization: ApiKey $CLIENT_ID:$CLIENT_SECRET"The response varies with the transaction state. This endpoint searches in the following sources, in this priority order:
- Settled transaction (
transactionsbytransaction_id, or by internal hex UUID —Base.decode16) - PIX OUT in progress (
outbound_requestsstage 1 or 2) bytransaction_id - QR Code (
qrcodesbytx_id) — if it haspaid_at+payment_end_to_end_id, resolves the linked real transaction intransactionsvia E2E; otherwise returns the "QR Code not paid" shape
If nothing matches in any source, returns 404.
Rejected transaction does NOT appear here by transaction_id
Synchronous BACEN rejections (PACS.002 RJCT) and force-voids are persisted in failed_transactions and are not found by this endpoint when you query by transaction_id. Use the alternative endpoints:
- GET /transactions/e2e/:e2e_id — searches
failed_transactionsbyend_to_end_id - GET /transactions/ref/:external_id — searches
failed_transactionsbyexternal_id - Webhook
pix.payout.rejected/pix.payout.failed— real-time notification with full payload
Input validation rejections (immediate 400/422) do not go through this flow — the synchronous error was already returned on the POST.
Response -- Settled Transaction (200)
{
"worked": true,
"data": {
"id": "c7f3a8b1-2d4e-4f6a-9c1b-3e5f7a9b1d3e",
"transaction_id": "PIXOUT20260309a1b2c3d4e5f6",
"end_to_end_id": "E37839059202603091530abcdef01",
"external_id": "order-9876",
"type": "pix",
"direction": "outbound",
"status": "settled",
"amount": 300000,
"fee_amount": 350,
"net_amount": 300350,
"description": "Pagamento fornecedor",
"counterparty_name": "Joao Silva",
"recipient_key": "12345678901",
"created_at": "2026-03-09T15:30:00Z",
"completed_at": "2026-03-09T15:30:02Z"
}
}| Field | Type | Description |
|---|---|---|
data.id | String | Internal transaction UUID in canonical form with hyphens, 36 characters (e.g., "c7f3a8b1-2d4e-4f6a-9c1b-3e5f7a9b1d3e"). The controller passes the 16-byte binary through Ecto.UUID.load/1 before serializing — the format is NOT 32-char lowercase hex without hyphens |
data.transaction_id | String | Unique transaction identifier (common prefix PIXOUT for outbound and PIXIN for inbound, but not guaranteed — always read the field, do not derive from the prefix) |
data.end_to_end_id | String | End-to-End identifier in SPI/BACEN |
data.external_id | String | Your system identifier. null if not provided |
data.type | String | Transaction type. Common values: pix, pix_return. The backend passes t.type through — legacy rows may return other values |
data.direction | String | outbound (cash-out), inbound (cash-in), credit, debit. The last two appear when the t.direction field was not persisted and the backend inferred via Helpers.infer_direction/2 based on account_id × to_account_id |
data.status | String | Normalized status (see table below) |
data.amount | Integer | Amount in base units (÷ 10,000 for BRL). 300000 = R$ 30.00 |
data.fee_amount | Integer | Fee charged in base units |
data.net_amount | Integer | Net amount in base units |
data.description | String | Description provided at creation |
data.counterparty_name | String | Counterparty name (payer or recipient) |
data.recipient_key | String | Recipient PIX key (outbound only) |
data.created_at | String | Creation date (ISO 8601) |
data.completed_at | String | Completion date (ISO 8601), null if pending |
Response -- PIX OUT in progress (200)
Returned when the transaction is still being processed (before BACEN confirmation).
{
"worked": true,
"data": {
"status": "processing",
"transaction_id": "PIXOUT20260309a1b2c3d4e5f6",
"end_to_end_id": "E37839059202603091530abcdef01",
"amount": 300000,
"fee_amount": 350,
"net_amount": 300350,
"external_id": "order-9876",
"pix_key": "12345678901",
"description": "Pagamento fornecedor",
"type": "pix",
"direction": "outbound",
"payment_status": "processing",
"started_at": "2026-03-09T15:30:00Z",
"recipient": {
"name": "Joao Silva",
"key": "12345678901",
"key_type": "cpf"
}
}
}| Additional field | Type | Description |
|---|---|---|
data.recipient | Object | Recipient data resolved via DICT |
data.recipient.name | String | Key holder's name |
data.recipient.key | String | PIX key used |
data.recipient.key_type | String | Key type (cpf, cnpj, email, phone, evp) |
data.started_at | String | Processing start date (ISO 8601) |
Response -- Rejected Transaction (200)
Returned by endpoints /transactions/e2e/:e2e_id and GET /transactions/ref/:external_id when the PIX was rejected by the SPI or failed during processing. Not returned by GET /transactions/:id (see warning above about failed_transactions).
{
"worked": true,
"data": {
"status": "failed",
"transaction_id": "PIXOUT20260309a1b2c3d4e5f6",
"end_to_end_id": "E37839059202603091530abcdef01",
"amount": 300000,
"fee_amount": 350,
"external_id": "order-9876",
"type": "pix",
"direction": "outbound",
"payment_status": "failed",
"failure_reason": "rejected: AB03",
"reason_code": "AB03",
"reason_description": "Aborted by PSP of creditor",
"started_at": "2026-03-09T15:30:00Z",
"failed_at": "2026-03-09T15:30:05Z",
"recipient": {
"name": "Joao Silva",
"key": "12345678901"
}
}
}| Additional field | Type | Description |
|---|---|---|
data.failure_reason | String | Raw rejection reason (format: "rejected: {CODE}" for BACEN codes, or integration descriptions like "dict_key_not_found", "ambiguous key", "insufficient balance") |
data.reason_code | String | Structured code extracted from failure_reason. For BACEN rejections, follows ISO 20022 (AC03, AB03, ED05, DUPL, etc.). null when failure_reason does not contain a structured code |
data.reason_description | String | Human description of reason_code, in English (centralized in Fluxiq.UseCases.Pix.ReasonCodes). Examples: "Invalid creditor account number" (AC03), "Aborted by PSP of creditor" (AB03), "Settlement failed" (ED05). null when the code is not recognized |
data.recipient | Object | Recipient data (when available). May be omitted in very early failures |
data.failed_at | String | Rejection date/time (ISO 8601) |
Rejection code structure
The reason_code and reason_description fields are derived from the centralized dictionary in Fluxiq.UseCases.Pix.ReasonCodes. Use them for programmatic error routing. Keep failure_reason only for logs and diagnostics. See the full code table in PIX Cash-Out by Key -- Rejection codes.
Response -- QR Code Not Paid (200)
Returned when the ID corresponds to a QR Code that has not yet been paid.
{
"worked": true,
"data": {
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"transaction_id": "7popu57v6us7p6pcicgq12345",
"end_to_end_id": null,
"external_id": "order-9876",
"type": "pix_qrcode",
"status": "pending",
"amount": 300000,
"fee_amount": 0,
"net_amount": 300000,
"description": "Cobrança PIX",
"direction": "credit",
"counterparty_name": null,
"recipient_key": "12345678901",
"created_at": "2026-03-09T15:30:00Z",
"completed_at": null
}
}| QR status | Description |
|---|---|
pending | QR Code active, awaiting payment |
settled | QR Code with paid_at filled, but the controller could not resolve the real transaction in transactions by payment_end_to_end_id. Happens in the window between Phase 1 ACSP (ACK to BACEN) and Phase 2 ACCC (PG MERGE completed), or when the QR↔TX link fails for another reason. Active code path (show_transaction_controller.ex:83) — do not treat as an error; retry the query a few seconds later and the backend should return the "Settled Transaction" shape normally |
expired | QR Code expired (default TTL 60 min — configurable per account via account.qrcode_expiration_seconds) |
cancelled | QR Code manually cancelled (or cancelled en masse during static→dynamic migration in 2026-04) |
Note: when the QR is paid and the backend can look up the real transaction by E2E, the response is the "Settled Transaction" shape above (with status: "settled" and the real transaction_id, not the QR tx_id). Only when this lookup returns nil does it fall back to the "QR Code Not Paid" shape with status: "settled".
status Field Values
Status of GET depends on the source
GET /transactions/:id searches 4 distinct tables and returns a different status depending on where the transaction is found. There is no single vocabulary across all sources.
| Status | Source | Meaning |
|---|---|---|
processing | outbound_requests (stage 1 or 2) | PACS.008 sent, awaiting BACEN confirmation. Balance on hold |
processing | outbound_requests (stage 4 — retry queue) | Rate-limited by ClientLimiter / DictBucket.Guard, awaiting automatic retry (Oban PixOutRetryWorker, TTL 120min). The shape returned is the same as stage 1/2 — differentiate via the pix.payout.queued webhook dispatched on enqueue |
settled | transactions (internal status 1) | BACEN confirmed settlement and the balance has already moved. Final success state |
pending | transactions (internal status 2) | Rare case — transaction in an intermediate state before promotion to settled |
failed | transactions (internal status 3) OR failed_transactions | Final failure state. Rejected by BACEN (PACS.002 RJCT), orphan force-voided (>30min on hold), retry queue expiration (DICT_QUEUE_TIMEOUT) or internal error |
Compatibility note: legacy rows (prior to vocabulary consolidation) may show status: "completed" or status: "accepted". Treat them as equivalent to "settled".
Webhook correspondence
pix.payout.queuedcorresponds tostatus: "processing"on GET while the request is inoutbound_requests.stage=4pix.payout.confirmedalways corresponds tostatus: "settled"on GETpix.payout.failedandpix.payout.rejectedalways correspond tostatus: "failed"on GET
For QR Codes (cash-in, outside the PIX OUT scope), the qrcodes table adds 4 values: pending, settled, expired, cancelled — described in the "QR Code Not Paid" section above.
Error Response -- 404
{
"worked": false,
"detail": "Transação não encontrada"
}