Skip to main content

Orchestration and routing

Set up per account

Routing is account configuration that TensorRail sets up and manages for you; ask your account manager to change yours. What applies to every payment is the one error contract described in Error intelligence and the idempotency contract below.

TensorRail takes a single payment and routes it to a rail enabled for your account, under the routing set up for your account: which of the eligible rails carries a payment, by its market, method and currency. All of it happens behind the one payment object: you send one payment, and you get one outcome back.

What you get​

  • Rule-based routing. A payment is routed to an eligible rail for its market, method and currency, in the priority set up for your account.
  • One outcome surface. You see the final result on the payment object and the webhook. You do not need to pick a route, and you never learn which one was used.
  • Declines you can act on. A decline comes back as a stable code from the unified error catalog, and the Error catalog says for each code whether a new attempt can succeed.

You do not build, tune, or maintain the routing logic.

Declines and new attempts​

Integrating rails one by one means learning each rail's dialect of failure before you can decide what to do next. On TensorRail that decision starts from one code:

  • Declines are classified, not treated alike. A terminal decline (for example an invalid instrument) will fail again however often it is attempted; for other codes the catalog says a new attempt can succeed.
  • failed is terminal for the payment. When the customer tries again, that is a new payment with a new Idempotency-Key (below). A failed payment is never re-confirmed.

What you observe from the outside: the payment resolves to succeeded or failed, with the same statuses and codes whatever rail carried it.

Safe client retries with Idempotency-Key​

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/refunds does compare bodies and rejects a mismatch with ERR_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_id on create. Creation is unique on it, but it behaves the other way round: a second create with the same payment_id is rejected with ERR_3018 (duplicate) rather than replayed, so on ERR_3018 retrieve the existing payment. It is the right tool when you already have a natural unique order reference; the Idempotency-Key is the right tool for a network retry.

Refunds take an Idempotency-Key too, on POST /v1/refunds, where it is required and a reused key with a different body is refused; 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 orchestrationOn TensorRail
Per-rail integrations and their maintenanceOne create/confirm/webhook path
Decline taxonomies per railOne unified error catalog (Error intelligence)
Retry logic, with double-charge riskEnforced idempotency on payment and refund creation
Per-rail reconciliationOne ledger-backed balance (Money integrity)

How this fits the platform​

Orchestration is the layer that sits under your payments. It routes each one across the rails enabled for your account and returns a single unified result, so the same integration reaches more markets without you building any of it. Routing is account configuration that TensorRail manages, not merchant-configurable API surface; you get its results, not its knobs. See The platform.


TensorRail, Limassol, Cyprus.