Terminal API Overview
The Terminal API lets your POS system accept card-present transactions on compatible card readers. Customers swipe, dip, or tap their card, and the API returns a payment account token and the authorized amount. You don’t need Runner.js to tokenize card-present payments.
There are two ways to take a payment:
- Read, then charge. Call Initiate a card read to tokenize the card, then send the token and amount to the Payments API Charge endpoint.
- Charge in one call. Call Charge a card to read the card, optionally capture a signature, charge, and optionally print and email a receipt.
Supported devices
Capabilities are reported per terminal. Check the capabilities object from List terminals rather than relying on the device model.
Setup
Send all UAT requests to integrations@runpayments.io.
Authentication
All endpoints require a Payments API key as a bearer token:
Use your api_key and refresh_token to request a fresh key from Refresh API Keys.
ISV tokens
If your bearer token is an ISV token acting on behalf of a merchant, pass that merchant’s mid as a query parameter (for Send a receipt, it goes in the request body). If the mid can’t be resolved to an authorized account, the request returns 400.
Rate limiting
All Terminal API endpoints share a per-credential rate limit. Requests over the limit return 429 Too Many Requests.
How interactions work
read-card and charge-card are asynchronous. Each returns 202 Accepted with an interaction_id right away, while the terminal prompts the cardholder. Interactions expire 3 minutes after they’re created.
Start the interaction
Call POST /api/terminal/{device_id}/read-card or POST /api/terminal/{device_id}/charge-card. The terminal session connects automatically if one isn’t active.
Real-time updates over WebSocket
Instead of polling, you can receive status updates over WebSocket:
- Call Generate WebSocket auth token to get a single-use token. It expires after 60 seconds.
- Connect to
wss://javelin.runpayments.io/cable?token=<token>. - Subscribe to the channel
terminal_job:{interaction_id}.
Example: read a card
When you use charge-card, a declined card still returns status: "succeeded", because the workflow completed as requested. Always check result.result (A = Approved, B = Retry, C = Declined) before showing an approval. See Get charge status.