Transactions
Create, list, capture, and void payment transactions through Tokeflow's orchestration engine — with smart routing, fallback timelines, and idempotent creation.
A transaction is a single attempt to move money for an order through Tokeflow's orchestration engine. You describe the intent — amount, currency, payment method, country, and the card source — and Tokeflow routes the charge across the payment providers you have connected, applying your routing rules and cascading to a fallback provider when one declines or errors.
Tokeflow orchestrates the charge; it does not process or settle funds. Authorization, capture, and settlement are performed by the connected payment providers. Tokeflow normalizes their behavior behind one consistent transaction object so your integration stays the same no matter which provider handles the payment.
Every transaction records a timeline of routing attempts — which connector was tried, in what order, and how each responded. That timeline is your audit trail and your debugging tool. See The timeline.
How a transaction flows
A single API call can fan out to several providers behind the scenes. The request and response shapes are stable regardless of which provider ultimately authorizes the charge.
The transaction object
| Field | Type | Description |
|---|---|---|
id | string | Transaction ID, prefixed tx_. |
organization_id | string | Owning organization, prefixed org_. |
merchant_id | string | Owning merchant, prefixed mrc_. |
order_id | string | null | Order this charge belongs to, prefixed ord_. Tokeflow auto-creates an order for direct API charges. |
subscription_id | string | null | Subscription created from this charge when the underlying offer is recurring, prefixed sub_. Null otherwise. |
external_order_id | string | null | Your own order reference, echoed back for reconciliation. |
customer_id | string | null | Customer the charge is attributed to, prefixed cust_. |
payment_instrument_id | string | null | Saved instrument used, prefixed pi_. Null for one-off card or async charges. |
amount_authorized | integer | Amount authorized, in minor units. |
amount_captured | integer | Amount captured, in minor units. |
currency | string | ISO 4217 currency code. |
payment_method | string | One of credit_card, debit_card, pix, boleto, wallet. |
charge_type | string | payment or setup_verification. |
country | string | ISO 3166-1 alpha-2 country code. |
status | string | Current transaction status. See Status lifecycle. |
applied_routing_rule_id | string | null | The routing rule node that produced the successful attempt. |
mode | string | null | live or test — whether the payment ran on live or test provider credentials, taken from the connector of the final attempt. Null when no attempt was made (the charge was refused before routing). |
timeline | array | Ordered list of routing attempts. See The timeline. |
payment_instructions | object | null | Customer-facing instructions for async methods (PIX, boleto). Null for synchronous card charges. |
metadata | object | null | Arbitrary key-value pairs you attached at creation. |
created_at | string | Creation timestamp (ISO 8601 UTC). |
updated_at | string | Last update timestamp (ISO 8601 UTC). |
The timeline
The timeline is the ordered record of every provider attempt Tokeflow made for this transaction. It is how cascade and fallback become observable.
| Field | Type | Description |
|---|---|---|
attempt_number | integer | 1-based sequence number of the attempt. |
is_fallback | boolean | true if this attempt ran only because a previous attempt failed. |
connector_id | string | The connector used for this attempt. |
provider_slug | string | Neutral slug for the provider behind the connector. |
status | string | pending, success, failed, or error. |
started_at | string | When the attempt began (ISO 8601 UTC). |
finished_at | string | null | When the attempt resolved (ISO 8601 UTC), or null if still pending. |
error_category | string | null | High-level error class when the attempt did not succeed. |
error_code | string | null | Normalized error code when the attempt did not succeed. |
decline_reason | string | null | Why the attempt failed, in Tokeflow's own words — the same value whichever provider answered. See Decline reasons. Null when no reason is known. |
For a richer, per-attempt view — including 3DS status, normalized error messages, and gateway fees — call GET /transactions/:id/attempts.
Status lifecycle
The full status enum:
pending, pre_authorized, authorized, failed, canceled, voided, charged_back, refund_pending, capture_pending, refunded, partially_refunded, expired.
A failed charge is not an HTTP error. When a provider declines, Tokeflow still returns 201 Created with status: "failed" and a timeline explaining why. Always branch on the status field — never assume a 2xx response means the money was authorized.
When the provider gives no answer
A card charge can also come back pending. That happens when the provider was asked to move money but gave no verdict — a timeout, an unreadable response, or a server error on its side. The charge may or may not have landed, so Tokeflow does not treat it as a decline: it does not cascade to a fallback connector (that could charge the customer twice) and does not start dunning on a renewal. The transaction stays pending while Tokeflow establishes the outcome with the provider, then moves to its final status, which reaches you through the usual transaction webhooks. A checkout session paid by such a charge is settled at the same moment.
Do not retry a pending card charge with a new idempotency key. Wait for the final status, or read it back with GET /transactions/:id.
Decline reasons
Every failed attempt carries a decline_reason: Tokeflow's name for why it failed. Each provider's own decline codes are mapped onto this one vocabulary, so insufficient_funds means the same thing whichever connector answered. error_category and error_code stay the fields that drive retries; decline_reason is the one to show your support team, group in reports, or turn into a message for the customer.
| Group | Reasons |
|---|---|
| Issuer | do_not_honor, generic_decline, call_issuer, try_again_later, issuer_unavailable, invalid_transaction, transaction_not_permitted, do_not_retry |
| Funds and limits | insufficient_funds, limit_exceeded, max_attempts |
| Card data | invalid_card_number, invalid_cvv, invalid_expiry, incorrect_pin, invalid_amount, invalid_installments |
| Card status | card_expired, lost_or_stolen, pickup_card, card_restricted, card_inactive, account_closed, account_updated, authorization_revoked, card_not_supported, currency_not_supported, test_card_used |
| Fraud and risk | fraud_suspected, risk_blocked, blocklisted |
| Authentication | authentication_required, authentication_failed |
| Request | duplicate_transaction, invalid_request, invalid_token |
| Provider | processing_error, provider_unavailable, timeout, rate_limited, invalid_credentials |
| Unmapped | other — the provider gave a reason that has no Tokeflow name yet. |
The list only grows: a reason is never renamed or removed, and new reasons are added over time. Store the value as a string and treat an unknown one like other.
The reason also decides how Tokeflow routes the decline, the same way for every provider:
- On a customer-initiated charge, temporary issuer and funds declines (
do_not_honor,generic_decline,try_again_later,issuer_unavailable,insufficient_funds) are retried once on the same connector and then cascade to your fallbacks. Every other decline reason stops the charge without a cascade — card data, card status, fraud and risk, and issuer answers such ascall_issuerortransaction_not_permittedthat another attempt right now would not change. The customer has to fix the card, contact their bank or use another one. - On a subscription renewal, nothing is retried within the request. A reason that says the card can never be charged again (
lost_or_stolen,pickup_card,card_restricted,account_closed,account_updated,authorization_revoked,card_not_supported,currency_not_supported,test_card_used,do_not_retry,fraud_suspected,blocklisted) cancels the subscription. Every other decline — including expired cards, wrong card data andrisk_blocked— sends it to dunning, so the customer can still update the card.
Charge type
| Value | Meaning |
|---|---|
payment | A real charge for goods or services. Uses amount. |
setup_verification | A zero-or-nominal-amount verification used to validate and store a card for later charges, without billing the customer. |
Card sources
Every card charge needs a card source. Pick the combination that matches your scenario:
| Scenario | Field to send | Notes |
|---|---|---|
| New card | card_ciphertext_id | The single-use encrypted card reference (tok_…) returned by the Bridge SDK. Add save_payment_instrument: true to also save the card for later. |
| Saved card, customer re-types the CVV | payment_instrument_id + card_ciphertext_id | A stored instrument, prefixed pi_, plus a fresh ciphertext for the same card. |
| Saved card, one click | payment_instrument_id + credential_on_file: true | Charges the provider credential kept at enrollment. No card data, no CVV. See Stored-credential charges. |
| Checkout | checkout_session_id | Charge a completed checkout session, prefixed cks_. |
| Checkout, saved card | checkout_session_id + payment_instrument_id + credential_on_file: true | Pay the session with the customer's saved card, no card re-entry. Requires the merchant's saved_card_checkout_enabled opt-in. See Saved-card checkout. |
payment_instrument_id on its own is refused with 400. A saved card is charged either with credential_on_file: true or with a fresh card_ciphertext_id — one of the two must say how the card is presented.
Never send raw card data to this endpoint. Tokeflow's secure fields run inside isolated, cross-origin iframes; card data is encrypted in the browser and never touches your servers. The browser receives a single-use encrypted card reference (tok_…), which you pass as card_ciphertext_id. For PIX, boleto, and wallet methods, no card source is required.
Need a tok_… to test this endpoint? Use the hosted Bridge SDK tester at card.tokeflow.com: run it against your merchant with a public key, then copy the returned encrypted card reference and send it here as card_ciphertext_id. See Test the Bridge SDK without code.
Stored-credential charges (one-click)
Two boolean flags turn a plain charge into the two halves of a one-click flow — the pattern behind post-purchase upsells and downsells. The step-by-step guide is One-click upsells & downsells; this section is the field reference.
save_payment_instrument
Saves the card behind card_ciphertext_id as a payment instrument for customer_id before the charge runs, then charges the submitted ciphertext. When the provider approves, its reusable credential for the card is kept on that connector, so later credential_on_file charges can use it. The response carries the new payment_instrument_id.
| Rule | Detail |
|---|---|
| Requires | card_ciphertext_id minted with purpose=instrument_enrollment — from the Bridge SDK with purpose: 'instrument_enrollment' (standalone) or saveCard: true (inside a checkout session). customer_id. A card payment method. |
| Refused with | checkout_session_id (the session's save_card governs there), payment_instrument_id, credential_on_file. |
one_time ciphertext | 400 validation_error — the token cannot be saved. Nothing is charged. |
| Existing card | If the customer already has this card on file, the existing instrument is reused. |
credential_on_file
Charges the saved payment_instrument_id with the provider credential kept at enrollment — no card data, no CVV. Meant for customer-present, unscheduled charges such as a one-click upsell; scheduled charges belong to subscriptions.
| Rule | Detail |
|---|---|
| Requires | payment_instrument_id (must belong to the customer and be active) and a card payment method. Without a checkout session, also customer_id. |
| Refused with | card_ciphertext_id, save_payment_instrument, capture: false. |
With checkout_session_id | Pays the session with the saved card: the session supplies amount, currency, customer and installments. Requires the merchant's saved_card_checkout_enabled opt-in. See Saved-card checkout. |
| Prerequisite | The instrument holds a credential from one successful cardholder-present charge or card verification on a connector whose provider supports stored-credential charges. Otherwise 400 validation_error. |
| Routing | Pinned to the active connector holding the instrument's newest usable credential. Routing rules and cascade do not apply; if that connector cannot take the charge (currency, installments) the request is refused with 422. |
| Capture | Always auth + capture. |
| Order | Created with recurrence: "unscheduled". |
| Metadata | stored_credential_usage: "customer_initiated" is merged into metadata. |
| Authentication | A stored-credential charge cannot run a 3-D Secure challenge. An issuer that requires it declines with error_code: "AUTHENTICATION_REQUIRED" — fall back to a fresh ciphertext. |
Saved-card checkout
A returning customer can pay a checkout session with a card they saved earlier, without typing it again. Your server charges the session with the saved instrument:
{
"checkout_session_id": "cks_a1b2c3d4e5",
"payment_instrument_id": "pi_123",
"credential_on_file": true,
"payment_method": "credit_card",
"charge_type": "payment",
"country": "BR"
}| Rule | Detail |
|---|---|
| Opt-in | The merchant must have saved_card_checkout_enabled on. It is off by default. Enabling it states that you authenticate the customer before you identify them on the session — on a session, Tokeflow never sees the cardholder, so your server vouches for them. |
| What the opt-in covers | Any use of a saved payment_instrument_id on a checkout session without a fresh card_ciphertext_id: the credential_on_file charge, and a zero-amount trial started from the saved card alone. A charge with a fresh ciphertext (re-typed card) needs no opt-in. |
| Amount and customer | Taken from the session; any amount, currency or customer_id you send is not used. |
| Connector | Pinned to the connector that holds the card's stored credential, as for any credential_on_file charge. On a recurring offer, the subscription starts on that connector. |
| Opt-in off | 400 — collect the card again and charge the session with a fresh card_ciphertext_id. If the message says the feature is switched off by a Tokeflow setting, changing saved_card_checkout_enabled will not lift it; contact the Tokeflow team. |
To decide whether to offer "pay with this saved card", read the checkout context in the browser: savedCardCheckoutEnabled and each saved instrument's reusable flag tell you which cards can be charged on this session. They are hints; the charge itself stays authoritative and can still be declined.
Endpoints
POST/api/v1/transactions
Auth: Organization key (with merchant_id) or Merchant key. Scope: transactions:write.
Creates and submits a payment transaction. Tokeflow evaluates your routing rules, attempts the charge (cascading to a fallback provider on failure), and auto-creates an order for the charge.
Request fields
| Field | Type | Required | Description |
|---|---|---|---|
payment_method | string | Yes | One of credit_card, debit_card, pix, boleto, wallet. |
charge_type | string | Yes | payment or setup_verification. |
country | string | Yes | ISO 3166-1 alpha-2 country code. Used for routing and acquirer selection. |
amount | integer | Conditional | Amount in minor units (e.g. R$150.00 = 15000). Use 0 for setup_verification. Required unless checkout_session_id is provided. |
currency | string | Conditional | ISO 4217 currency code. Required unless checkout_session_id is provided. |
merchant_id | string | Conditional | Target merchant. Required with an Organization key; inferred from a Merchant key. |
card_ciphertext_id | string | Conditional | The single-use encrypted card reference (tok_…) returned by the Bridge SDK when you tokenize a new card. Required for a new card, and for a saved card charged with a re-typed CVV. |
payment_instrument_id | string | Conditional | Saved instrument to charge (pi_…). Send it with a fresh card_ciphertext_id or with credential_on_file: true — never alone. |
credential_on_file | boolean | No | true charges payment_instrument_id with the provider credential kept at enrollment — no card data, no CVV. Customer-present, unscheduled charges only. Requires customer_id unless a checkout_session_id is sent; with a session it needs the merchant's saved_card_checkout_enabled opt-in. Not combined with card_ciphertext_id, save_payment_instrument or capture: false. Default false. See Stored-credential charges. |
save_payment_instrument | boolean | No | true saves the card behind card_ciphertext_id for customer_id before charging, so later credential_on_file charges can reuse it. The ciphertext must be minted with purpose=instrument_enrollment. Not used with checkout_session_id. Default false. |
checkout_session_id | string | Conditional | Completed checkout session to charge. When provided, amount and currency are derived from the session and any values sent are ignored. |
wallet_type | string | Conditional | Required when payment_method is wallet. |
customer_id | string | Conditional | Customer to attribute the charge to. Required with save_payment_instrument, and with credential_on_file outside a checkout session; recommended otherwise for saved-credential resolution. |
payer_ip | string | No | The payer's IP address (IPv4 or IPv6) — where the customer is buying from, never your server's. Some providers require it on card charges. When omitted and the charge belongs to a checkout session, Tokeflow uses the IP the payer opened the Bridge SDK session from. |
external_order_id | string | No | Your order reference, stored for reconciliation. |
capture | boolean | No | true (default) = auth + capture in one step. false = pre-authorize only; capture later. |
risk_score | integer | No | Pre-auth fraud score from 0 to 100. Stored on the transaction. |
installments | object | No | Installment plan details, where supported. |
payer_token_id | string | No | Payer token (ptk_…) from the Bridge SDK's pix.tokenize() or boleto.tokenize(). Supplies the payer of a PIX or boleto charge, so your server never handles the CPF/CNPJ or address. Single use, bound to its payment method and checkout session. See PIX and boleto payers. |
boleto | object | No | Boleto options, read only when payment_method is boleto. expires_in_days: days until the boleto is due, 1–30, default 3. A due date on a weekend moves to the next business day. |
idempotency_key | string | No | Prevents duplicate charges. Can also be sent as the Idempotency-Key header. |
metadata | object | No | Arbitrary key-value pairs stored on the transaction. |
Always send an idempotency key on creation. Pass a unique value per charge attempt as the Idempotency-Key header (or idempotency_key in the body). If a network hiccup makes you retry, replaying the same key returns the original transaction (HTTP 200) instead of charging the customer twice. See Idempotency and rate limits.
Response codes
| Code | When |
|---|---|
201 Created | First submission of this transaction. Inspect status for the result. |
200 OK | Idempotent replay — the same Idempotency-Key was seen before; the original transaction is returned. |
400 Bad Request | Invalid parameters or missing required fields. |
401 Unauthorized | Missing or invalid API key. |
403 Forbidden | Authenticated but missing the required scope. |
Example request
curl -X POST https://api.tokeflow.com/api/v1/transactions \
-H "Authorization: Bearer sk_live_mer_…" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: order_12345_attempt_1" \
-d '{
"payment_method": "credit_card",
"charge_type": "payment",
"country": "BR",
"amount": 15000,
"currency": "BRL",
"customer_id": "cust_123",
"external_order_id": "order_888",
"card_ciphertext_id": "tok_8f3c2a1b9d4e",
"capture": true,
"metadata": { "campaign": "black_friday" }
}'Example response — 201 Created
{
"success": true,
"data": {
"id": "tx_777",
"organization_id": "org_123",
"merchant_id": "mrc_123",
"order_id": "ord_001",
"subscription_id": null,
"external_order_id": "order_888",
"customer_id": "cust_123",
"payment_instrument_id": null,
"amount_authorized": 15000,
"amount_captured": 15000,
"currency": "BRL",
"payment_method": "credit_card",
"charge_type": "payment",
"country": "BR",
"status": "authorized",
"applied_routing_rule_id": "node_2",
"mode": "live",
"timeline": [
{
"attempt_number": 1,
"is_fallback": false,
"connector_id": "conn_a1b2c3",
"provider_slug": "acquirer_a",
"status": "failed",
"started_at": "2026-01-15T12:30:00.000Z",
"finished_at": "2026-01-15T12:30:02.000Z",
"error_category": "SOFT_DECLINE",
"error_code": "INSUFFICIENT_FUNDS",
"decline_reason": "insufficient_funds"
},
{
"attempt_number": 2,
"is_fallback": true,
"connector_id": "conn_d4e5f6",
"provider_slug": "acquirer_b",
"status": "success",
"started_at": "2026-01-15T12:30:02.000Z",
"finished_at": "2026-01-15T12:30:04.000Z",
"error_category": null,
"error_code": null,
"decline_reason": null
}
],
"payment_instructions": null,
"metadata": { "campaign": "black_friday" },
"created_at": "2026-01-15T12:30:00.000Z",
"updated_at": "2026-01-15T12:30:04.000Z"
},
"request_id": "req_8f3c",
"timestamp": "2026-01-15T12:30:04.000Z"
}The example above shows a cascade: the primary connector declined, and Tokeflow's fallback authorized the charge — all in one API call.
A provider decline returns 201 with status: "failed" and an explanatory timeline, not an HTTP error. Branch on status. See The timeline for the fields that explain a failure.
Async methods (PIX and boleto)
For PIX and boleto, the response carries payment_instructions instead of an immediate authorization. The transaction stays pending until the customer pays; the final status arrives via webhook. Return payment_instructions to the browser so the customer can pay.
| Field | Method | Description |
|---|---|---|
type | both | pix or boleto. |
qr_code_text | PIX | The copy-and-paste ("copia e cola") code. |
qr_code_url | PIX | Provider-hosted QR code image. |
digitable_line | Boleto | The linha digitável — the 47 digits the payer types into their bank app. |
barcode | Boleto | The 44-digit barcode content. |
boleto_url | Boleto | Provider-hosted slip (PDF or instructions page). |
due_date | Boleto | Due date in Brasília time, YYYY-MM-DD, after any weekend roll-forward. |
expires_at | both | When the payment can no longer be made (ISO 8601). |
{
"success": true,
"data": {
"id": "tx_778",
"status": "pending",
"payment_method": "pix",
"amount_authorized": 0,
"amount_captured": 0,
"currency": "BRL",
"payment_instructions": {
"type": "pix",
"qr_code_text": "00020126580014BR.GOV.BCB.PIX…",
"qr_code_url": "https://example.com/qrcode/image.png",
"expires_at": "2026-01-15T13:00:00.000Z"
},
"timeline": [
{
"attempt_number": 1,
"is_fallback": false,
"connector_id": "conn_a1b2c3",
"provider_slug": "acquirer_a",
"status": "pending",
"started_at": "2026-01-15T12:30:00.000Z",
"finished_at": null,
"error_category": null,
"error_code": null,
"decline_reason": null
}
],
"created_at": "2026-01-15T12:30:00.000Z",
"updated_at": "2026-01-15T12:30:00.000Z"
},
"request_id": "req_9a2d",
"timestamp": "2026-01-15T12:30:00.000Z"
}A boleto charge returns the slip instead:
"payment_instructions": {
"type": "boleto",
"digitable_line": "23793381286000782713695000063305975520000370000",
"barcode": "23799755200003700003381260007827139500006330",
"boleto_url": "https://example.com/boleto/tx_779.pdf",
"due_date": "2026-10-29",
"expires_at": "2026-10-30T02:59:59.000Z"
}PIX and boleto payers
PIX and boleto need the payer's details — name, email and CPF/CNPJ, plus a full billing address for a boleto. The recommended way to collect them is a payer token: the Bridge SDK registers the payer from the browser and returns a ptk_…, and your server charges it with payer_token_id:
{
"payment_method": "boleto",
"charge_type": "payment",
"country": "BR",
"amount": 10000,
"currency": "BRL",
"external_order_id": "order_123",
"payer_token_id": "ptk_abc123",
"boleto": { "expires_in_days": 3 }
}| Rule | Detail |
|---|---|
| Single use | A token is spent by the charge that uses it — even a failed one — and expires 30 minutes after creation. Send an Idempotency-Key so a retried request replays the charge instead of needing a new token. |
| Bound | The token is valid only for the payment_method it was collected for and, when collected inside a checkout session, only with that checkout_session_id. |
| Refused | A token that is unknown, used, expired, collected for the other method or for another session is refused with 400 and code INVALID_PAYER_TOKEN. Nothing is charged. |
| Customer | The token's email alone never links the charge to an existing customer, since anyone can type any email in a browser. To attribute the charge, send customer_id, or send the same email from your server. |
| Precedence | The token's payer details win over payer details your server sends. |
Without a payer token, the payer comes from the customer you name with customer_id and its stored details. A boleto whose payer is missing a required field is refused before it reaches a provider.
GET/api/v1/transactions
Auth: Organization key (with merchant_id) or Merchant key. Scope: transactions:read.
Returns a paginated list of transactions, newest first.
Query parameters
| Field | Type | Required | Description |
|---|---|---|---|
status | string | No | Filter by status. Repeatable or comma-separated for multiple (e.g. status=refunded,partially_refunded). |
merchant_id | string | Conditional | Required with an Organization key; inferred from a Merchant key. |
customer_reference | string | No | Filter by customer reference. |
order_id | string | No | Filter by order. |
payment_method | string | No | Filter by payment method. |
currency | string | No | Filter by ISO 4217 currency code. |
date_from | string | No | Created on or after this timestamp (ISO 8601). |
date_to | string | No | Created on or before this timestamp (ISO 8601). |
page | integer | No | Page number (default 1, min 1). |
limit | integer | No | Items per page (default 20, max 100). |
Example request
curl -G https://api.tokeflow.com/api/v1/transactions \
-H "Authorization: Bearer sk_live_mer_…" \
--data-urlencode "status=authorized,refunded" \
--data-urlencode "currency=BRL" \
--data-urlencode "date_from=2026-01-01T00:00:00Z" \
--data-urlencode "page=1" \
--data-urlencode "limit=20"Example response — 200 OK
{
"success": true,
"data": [
{
"id": "tx_777",
"organization_id": "org_123",
"merchant_id": "mrc_123",
"order_id": "ord_001",
"external_order_id": "order_888",
"customer_id": "cust_123",
"amount_authorized": 15000,
"amount_captured": 15000,
"currency": "BRL",
"payment_method": "credit_card",
"charge_type": "payment",
"country": "BR",
"status": "authorized",
"applied_routing_rule_id": "node_2",
"mode": "live",
"timeline": [
{
"attempt_number": 1,
"is_fallback": false,
"connector_id": "conn_a1b2c3",
"provider_slug": "acquirer_a",
"status": "success",
"started_at": "2026-01-15T12:30:00.000Z",
"finished_at": "2026-01-15T12:30:02.000Z",
"error_category": null,
"error_code": null,
"decline_reason": null
}
],
"payment_instructions": null,
"metadata": {},
"created_at": "2026-01-15T12:30:00.000Z",
"updated_at": "2026-01-15T12:30:02.000Z"
}
],
"meta": {
"pagination": {
"page": 1,
"limit": 20,
"total": 42,
"total_pages": 3,
"has_next": true,
"has_prev": false
}
},
"request_id": "req_3b7e",
"timestamp": "2026-01-15T12:30:05.000Z"
}GET/api/v1/transactions/:id
Auth: Organization key (any transaction in the org) or Merchant key (its own transactions). Scope: transactions:read.
Retrieves a single transaction, including its full routing timeline. Returns the transaction object.
Example request
curl https://api.tokeflow.com/api/v1/transactions/tx_777 \
-H "Authorization: Bearer sk_live_mer_…"Example response — 200 OK
{
"success": true,
"data": {
"id": "tx_777",
"merchant_id": "mrc_123",
"amount_authorized": 15000,
"amount_captured": 15000,
"currency": "BRL",
"payment_method": "credit_card",
"charge_type": "payment",
"country": "BR",
"status": "authorized",
"applied_routing_rule_id": "node_2",
"mode": "live",
"timeline": [
{
"attempt_number": 1,
"is_fallback": false,
"connector_id": "conn_a1b2c3",
"provider_slug": "acquirer_a",
"status": "success",
"started_at": "2026-01-15T12:30:00.000Z",
"finished_at": "2026-01-15T12:30:02.000Z",
"error_category": null,
"error_code": null,
"decline_reason": null
}
],
"payment_instructions": null,
"metadata": {},
"created_at": "2026-01-15T12:30:00.000Z",
"updated_at": "2026-01-15T12:30:02.000Z"
},
"request_id": "req_5c1a",
"timestamp": "2026-01-15T12:30:06.000Z"
}A 404 (not_found_error) is returned if the transaction does not exist or does not belong to the authenticated merchant.
GET/api/v1/transactions/:id/attempts
Auth: Organization key (any transaction in the org) or Merchant key (its own transactions). Scope: transactions:read.
Returns every individual provider attempt for a transaction, with more detail than the inline timeline — including 3DS status, normalized error messages, and gateway fees. Use it to debug declines and analyze routing behavior.
Attempt fields
| Field | Type | Description |
|---|---|---|
id | string | Attempt ID. |
payment_transaction_id | string | Parent transaction, prefixed tx_. |
merchant_connector_id | string | Connector used for this attempt. |
provider_slug | string | Neutral provider slug. |
attempt_number | integer | 1-based sequence number. |
is_fallback | boolean | Whether the attempt ran as a fallback. |
status | string | pending, success, failed, or error. |
three_ds_status | string | null | 3DS authentication outcome, when applicable. |
error_category | string | null | High-level error class, on failure. |
error_code | string | null | Normalized error code, on failure. |
decline_reason | string | null | Tokeflow's reason for the failure, the same value whichever provider answered. See Decline reasons. |
error_message | string | null | Human-readable error, on failure. |
psp_transaction_id | string | null | Provider-side transaction reference. |
gateway_fee | integer | null | Processing fee as reported by the provider, in minor units. Null when the provider did not report one; some providers report it only later, and it is filled in when it arrives. |
started_at | string | When the attempt began (ISO 8601 UTC). |
finished_at | string | null | When the attempt resolved (ISO 8601 UTC). |
Example request
curl https://api.tokeflow.com/api/v1/transactions/tx_777/attempts \
-H "Authorization: Bearer sk_live_mer_…"Example response — 200 OK
{
"success": true,
"data": [
{
"id": "att_1",
"payment_transaction_id": "tx_777",
"merchant_connector_id": "conn_a1b2c3",
"provider_slug": "acquirer_a",
"attempt_number": 1,
"is_fallback": false,
"status": "failed",
"three_ds_status": null,
"error_category": "SOFT_DECLINE",
"error_code": "INSUFFICIENT_FUNDS",
"decline_reason": "insufficient_funds",
"error_message": "Issuer declined: insufficient funds",
"psp_transaction_id": "psp_tx_123",
"gateway_fee": null,
"started_at": "2026-01-15T12:30:00.000Z",
"finished_at": "2026-01-15T12:30:02.000Z"
},
{
"id": "att_2",
"payment_transaction_id": "tx_777",
"merchant_connector_id": "conn_d4e5f6",
"provider_slug": "acquirer_b",
"attempt_number": 2,
"is_fallback": true,
"status": "success",
"three_ds_status": "authenticated",
"error_category": null,
"error_code": null,
"decline_reason": null,
"error_message": null,
"psp_transaction_id": "psp_tx_456",
"gateway_fee": 45,
"started_at": "2026-01-15T12:30:02.000Z",
"finished_at": "2026-01-15T12:30:04.000Z"
}
],
"request_id": "req_7d2f",
"timestamp": "2026-01-15T12:30:07.000Z"
}POST/api/v1/transactions/:id/capture
Auth: Organization key (any transaction in the org) or Merchant key (its own transactions). Scope: transactions:write.
Captures a transaction that was pre-authorized with capture: false. The transaction must be in capture_pending status; after a successful capture it becomes authorized.
Request fields
| Field | Type | Required | Description |
|---|---|---|---|
amount | integer | No | Amount to capture, in minor units. Omit to capture the full authorized amount. For a partial capture, must be ≤ amount_authorized. |
Most providers allow only one capture per authorization. On a partial capture, the uncaptured remainder is automatically released.
Capture and void on the same authorization never both win. If a void (yours, or one the provider reports) settles the transaction while a capture is in flight, or a capture at a different amount lands first, the losing call answers 409 (conflict_error) — the provider may already have acted, so read the transaction back with GET /transactions/:id instead of retrying. A capture that finds the same capture already recorded returns that transaction with 200.
Example request — full capture
curl -X POST https://api.tokeflow.com/api/v1/transactions/tx_777/capture \
-H "Authorization: Bearer sk_live_mer_…" \
-H "Content-Type: application/json" \
-d '{}'Example request — partial capture
curl -X POST https://api.tokeflow.com/api/v1/transactions/tx_777/capture \
-H "Authorization: Bearer sk_live_mer_…" \
-H "Content-Type: application/json" \
-d '{ "amount": 10000 }'Example response — 200 OK
{
"success": true,
"data": {
"id": "tx_777",
"merchant_id": "mrc_123",
"amount_authorized": 15000,
"amount_captured": 15000,
"currency": "BRL",
"payment_method": "credit_card",
"charge_type": "payment",
"country": "BR",
"status": "authorized",
"applied_routing_rule_id": "node_2",
"mode": "live",
"timeline": [
{
"attempt_number": 1,
"is_fallback": false,
"connector_id": "conn_a1b2c3",
"provider_slug": "acquirer_a",
"status": "success",
"started_at": "2026-01-15T12:30:00.000Z",
"finished_at": "2026-01-15T12:30:02.000Z",
"error_category": null,
"error_code": null,
"decline_reason": null
}
],
"payment_instructions": null,
"metadata": {},
"created_at": "2026-01-15T12:30:00.000Z",
"updated_at": "2026-01-15T12:35:00.000Z"
},
"request_id": "req_a4e9",
"timestamp": "2026-01-15T12:35:00.000Z"
}A 400 (validation_error) is returned if the transaction is not in capture_pending status or the amount exceeds the authorized amount.
POST/api/v1/transactions/:id/void
Auth: Organization key (any transaction in the org) or Merchant key (its own transactions). Scope: transactions:write.
Voids an authorization before it is captured, releasing the held funds. The transaction must be in capture_pending or authorized status; after voiding it becomes voided.
Example request
curl -X POST https://api.tokeflow.com/api/v1/transactions/tx_777/void \
-H "Authorization: Bearer sk_live_mer_…"Example response — 200 OK
{
"success": true,
"data": {
"id": "tx_777",
"status": "voided",
"updated_at": "2026-01-15T12:36:00.000Z"
},
"request_id": "req_b8c0",
"timestamp": "2026-01-15T12:36:00.000Z"
}A 400 (validation_error) is returned if the transaction is not in a voidable status. A void that loses to a capture already recorded answers 409 (conflict_error); a void that finds the hold already released by the provider succeeds.
To reverse funds after capture, issue a refund instead of a void. See Refunds.
Errors
Every error uses the standard envelope. The most common cases for this resource:
| HTTP | type | Typical cause |
|---|---|---|
400 | validation_error | Missing required field, an invalid combination of card-source fields (payment_instrument_id alone, credential_on_file with a ciphertext, a one_time ciphertext with save_payment_instrument, an instrument with no stored credential yet), an unusable payer_token_id (INVALID_PAYER_TOKEN), or capture/void in the wrong state. |
401 | authentication_error | Missing or invalid API key. |
403 | authorization_error | Key lacks the required scope. |
404 | not_found_error | Transaction not found or not owned by the merchant. |
409 | conflict_error | A capture and a void raced on the same authorization and this call lost. Read the transaction back; do not retry blindly. |
422 | business_rule_error | No eligible connector for the charge — for example the currency, card brand or installment plan is not supported by the connector routing selected or pinned. |
429 | rate_limit_error | Rate limit exceeded — back off exponentially. |
See Errors for the full envelope and type catalog.
Related
- One-click upsells & downsells — the end-to-end guide for
save_payment_instrumentandcredential_on_file. - Idempotency and rate limits — safe retries and throttling.
- Refunds — reverse captured funds.
- Authentication — provision keys and scopes.
- How Tokeflow works — routing, cascade, and fallback.