Skip to navigation

Terminal API Overview

Accept card-present payments on integrated card readers.

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

DeviceSignature captureReceipt printing
Clover FlexYesYes
Clover MiniYesYes
Clover Flex PocketYesNo
Clover CompactYesNo
Ingenico Link 2500NoNo
Ingenico Lane 3600NoNo
Ingenico Lane 7000YesNo
Ingenico Lane 8000YesNo

Capabilities are reported per terminal. Check the capabilities object from List terminals rather than relying on the device model.

Setup

Request a development account

Send all UAT requests to integrations@runpayments.io.

VariableValue
Base URLhttps://javelin.runpayments.io
device_idThe terminal’s Hardware Serial Number (HSN), provided by your Integration Delivery lead.

Authentication

All endpoints require a Payments API key as a bearer token:

Authorization: Bearer <api_key>

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.

1

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.

2

Get the result

Poll the matching status endpoint until status is no longer pending, or subscribe over WebSocket for real-time updates.

3

Handle the outcome

For read-card, send result.token and the amount to the Payments API. For charge-card, check result.result for approval.

Real-time updates over WebSocket

Instead of polling, you can receive status updates over WebSocket:

  1. Call Generate WebSocket auth token to get a single-use token. It expires after 60 seconds.
  2. Connect to wss://javelin.runpayments.io/cable?token=<token>.
  3. Subscribe to the channel terminal_job:{interaction_id}.

Example: read a card

async function readCard(deviceId, apiKey, amount) {
const response = await fetch(
`https://javelin.runpayments.io/api/terminal/${deviceId}/read-card`,
{
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${apiKey}`,
},
body: JSON.stringify({ amount, confirm_amount: true }),
}
);
if (response.status !== 202) {
throw new Error(`Card read failed to start: ${response.status}`);
}
// Interaction queued. Poll or subscribe for the result.
const { interaction_id } = await response.json();
return interaction_id;
}
A decline isn't a failure

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.