Skip to content
MCP Server

Initiating a test transaction over REST

REST is the simplest way to initiate a test: one HTTPS call, authenticated with an API key — the call itself uses no mTLS. Completing the transaction is a separate matter: the outbound leg to your fintech backend rides an mTLS TCP connection that your backend must already hold open as the responder, so a full round trip also needs a client certificate and a live connection.

Complete Before you begin first. For a structured transaction call you need an sk_live_… API key; a configured behaviour for the card is optional and only shapes the simulated outcome — without one the engine returns its normal faithful response. To exercise the full round trip you also need your fintech backend connected over mTLS as the responder — see Delivery needs a live TCP connection below; without it the call returns 502.

This page is the narrative walkthrough. For the machine-readable contract — request/response schemas and every error body, for every operation and channel — see the API reference, generated from src/openapi/switchbench.yaml. Keep the two in sync when either changes.

Switchbench’s REST transaction API is a family of routes, one per operation and acceptance channel — for example POST /transaction/sale/pos or POST /transaction/reversal/ecom. You send typed JSON fields and Switchbench builds the ISO 8583 wire message for you; there is no hand-packed hex to assemble. Every route shares the same authentication, rate limiting, and RFC 7807 error shape; only the request/response bodies differ. Visa only today — a mastercard request returns 501 Not Implemented until Mastercard structured support lands.

POST https://api.switchbench.com/transaction/sale/pos

Non-production environments use a single-level environment prefix on the host — for example https://test-api.switchbench.com/transaction/sale/pos. Every route is fronted by nginx behind a Cloudflare Tunnel; the path and payload are identical across environments.

Every route from the tables below is documented individually, with its own request/response schema and example, in the API reference under Transaction.

Send your API key as a bearer token:

Authorization: Bearer sk_live_...

JWT bearer tokens are rejected with 401 on every structured transaction route. A CPS-validated API key is required. A Logto-issued JWT — even a valid one carrying an organization_id claim — is refused here, because SMP cannot yet map a Logto org to the numeric org_id server-side without trusting a caller-supplied id. Use the API key.

The API key is a Control Plane Service (CPS) product credential, not an OIDC token.

Only these operation × channel combinations are registered as routes. Any other combination — for example sale/atm or cash-withdrawal/ecom — is a plain 404, not a validation error, because the route was never registered:

Operationposcontactlessmagstripeecomecom-cofmotoatm
saleXXXXXX-
adviceXXXXXXX
cash-withdrawalXX----X
balance-inquiryXX----X
refundXXXXXX-
reversalXXXXXX-
voidXXXXXX-

Three routes have no {channel} path segment because the channel is fixed:

RouteNotes
POST /transaction/account-verificationA zero-amount sale; always the e-commerce use case internally.
POST /transaction/pin/changeAlways ATM.
POST /transaction/pin/unblockAlways ATM.

POST /transaction/pin/check/{channel} is card-present/ATM only — pos, contactless, or atm.

POST /transaction/network-management/{action} takes one of echo, sign-on, sign-off, key-exchange as {action}; it is a control message with no channel, card, or amount.

The financial and PIN routes share one request body. The route carries the operation and channel, so the body does not:

FieldRequiredNotes
networknoTarget network. Defaults to visa, the only network supported today; any other value (including mastercard) returns 501. A network whose product the organization is not entitled to returns 403.
envelopenoAcceptance context: currency (DE 49, ISO 4217 alphabetic or numeric), merchant_id (DE 42), and terminal_id (DE 41). Each field you leave out is filled from your merchant/terminal/currency catalog.
amount.amountdepends on the routeTransaction amount in minor units (DE 4). Required for sale, refund, cash-withdrawal, reversal, void, and advice (except a balance-inquiry advice). Ignored for balance-inquiry, account-verification (both sent as amount 0), and the PIN routes.
amount.feenoTransaction fee in minor units (DE 28). Only sale sends it; every other route ignores it, including account-verification.
cardyesSee Card fields.
original_refnoReference to an earlier message, for reversal, advice, and void. refund ignores it, so a refund is not linked to the original sale. See the reversal/ecom example.
typenoAdvice sub-type for the advice routes: sale, cash-withdrawal, balance-inquiry, or refund. Ignored by every other route.
new_pinpin/change onlyThe new clear PIN digits. Required for POST /transaction/pin/change; ignored by every other route.

POST /transaction/network-management/{action} takes a smaller body: network as above, plus an optional type — the DE 70 network-management code for key-exchange (3 or 4 digits, for example 101; defaults to the key-change-request code). type is ignored for echo, sign-on, and sign-off.

The API reference has the full schema for both bodies.

The card object is the same shape for every channel — there is no channel-specific variant on the public API today:

FieldRequiredNotes
panyesPrimary account number (DE 2).
expiryyesCard expiry as MMYY (DE 14).
cvv2noCard verification value 2.
cardholder_namenoFor the card acceptor record.
pinnoClear PIN digits. Only consumed by pin-check, pin-change (the current PIN), and pin-unblock; ignored by every other operation.

