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. |
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. |
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.
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 exactly one card source. Pick the one 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. |
| Saved card | payment_instrument_id | A stored instrument, prefixed pi_. |
| Checkout | checkout_session_id | Charge a completed checkout session, prefixed cks_. |
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.
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 unless payment_instrument_id is sent. |
payment_instrument_id | string | Conditional | Saved instrument to charge. Required for a saved card unless card_ciphertext_id is sent. |
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 | No | Customer to attribute the charge to. Recommended for saved-credential resolution. |
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. |
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",
"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"
},
{
"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
}
],
"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.
{
"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
}
],
"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"
}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",
"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
}
],
"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",
"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
}
],
"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. |
error_message | string | null | Human-readable error, on failure. |
psp_transaction_id | string | null | Provider-side transaction reference. |
gateway_fee | integer | null | Gateway fee in minor units. |
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",
"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,
"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.
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",
"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
}
],
"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.
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, 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. |
429 | rate_limit_error | Rate limit exceeded — back off exponentially. |
See Errors for the full envelope and type catalog.
Related
- Idempotency and rate limits — safe retries and throttling.
- Refunds — reverse captured funds.
- Authentication — provision keys and scopes.
- How Tokeflow works — routing, cascade, and fallback.