Authentication
TensorRail uses API keys. There is one base URL, https://api.tensorrail.com, and your
key selects the environment: …_test_… keys operate on sandbox data, …_live_… keys on
production.
Everything is served from https://api.tensorrail.com. Which header you use
depends on the endpoint:
- Payment endpoints (
/payments,/refunds,/disputes) takeapi-key: rail_…. /v1/…endpoints (the read-oriented Reporting API plus balances, settlements, exports, webhook and key management) take the samerail_*key as a bearer token:Authorization: Bearer rail_….
The main API is published as an OpenAPI spec, downloadable at
/openapi/tensorrail-merchant.json. Import it into Postman
or Insomnia to browse and try those endpoints against your test keys. The spec covers the main
API; the /v1 account and reporting endpoints are documented on the
Reporting API guide pages instead.
Key types
| Key | Prefix | Use | Where |
|---|---|---|---|
| Secret | rail_full_… | Full server-to-server access | Server-side only, never in a browser |
| Publishable | rail_open_… | Browser-safe; must be paired with a payment's client_secret | Hosted checkout and client-side flows |
- Secret keys (
rail_full_test_…,rail_full_live_…) authenticate every server call: create, confirm, capture, cancel, retrieve, list, and refunds. Keep them secret and server-side. - Publishable keys (
rail_open_test_…,rail_open_live_…) are safe to expose in the browser. They cannot authenticate server APIs on their own; they are used together with a payment'sclient_secretto drive the hosted page or a client-side confirm.
This split is what makes the browser flow safe: a publishable key plus a client_secret can
act on exactly one payment (the one whose secret it holds) and nothing else. A leaked
publishable key cannot create payments, list data, or refund anything.
Sending the key
Pass your secret key in the api-key header on every request:
- curl
- Node
- Python
curl https://api.tensorrail.com/payments/pay_N5c… \
-H "api-key: rail_full_test_xxx"
const response = await fetch("https://api.tensorrail.com/payments/pay_N5c…", {
headers: { "api-key": "rail_full_test_xxx" },
});
import requests
response = requests.get(
"https://api.tensorrail.com/payments/pay_N5c…",
headers={"api-key": "rail_full_test_xxx"},
)
Test mode lives in the key
There is no separate sandbox host. The mode is encoded in the key:
| Secret | Publishable | |
|---|---|---|
| Test | rail_full_test_… | rail_open_test_… |
| Live | rail_full_live_… | rail_open_live_… |
Test-mode and live-mode data never mix: payments you create with a test key never appear in
your live balance, reporting, or settlements. Presenting a key whose mode does not match the
resource is rejected with a mode-mismatch error (ERR_3051), which is your safety net against
an environment loading the wrong key.
Scopes
Each key carries scopes that gate access. On the Reporting API (the /v1
surface, the /v1/… endpoints), a request whose key lacks the required
scope returns 403 (ERR_1004).
| Scope | Grants |
|---|---|
payments:read | View payments, transactions, CSV exports and your account audit log |
payments:write | Create and confirm payments |
refunds:read | View refunds |
refunds:write | Create refunds |
settlements:read | View balances |
analytics:read | Run analytics reports |
disputes:read | View disputes |
disputes:write | Respond to disputes |
payouts:read | View payouts: reserved; the payout API is not part of the merchant API |
payouts:write | Create payouts: reserved; the payout API is not part of the merchant API |
webhooks:read | View webhook configuration |
webhooks:write | Retry a webhook delivery. Changing the endpoint or rotating its secret is a dashboard action, not an API call |
customers:read | View customers |
customers:write | Create and update customers |
compliance:read | No API call uses it: compliance documents are managed in the dashboard (see Go live) |
compliance:write | No API call uses it: compliance documents are managed in the dashboard (see Go live) |
routing:read | View routing configuration |
routing:write | Change routing configuration: reserved; not granted by default |
connectors:read | View the payment method configuration on your account |
connectors:write | Change payment method configuration: reserved; not granted by default |
settlements:write | Initiate settlement actions: reserved; not granted by default |
api_keys:manage | No API call uses it: keys are created, rotated and revoked in the dashboard (see API key management) |
A key can carry payouts:read / payouts:write, but money-out is not exposed on the merchant
API: /v1/payouts and /v1/settlements* return 404. settlements:read is live and is
what GET /v1/balances requires. Payouts and settlement
requests are handled by your account manager; the scopes exist so that keys do not need reissuing
when the surface opens.
New keys include the 19 default scopes listed above. Contact your account manager for
additional scopes (for example routing:write). The machine-readable form is
GET /v1/scopes; see API key management.
Authentication errors you will meet
All auth failures use the standard error body and stable codes; branch on error.code:
| Code | Meaning | Usual cause |
|---|---|---|
ERR_1002 | Missing or invalid API key | The api-key header was not sent, or its value is wrong: a truncated paste, or a rotated or revoked key still deployed. Payment endpoints return this in both cases; ERR_1001 is emitted only on /v1/*, where auth is Authorization: Bearer. |
ERR_1003 | Expired API key | A key past its validity still in use |
ERR_3051 | Mode mismatch | Test key against a live resource or vice versa |
ERR_2002 | Invalid parameter | For example a secret-key confirm that also passes client_secret |
Treat any ERR_1xxx as a configuration alert, never something to retry in a loop.
Browser flows and client_secret
For anything in the browser, use the publishable key together with the payment's
client_secret (returned when you create the payment). A confirm from the browser uses the
publishable key plus client_secret. A secret-key call that also passes client_secret is
rejected (ERR_2002): keep secret keys server-side and pair the publishable key with the
client_secret client-side.
Handle the client_secret like a short-lived credential: pass it to the paying customer's
session only, do not log it, and do not store it beyond the checkout.
A browser confirm also needs its page's origin enabled for your account: TensorRail checks the
Origin your browser sends against the origins enabled for your account, and a confirm run from
any other origin gets no browser access to the response. The hosted checkout page is enabled for
every account, so it works without any setup on your side. For a confirm from your own pages,
origins are enabled on request through the account team.
Keeping keys safe
- Never ship a secret key to the browser, a mobile binary, or source control. Load it from an environment variable or secret store.
- Manage, create, and rotate keys from the dashboard under Developers → API Keys. Rotation issues a new key so you can cut over without downtime; sensitive key operations in the dashboard require step-up verification.
- If a key is exposed, use Emergency revoke on its row: it stops the key immediately and issues a replacement. Rotation keeps the old key working for 24 hours, so keep it for routine hygiene. Then check your webhook deliveries and payment list for anything unexpected.
- Use different keys per environment and never point staging at live keys;
ERR_3051exists to catch exactly this, but the goal is to never see it.
See Testing and sandbox for the build-then-go-live key workflow.
TensorRail, Limassol, Cyprus.