Skip to content
BaxPayDevelopers

In-store integration

Terminal & Session API

Your till already knows the basket total. BaxPay is just the tender it calls — and the card and the fingerprint never touch your system.

The Session API is how a point-of-sale system — a supermarket ECR, a fuel forecourt controller, a kiosk — hands a sale to a BaxPay terminal and gets a result back. Your system stays the source of truth for the basket. BaxPay owns the money and the authentication.

You call us

Your till opens a session with the amount. BaxPay drives the terminal — prompts, biometric, PIN — and returns the outcome.

Cardholder data stays out

PANs and fingerprints flow between the terminal and BaxPay only. Your system never sees them, so your PCI scope stays small.

Idempotent by design

Every session carries your reference. Retries reconcile to the same sale instead of double-charging.

Authenticating as a connection

In-store calls don't use your API key. They use a connection credential, a bxc_ key provisioned per till or lane (see connections). Every path below starts with https://sandbox-api.baxpay.com/v1/integration.

Authorization: Bearer bxc_q7Vd2kP9xR4mT1sN…

The connection decides which terminal gets the sale (its terminalId) and the mode: a connection created with a bx_test_ key only ever opens test sessions.

Open a session

POST/integration/sessionsIdempotency-Key required
curl -X POST https://sandbox-api.baxpay.com/v1/integration/sessions \
  -H "Authorization: Bearer $BAXPAY_CONNECTION" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "saleReferenceId": "till-04-receipt-99182",
    "amountMinor": 4730,
    "currency": "EUR"
  }'
{
  "success": true,
  "data": {
    "sessionId": "4fba7ed1-…",
    "saleReferenceId": "till-04-receipt-99182",
    "livemode": false,
    "status": "CREATED",
    "type": "SALE",
    "amountMinor": 4730,
    "capturedAmountMinor": null,
    "currency": "EUR",
    "resultCode": null,
    "resultDetail": null,
    "authCode": null,
    "maskedInstrument": null,
    "final": false,
    "updatedAt": "2026-07-16T14:02:10.000Z"
  }
}
FieldRequiredMeaning
saleReferenceIdyesYour receipt or basket id, 1–128 characters. Opening a second session with the same reference on the same connection returns the existing session, never a second charge
amountMinoryesPositive integer in minor units (4730 = €47.30)
currencynoISO code, default EUR
typenoSALE (default) or PREAUTH for fuel
methodHintnoA hint for the terminal's first screen

BaxPay queues the sale for the connection's terminal, which prompts the shopper and collects the authentication factors: the same SCA gate as any other BaxPay payment.

Follow the outcome

GET/integration/sessions/{id}poll
GET/integration/sessions/by-reference/{saleReferenceId}find it by your own reference
curl https://sandbox-api.baxpay.com/v1/integration/sessions/4fba7ed1-… \
  -H "Authorization: Bearer $BAXPAY_CONNECTION"
{ "success": true, "data": { "sessionId": "4fba7ed1-…", "status": "CREATED", "final": false } }

Poll until final is true, then act on status:

statusFinalMeaning
CREATEDnoReceived, waiting for the terminal
AWAITING_CUSTOMERnoThe terminal is showing the amount
AUTHENTICATINGnoThe shopper is authenticating
AUTHORIZEDnoFunds reserved (fuel pre-auth); capture next
CAPTUREDyesPaid: the only outcome that means money moved
DECLINEDyesRefused; resultCode says why (DECLINED, TIMEOUT, …)
VOIDEDyesYou voided it before capture
EXPIREDyesNobody completed it within 3 minutes of opening; open a new session
FAILEDyesSomething broke on our side; open a new session

If your till crashed or timed out and lost the session id, look it up by your own reference. The lookup only sees sessions opened by the calling connection:

curl https://sandbox-api.baxpay.com/v1/integration/sessions/by-reference/till-04-receipt-99182 \
  -H "Authorization: Bearer $BAXPAY_CONNECTION"
{ "success": true, "data": { "sessionId": "4fba7ed1-…", "saleReferenceId": "till-04-receipt-99182" } }

Keep references URL-safe (no /), or URL-encode them in this path.

Void before capture

The shopper walked away? Void the session. Voiding works until the session is final; a captured, declined or already voided session answers 409.

POST/integration/sessions/{id}/void
curl -X POST https://sandbox-api.baxpay.com/v1/integration/sessions/4fba7ed1-…/void \
  -H "Authorization: Bearer $BAXPAY_CONNECTION"
{ "success": true, "data": { "sessionId": "4fba7ed1-…", "status": "VOIDED", "resultCode": "ABORTED", "final": true } }

There is no refund call on the connection API. A captured in-store payment is refunded from the Business dashboard.

In sandbox, drive a session to CAPTURED, DECLINED or a timeout yourself with /simulate.

Two topologies

Localyou poll

Your controller polls GET /integration/sessions/{id} until final: true. Lowest latency, and it keeps working through a WAN blip.

Cloudwe call you

Each time the session reaches an outcome (authorized, captured, declined, voided), BaxPay POSTs the session view to the connection's callbackUrl, signed with its callbackSecret using the same HMAC scheme as webhooks (X-BaxPay-Signature-V2). The body is the session view itself, without a type envelope.

Who calls what

The till only needs the calls above. POST /integration/terminal/claim-next, /integration/sessions/{id}/biometric and /integration/sessions/{id}/pin are called by BaxPay terminals, not by your system.

A connection reaches every session of its merchant in its mode (by id), not only its own. Revoking one connection cuts that till off, but treat every bxc_ credential as able to read and void any of your sessions.