Terminal API
Connect in-person devices to NeroPay and build POS payment flows.
Terminal API map
| Method | Endpoint | Purpose |
|---|---|---|
| GET | /locations |
List merchant terminal locations. |
| POST | /locations |
Create a terminal location. |
| PATCH | /locations/{id} |
Update a terminal location where enabled. |
| GET | /readers |
List merchant readers. |
| POST | /readers |
Register a reader. |
| DELETE | /readers/{id} |
Remove a reader. |
| POST | /terminal/readers/{id}/cancel-action |
Cancel the current action on a reader. |
| POST | /terminal/payments |
Send a terminal payment request. |
Terminal resources are returned only for the authenticated merchant or selected connected account. Developers should store returned neropay_location_id and neropay_reader_id for future payments and support workflows.
NeroPay-Account header. Without this header, requests apply to the authenticated merchant account.Authentication
All Terminal API requests use your NeroPay API secret key. For write requests, include a timestamp, signature and idempotency key to prevent duplicate device or payment actions.
| Header | Required | Description |
|---|---|---|
Authorization |
Yes | Bearer secret API key. |
Accept |
Yes | Use application/json. |
Content-Type |
For POST/PATCH | Use application/json when sending JSON body data. |
X-NeroPay-Timestamp |
For write requests | Unix timestamp used when creating the request signature. |
X-NeroPay-Signature |
For write requests | HMAC signature for the request body. |
Idempotency-Key |
For write requests | A unique key for each create, update, delete or payment request. |
NeroPay-Account |
Optional | Connected account ID, for example NP6454f52b_6165. |
Create a location
Create a location before registering a reader. A location should represent a real store, branch, counter or payment area. The returned id is the local NeroPay API resource ID, while neropay_location_id is the terminal location reference used for reader registration and terminal payments.
Endpoint
POST https://eu.neropay.app/v2/locations
Request parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
display_name |
string | Yes | Location name shown in the dashboard, for example Main Store. |
line1 |
string | Yes | First address line. |
city |
string | Yes | City or town. |
postal_code |
string | Yes | Postal code. |
country |
string | Yes | Two-letter country code, for example GB. |
is_default |
boolean | Optional | Set to true when this should be the default terminal location. |
Example PHP request
'Main Store',
'line1' => '1 Demo Street',
'city' => 'London',
'postal_code' => 'SW1A 1AA',
'country' => 'GB',
'is_default' => true,
];
$body = json_encode($payload, JSON_UNESCAPED_SLASHES);
$timestamp = time();
$signature = hash_hmac('sha256', $timestamp . '.' . $body, $secretKey);
$ch = curl_init($baseUrl . '/locations');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . $secretKey,
'Accept: application/json',
'Content-Type: application/json',
'X-NeroPay-Timestamp: ' . $timestamp,
'X-NeroPay-Signature: ' . $signature,
'Idempotency-Key: loc_' . bin2hex(random_bytes(12)),
// Optional for NeroConnect platforms:
// 'NeroPay-Account: NP6454f52b_6165',
],
CURLOPT_POSTFIELDS => $body,
]);
$response = curl_exec($ch);
curl_close($ch);
echo $response;
Example response
{
"success": true,
"data": {
"id": 14059,
"display_name": "Main Store",
"line1": "1 Demo Street",
"city": "London",
"postal_code": "SW1A 1AA",
"country": "GB",
"is_default": true,
"account_id": "NPEE4E742AB2_6165",
"neropay_location_id": "tml_Gex0cASDFxDhTL",
"created_at": "2026-04-27T19:54:44+01:00",
"updated_at": "2026-04-27T19:54:44+01:00"
}
}
Register a reader
Register a physical reader using its registration code. The reader must be registered against an existing NeroPay terminal location. Store the returned id and neropay_reader_id; the id can be used for resource operations, while neropay_reader_id is used when sending terminal actions.
Endpoint
POST https://eu.neropay.app/v2/readers
Request parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
label |
string | Yes | A friendly reader name, for example Front counter. |
location_id |
integer or string | Yes | The local location id or the neropay_location_id returned from the location API. |
registration_code |
string | Yes | The registration code displayed on the physical reader. |
Example PHP request
'Front counter',
'location_id' => 14059,
'registration_code' => 'simulated-wpe',
];
$body = json_encode($payload, JSON_UNESCAPED_SLASHES);
$timestamp = time();
$signature = hash_hmac('sha256', $timestamp . '.' . $body, $secretKey);
$ch = curl_init($baseUrl . '/readers');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . $secretKey,
'Accept: application/json',
'Content-Type: application/json',
'X-NeroPay-Timestamp: ' . $timestamp,
'X-NeroPay-Signature: ' . $signature,
'Idempotency-Key: rdr_' . bin2hex(random_bytes(12)),
],
CURLOPT_POSTFIELDS => $body,
]);
$response = curl_exec($ch);
curl_close($ch);
echo $response;
Example response
{
"success": true,
"data": {
"id": 884,
"label": "Front counter",
"location_id": 14059,
"account_id": "NPEE4E742AB2_6165",
"neropay_location_id": "tml_Gex0cASDFxDhTL",
"neropay_reader_id": "tmr_Y8mVq9K2pR4x",
"status": "online",
"created_at": "2026-04-27T20:02:10+01:00"
}
}
Create a terminal payment
Use a terminal payment when you want to send an amount to a registered reader. The transaction is created as pending first. When the customer completes or cancels the reader action, the transaction status updates accordingly and webhook events can be delivered to your application.
Endpoint
POST https://eu.neropay.app/v2/terminal/payments
Request parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
amount |
decimal | Yes | Amount in major currency units, for example 12.50. |
currency |
string | Yes | Three-letter currency code, for example GBP. |
reader_id |
integer or string | Yes | Local reader id or neropay_reader_id. |
location_id |
integer or string | Optional | Local location id or neropay_location_id. |
description |
string | Optional | Short description shown in transaction reporting. |
reference |
string | Optional | Your internal order, sale or receipt reference. |
connect_application_fee |
decimal | Optional | NeroConnect platform fee amount. Only available when acting for a connected account. |
Example PHP request
12.50,
'currency' => 'GBP',
'reader_id' => 'tmr_Y8mVq9K2pR4x',
'location_id' => 'tml_Gex0cASDFxDhTL',
'description' => 'In-store card payment',
'reference' => 'POS-1001',
];
$body = json_encode($payload, JSON_UNESCAPED_SLASHES);
$timestamp = time();
$signature = hash_hmac('sha256', $timestamp . '.' . $body, $secretKey);
$ch = curl_init($baseUrl . '/terminal/payments');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . $secretKey,
'Accept: application/json',
'Content-Type: application/json',
'X-NeroPay-Timestamp: ' . $timestamp,
'X-NeroPay-Signature: ' . $signature,
'Idempotency-Key: termpay_' . bin2hex(random_bytes(12)),
],
CURLOPT_POSTFIELDS => $body,
]);
$response = curl_exec($ch);
curl_close($ch);
echo $response;
Example response
{
"success": true,
"data": {
"id": 6636598,
"trx": "NP-TERM-8f39d21",
"status": "pending",
"amount": "12.50",
"currency": "GBP",
"reader_id": "tmr_Y8mVq9K2pR4x",
"location_id": "tml_Gex0cASDFxDhTL",
"reference": "POS-1001",
"description": "In-store card payment",
"account_id": "NPEE4E742AB2_6165",
"created_at": "2026-04-27T20:10:31+01:00"
}
}
Cancel a reader action
Cancel the current action on a reader, such as a payment that is waiting for customer interaction. Use this when a customer changes their mind, the sale is abandoned, or the POS needs to reset the reader.
Endpoint
POST https://eu.neropay.app/v2/terminal/readers/{id}/cancel-action
Example PHP request
true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . $secretKey,
'Accept: application/json',
'Content-Type: application/json',
'X-NeroPay-Timestamp: ' . $timestamp,
'X-NeroPay-Signature: ' . $signature,
'Idempotency-Key: cancel_' . bin2hex(random_bytes(12)),
],
CURLOPT_POSTFIELDS => $body,
]);
$response = curl_exec($ch);
curl_close($ch);
echo $response;
Example response
{
"success": true,
"data": {
"reader_id": "tmr_Y8mVq9K2pR4x",
"status": "cancelled",
"message": "Reader action cancelled."
}
}
Example error responses
Errors use a consistent JSON structure. Do not parse error messages for business logic; use the error.code value.
Missing authentication
{
"success": false,
"error": {
"code": "unauthenticated",
"message": "A valid API key is required."
}
}
Invalid connected account
{
"success": false,
"error": {
"code": "account_not_found",
"message": "Connected account was not found."
}
}
Invalid reader registration code
{
"success": false,
"error": {
"code": "reader_registration_failed",
"message": "The reader could not be registered. Check the registration code and try again."
}
}
Reader not found
{
"success": false,
"error": {
"code": "reader_not_found",
"message": "Reader was not found for this merchant."
}
}
Validation error
{
"success": false,
"error": {
"code": "validation_error",
"message": "The given data was invalid.",
"fields": {
"amount": [
"The amount field is required."
],
"reader_id": [
"The reader_id field is required."
]
}
}
}
Implementation notes
- Use
GET /locationsandGET /readersbefore creating duplicates. - Use one unique
Idempotency-Keyper write request. - Store both local IDs and NeroPay IDs in your POS system.
- For connected account integrations, send
NeroPay-Accountwith every request that should act on a connected merchant. - Never send raw card data to the Terminal API. Terminal payments are completed on the reader.