Orchestration and routing
Everything on this page is a capability we enable for an account, not behaviour that is on by default. Whether routing and decline recovery are switched on for your account is account configuration: ask your account manager where your account stands, and do not build against the behaviour described here until it is enabled for you. What IS ambient, on every payment, is the one error contract described in Error intelligence.
When it is enabled, TensorRail takes a single payment and places it across the rails available for your account: choosing an approval-optimized route, applying rule-based fallback, and recovering a recoverable decline by re-attempting on an alternative. All of it happens behind the one payment object — you send one payment, and TensorRail works out how best to get it approved.
What you get
- Approval-optimized routing. A payment is routed to an eligible path for its market, method, currency, and account configuration.
- Cross-processor decline recovery. When an attempt returns a recoverable decline, the platform can automatically re-attempt the same payment on an alternative eligible route, without you re-submitting, up to your account's configured policy, before returning a terminal outcome.
- One outcome surface. You see the final result on the payment object and the webhook. You never pick a route and never learn which one was used.
You do not build, tune, or maintain any of this. The value shows up as a higher realized approval rate and resilience against transient failures.
Why this lifts approval rates
A single-route setup converts every decline into lost revenue, even when the decline is transient: a rail timing out, a route momentarily degraded, an attempt that a different eligible path would have approved. Orchestration changes the economics of exactly those cases:
- Declines are classified, not treated alike. A terminal decline (for example an invalid instrument) is returned to you immediately; a recoverable one is eligible for another attempt.
- Recovery happens inside the same payment. There is one
payment_id, one amount, one eventual outcome, one webhook. Recovery attempts can never double-collect, because they are attempts of the same payment, not new payments. - The policy is per account. How aggressively recovery runs is account configuration, not code you write, and it improves without you shipping anything.
What you observe from the outside: the payment stays processing slightly longer, then
resolves to succeeded or failed. A payment that would have died on a single route comes
back approved, and your integration cannot tell the difference, which is the point.
Safe client retries with Idempotency-Key
Separately from platform-side recovery, you make your own create calls safe to retry over a
flaky network by sending an Idempotency-Key header on POST /payments. Idempotency is
enforced on the payment-create path:
- The same key with the same body returns the original payment rather than creating a new one.
- The same key with a different body also returns the original payment. There is no
body-mismatch rejection on this path: the differing body is silently ignored, and you get the
first payment back with the first payment's amount. That makes a retry safe and a
key-generation bug invisible, so treat one-key-per-logical-attempt as your discipline rather
than something the API will catch for you. (The Reporting API's
POST /v1/refundsdoes compare bodies and rejects a mismatch withERR_3002; see Refunds via the Reporting API.)
Concretely, this timeout-retry sequence is safe:
# First attempt times out at your HTTP client, outcome unknown.
curl -X POST https://api.tensorrail.com/payments \
-H "api-key: rail_full_test_xxx" \
-H "Idempotency-Key: 5f0c9e2a-1b7d-4d3e-9a1c-6b2f8e4a1c33" \
-H "Content-Type: application/json" \
-d '{ "amount": 50000, "currency": "INR" }'
# Retry with the SAME key and SAME body: returns the original payment,
# same payment_id, no second collection.
Rules of thumb:
- Generate one fresh UUID per logical payment attempt and reuse it across network retries of that attempt.
- When the customer deliberately tries again after a failed payment, that is a new logical attempt: create a new payment with a new key. Never reuse the old key for a different attempt.
- You can also supply your own
payment_idon create. Creation is unique on it, but it behaves the other way round: a second create with the samepayment_idis rejected withERR_3018(duplicate) rather than replayed, so onERR_3018retrieve the existing payment. It is the right tool when you already have a natural unique order reference; theIdempotency-Keyis the right tool for a network retry.
The same Idempotency-Key contract applies to POST /refunds; see Refunds.
For the request shapes, see Accept a payment.
What you never have to build
Teams that run multi-market payments without an orchestration layer end up owning all of this themselves. On TensorRail it is platform behavior:
| Without orchestration | On TensorRail |
|---|---|
| Per-rail integrations and their maintenance | One create/confirm/webhook path |
| Decline taxonomies per rail | One unified error catalog (Error intelligence) |
| Retry and failover logic, with double-charge risk | In-payment recovery plus enforced idempotency |
| Per-rail reconciliation | One ledger-backed balance (Money integrity) |
How this fits the platform
Orchestration is the layer that sits under your payments once it is enabled for your account. It routes each one across the rails available to you, recovers declines where it safely can, and returns a single unified result, so the same integration reaches more markets and more approvals without you building any of it. The routing decision logic itself is internal to the platform and not merchant-configurable surface area; you get its results, not its knobs. See The platform.
TensorRail, Limassol, Cyprus.