StoreRail API Gateway
The gateway runs on a PC in the restaurant. Your kiosk sends it an order; it records the order, stages it on NCR Aloha, waits for your app to take payment, then releases it to the kitchen. Your app never talks to the POS.
Connecting
Kiosks reach the gateway at https://<gateway host>:8443 on the store LAN.
- TLS trust. Trust only the store’s internal CA root. Pilot stores may pin the certificate’s public key instead.
- Strict JSON. camelCase, case-sensitive. Unknown or duplicate properties return
VALIDATION_FAILED. - Money is a decimal string in major units, for example
"43.50". Never a JSON number. - Correlation. Every response carries
X-Correlation-Id. Log it with every error.
GET /health/ready to show an “ordering unavailable” screen: it returns 200 when ready and 503 when not.Signing requests
Every /v1/orders request is signed with RFC 9421 HTTP Message Signatures, using the device’s hardware-backed P-256 key. The device presents its licence in X-Device-License. The gateway — not the app — decides whether a device may order.
| Header | Value |
|---|---|
X-Device-License | Compact JWS licence issued for this device and store |
Content-Digest | SHA-256 of the exact body bytes you send |
Signature-Input | Covered components, created, a fresh nonce, keyid, alg="ecdsa-p256-sha256" |
Signature | ES256 signature over the signature base |
createdmust be within 300 seconds of the gateway clock.- A nonce is accepted once. Generate a new one for every request, including retries.
- Sign the bytes you send. Don’t re-serialize the JSON after computing the digest.
Order and payment flow
The gateway stages the order on the POS without sending it to the kitchen. Your app charges the POS-computed total, then releases the order.
paymentReference is your provider’s transaction reference, 1–40 printable characters. Values shaped like a card number are rejected./v1/orders
Submit order intent. The reply waits up to 3 seconds for staging. Each order gets its own POS check.
Example values are fictional. Cart limits: quantity 1–99, up to 20 modifiers per line, notes up to 40 characters, and at most 40 POS entries per cart.
/v1/orders/{orderId}/release
Record the payment and send the order to the kitchen. The body accepts exactly three properties.
A release retried with identical details returns the current order and charges nothing again. Different details return IDEMPOTENCY_CONFLICT.
Order status
Read with GET /v1/orders/{orderId}. The response reflects the gateway ledger, never raw POS errors.
| status | What your app does |
|---|---|
| pending | Poll. Do not charge. |
| awaitingPayment | Charge total, then release. Or cancel. |
| releasing | Poll. Do not charge again. |
| reconciling | Poll. Do not charge, resubmit or reverse. |
| completed | Done. Show the order as confirmed. |
| failed | Do not charge. If a payment was taken, reverse it. |
| cancelled | If a payment was taken, reverse it. |
attention is set, tell the guest staff will help.Retries and idempotency
- Use an HTTP timeout of at least 10 seconds.
- Retry a submit with the same key and same body. A recorded order returns
200withIdempotency-Replay: true. - Never reuse a key for a different order — that returns
IDEMPOTENCY_CONFLICT. - Every retry is a new signed request with a fresh nonce.
Errors
Every error is an RFC 9457 Problem Details body (application/problem+json). Branch on code, never on title. Always log correlationId.
| HTTP | code | What to do |
|---|---|---|
| 400 | VALIDATION_FAILED | Fix the request. The idempotency key is not consumed. |
| 401 | DEVICE_NOT_AUTHENTICATED | Check the kiosk clock and signing code. |
| 403 | LICENSE_INVALID | The operator must issue a new licence. |
| 403 | LICENSE_EXPIRED | The operator must renew the licence. |
| 403 | DEVICE_NOT_AUTHORIZED | License this device for this store. |
| 404 | ORDER_NOT_FOUND | No order with this id for this device. |
| 409 | IDEMPOTENCY_CONFLICT | Use a new key for a new order. |
| 409 | PAYMENT_AMOUNT_MISMATCH | Nothing released. Charge exactly the total. |
| 409 | ORDER_STATE_INVALID | If you already charged, reverse the payment. |
| 409 | ORDER_RECONCILING | Do not reverse or release again. Poll. |
| 503 | POS_UNAVAILABLE | Nothing recorded. Retry later with the same key. |
| 500 | INTERNAL_ERROR | Retry safely; report the correlationId. |
Health
GET /healthProcess liveness. Unauthenticated.GET /health/readyReady to accept orders: the POS preflight has passed. 200 or 503.