Skip to content

Query Cash-Out by ID

Queries the details and status of a PIX transaction by transaction_id.

Endpoint

GET /api/external/transactions/:id

Headers

HeaderTypeRequiredDescription
AuthorizationStringYesApiKey {client_id}:{client_secret}

Path Parameters

ParameterTypeRequiredDescription
idStringYesTransaction ID (transaction_id)

Example

bash
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:

  1. Settled transaction (transactions by transaction_id, or by internal hex UUID — Base.decode16)
  2. PIX OUT in progress (outbound_requests stage 1 or 2) by transaction_id
  3. QR Code (qrcodes by tx_id) — if it has paid_at + payment_end_to_end_id, resolves the linked real transaction in transactions via 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:

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)

json
{
  "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"
  }
}
FieldTypeDescription
data.idStringInternal 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_idStringUnique 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_idStringEnd-to-End identifier in SPI/BACEN
data.external_idStringYour system identifier. null if not provided
data.typeStringTransaction type. Common values: pix, pix_return. The backend passes t.type through — legacy rows may return other values
data.directionStringoutbound (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.statusStringNormalized status (see table below)
data.amountIntegerAmount in base units (÷ 10,000 for BRL). 300000 = R$ 30.00
data.fee_amountIntegerFee charged in base units
data.net_amountIntegerNet amount in base units
data.descriptionStringDescription provided at creation
data.counterparty_nameStringCounterparty name (payer or recipient)
data.recipient_keyStringRecipient PIX key (outbound only)
data.created_atStringCreation date (ISO 8601)
data.completed_atStringCompletion date (ISO 8601), null if pending

Response -- PIX OUT in progress (200)

Returned when the transaction is still being processed (before BACEN confirmation).

json
{
  "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 fieldTypeDescription
data.recipientObjectRecipient data resolved via DICT
data.recipient.nameStringKey holder's name
data.recipient.keyStringPIX key used
data.recipient.key_typeStringKey type (cpf, cnpj, email, phone, evp)
data.started_atStringProcessing 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).

json
{
  "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 fieldTypeDescription
data.failure_reasonStringRaw rejection reason (format: "rejected: {CODE}" for BACEN codes, or integration descriptions like "dict_key_not_found", "ambiguous key", "insufficient balance")
data.reason_codeStringStructured 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_descriptionStringHuman 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.recipientObjectRecipient data (when available). May be omitted in very early failures
data.failed_atStringRejection 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.

json
{
  "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 statusDescription
pendingQR Code active, awaiting payment
settledQR 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
expiredQR Code expired (default TTL 60 min — configurable per account via account.qrcode_expiration_seconds)
cancelledQR 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.

StatusSourceMeaning
processingoutbound_requests (stage 1 or 2)PACS.008 sent, awaiting BACEN confirmation. Balance on hold
processingoutbound_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
settledtransactions (internal status 1)BACEN confirmed settlement and the balance has already moved. Final success state
pendingtransactions (internal status 2)Rare case — transaction in an intermediate state before promotion to settled
failedtransactions (internal status 3) OR failed_transactionsFinal 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.queued corresponds to status: "processing" on GET while the request is in outbound_requests.stage=4
  • pix.payout.confirmed always corresponds to status: "settled" on GET
  • pix.payout.failed and pix.payout.rejected always correspond to status: "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

json
{
  "worked": false,
  "detail": "Transação não encontrada"
}

Owem Pay Instituição de Pagamento — ISPB 37839059