There is no field for track 2 data, EMV/ICC data, or an encrypted PIN block on any channel, including pos/contactless/magstripe/atm — SMP binds card as this one shape regardless of channel, so those fields would be silently discarded if sent.

A USD 10.00 sale on a contact-chip POS terminal:

Terminal window
curl -X POST https://test-api.switchbench.com/transaction/sale/pos \
-H "Authorization: Bearer sk_live_your_key_here" \
-H "Content-Type: application/json" \
-d '{
"amount": {
"amount": 1000
},
"card": {
"pan": "4111111111111111",
"expiry": "1229",
"cvv2": "123"
}
}'

Response shape on success (200 OK) — a declined transaction (parsed_response’s DE 39 ≠ "00") is still 200, not an HTTP error:

{
"request_id": "req_abc123",
"sent": "0200B23A80010000000000004111111111111111000000001000",
"parsed_request": {
"mti": "0200",
"fields": {
"02 (Primary Account Number)": "4111111111111111",
"03 (Processing Code)": "000000",
"04 (Amount, Transaction)": "000000001000"
}
},
"last_response_hex": "0210B23A80010000000000004111111111111111000000001000C1B2C300",
"parsed_response": {
"mti": "0210",
"fields": {
"02 (Primary Account Number)": "4111111111111111",
"38 (Authorization Identification Response)": "C1B2C3",
"39 (Response Code)": "00"
}
}
}

A reversal of a prior e-commerce sale, referencing the original transaction by its DE 11 (STAN), DE 37 (RRN), and DE 38 (approval code) via original_ref:

Terminal window
curl -X POST https://test-api.switchbench.com/transaction/reversal/ecom \
-H "Authorization: Bearer sk_live_your_key_here" \
-H "Content-Type: application/json" \
-d '{
"amount": {
"amount": 1000
},
"card": {
"pan": "4111111111111111",
"expiry": "1229",
"cvv2": "123"
},
"original_ref": {
"original_stan": "000123",
"original_rrn": "123456789012",
"original_auth_code": "C1B2C3"
}
}'
{
"request_id": "req_def456",
"sent": "0420323A800100000000000041111111111111110000000010000000000123123456789012",
"parsed_request": {
"mti": "0420",
"fields": {
"02 (Primary Account Number)": "4111111111111111",
"04 (Amount, Transaction)": "000000001000",
"90 (Original Data Elements)": "0100000123"
}
},
"last_response_hex": "0430323A800100000000000041111111111111110000000010000000000123123456789012C1B2C300",
"parsed_response": {
"mti": "0430",
"fields": {
"02 (Primary Account Number)": "4111111111111111",
"38 (Authorization Identification Response)": "C1B2C3",
"39 (Response Code)": "00"
}
}
}

Errors use RFC 7807 application/problem+json.

StatusMeaning
400Malformed request — the body is not valid JSON or does not match the schema, or card is missing or has an empty pan.
401Authentication failed — missing, invalid, revoked, or suspended API key, or a JWT was sent.
404The operation × channel combination is not in the capability matrix and was never registered as a route.
403The organization is not entitled to the product for the requested network — no active trial or subscription. Checked after the 501 network check, and again per frame at delivery.
429Rate limit exceeded — the API key’s requests-per-second limit, or the trial’s per-product requests-per-second cap (type: trial_rate_limited). A Retry-After header is returned.
501Unsupported network — only visa is supported today.
502Delivery error — no active mTLS connection for the org, or a write/read failure (a delivery timeout included) on the outbound leg to your fintech backend; also an engine HTTP error or an undecodable engine response. Engine-side input validation failures (for example a missing amount or PIN) also surface here as Engine error, not 400.
503API-key validation with CPS is unavailable, so the tenant could not be resolved; or the entitlement check at delivery could not be confirmed (type: entitlement_check_unavailable). Retry the request.
504The Visa engine call itself timed out, hit an open circuit breaker, or failed at the transport level.

A structured transaction call only originates the authorization. The outbound leg to your fintech backend is written over the mTLS TCP connection your backend established through the FRP edge, acting as the responder. That connection is a separate concern from the API key: it needs a client certificate and must be open at the moment you call the route.

If no live connection is registered for your org when the call arrives, SMP has nowhere to deliver the outbound leg: the call fails with 502 (delivery error) and the authorization is never completed. A pure-REST caller that has set up only an API key hits exactly this.

To exercise the full round trip, open a live mTLS TCP connection first — see Visa ISO 8583 over mTLS TCP for the connection and wire contract — then send your transaction request.

  • The API key authenticates the call — nothing more.
  • Completing the transaction needs your fintech backend connected over mTLS as the responder: a client certificate and a live TCP connection.
  • No live connection for the org → 502 delivery error, before any authorization is finalized.
  • Typed JSON fields, not hand-packed ISO 8583 hex — one route per operation and acceptance channel, e.g. POST /transaction/sale/pos or POST /transaction/reversal/ecom.