# NeroPay API reference

Source: https://docs.neropay.app/api/
Last updated: 2026-10-19T13:02:49+00:00

API reference

Environment

Filter sections

API v2 10-2026

API referenceIntroduction

Examples

API documentation

NeroPay API v2

# Build with NeroPay

[View Markdown ↗](https://docs.neropay.app/api/reference.md)

Build hosted payments, connected accounts, money movement, NeroDisburse, Terminal, NeroCard, NeroTrade, NeroPOS, NeroWeb, bookings, identity, compliance, proof workflows, support tickets and webhooks from one v2 API.

**/v2**Live base path

**HMAC**Signed writes

**Products**Payment, POS, Web, Card, Trade, Disburse

**Risk-ready**KYC, compliance, proof, reserve

[**NeroConnect Partner API →**Partner accounts, pricing and commission collection](https://docs.neropay.app/api/#neroconnect-partner)[**NeroConnect Sales Agent API →**Agent profiles, merchant assignments and reports](https://docs.neropay.app/api/#neroconnect-sales-agents)

## Quick start

Use the live v2 base URL for production. Sandbox endpoints are dry-run and do not create live payment, terminal, payout or local records.

https://eu.neropay.app/v2

### Merchant mode

Use a merchant secret key to operate directly on that merchant account. Identity, compliance, proof, payments, support and resources are available for direct merchant integrations.

### Platform mode

Use the platform key. Send `NeroPay-Account` to operate on a connected merchant. Keep your own customer-facing dashboard while NeroPay manages regulated flows.

**Recommended platform flow:** create connected account, create identity verification link, listen for KYC/compliance/proof webhooks, handle support tickets and only release marketplace UX once account and reserve states are safe.

Base request

```
curl https://eu.neropay.app/v2/account \
  -H "Authorization: Bearer neropay_secret_..." \
  -H "Accept: application/json"
```

Connected account request

```
curl https://eu.neropay.app/v2/balances \
  -H "Authorization: Bearer neropay_secret_platform_..." \
  -H "NeroPay-Account: NPVesdTGASO23_547878" \
  -H "Accept: application/json"
```

Updated 16 Sep 2026 

## Ready-to-Use Plugins

Use official NeroPay plugins when you want a faster launch without building every checkout, invoice, webhook and order status workflow manually.

[### NeroPay for WooCommerce

Accept card and wallet payments directly in WooCommerce, receive real-time status updates and support refunds from the store workflow. Download link is inside the section.

Open WooCommerce section →](https://docs.neropay.app/api/#plugin-woocommerce) [### NeroPay for Wix

Install NeroPay from the official Wix marketplace, configure public keys and redirect URLs, then test with sandbox checkout.

Open Wix section →](https://docs.neropay.app/api/#plugin-wix) [### NeroPay for Odoo

Connect NeroPay to Odoo eCommerce, Website and Invoicing apps using the Odoo payment provider flow. Odoo 16, 17, 18 and 19 app links are inside.

Open Odoo section →](https://docs.neropay.app/api/#plugin-odoo) [### NeroPay for Perfex CRM

Enable invoice payments, IPN callbacks and reconciliation inside Perfex CRM without building a custom gateway. Module download is inside.

Open Perfex CRM section →](https://docs.neropay.app/api/#plugin-perfex) [### NeroPay for Zapier

Create payment links and automate payment workflows between NeroPay and thousands of apps without code through Zapier.

Open Zapier section →](https://docs.neropay.app/api/#plugin-zapier) [Direct install · RC1

### NeroPay for OpenCart 3

Download directly from NeroPay. Marketplace not required. Hosted checkout; staging acceptance required.

Installation and download →](https://docs.neropay.app/api/#plugin-opencart) [Direct install · RC1

### NeroPay for PrestaShop

Download directly from NeroPay. Marketplace not required. Hosted checkout; staging acceptance required.

Installation and download →](https://docs.neropay.app/api/#plugin-prestashop) [Direct install · RC1

### NeroPay for WHMCS

Download directly from NeroPay. Marketplace not required. Hosted checkout; staging acceptance required.

Installation and download →](https://docs.neropay.app/api/#plugin-whmcs) [Direct install · RC1

### NeroPay for Magento 2 / Adobe Commerce

Download directly from NeroPay. Marketplace not required. Hosted checkout; staging acceptance required.

Installation and download →](https://docs.neropay.app/api/#plugin-magento)

Plugins are recommended for standard store, invoice and no-code workflows. Use the API Reference when you need full platform control, connected accounts, custom onboarding, identity, compliance, proof and support flows.

Choose integration type

```
{
  "simple_storefront": "WooCommerce or Wix",
  "erp_or_invoice_flow": "Odoo or Perfex CRM",
  "automation": "Zapier",
  "custom_platform": "NeroPay API v2 + NeroConnect"
}
```

## NeroPay for WooCommerce

Use the WooCommerce plugin to add secure NeroPay checkout to a WordPress store without building the hosted-payment, order-status and webhook flow from scratch.

[Download WooCommerce plugin](https://docs.neropay.app/contents/docs/medias/neropay_woocommerce_plugin_v2.6_final.zip)[Use Payments instead](https://docs.neropay.app/api/#hosted-payments)

**Plugin package**

Download the WooCommerce ZIP package and upload it from WordPress Admin → Plugins → Add New, or upload it manually by FTP.

### Key features

- Quick setup from the WordPress plugin area.
- Secure processing through NeroPay APIs.
- Real-time payment status updates through webhooks.
- Refund support from the WooCommerce workflow where enabled.

### Best for

Retail stores that need a standard checkout integration, payment confirmation, order reconciliation and optional advanced API checks.

**1. Install and activate**Upload the NeroPay WooCommerce plugin through WordPress Admin or FTP, then activate it from Plugins.

**2. Configure credentials**Open WooCommerce → Settings → Payments → NeroPay and enter your API key, merchant details and secret key.

**3. Confirm webhooks**Keep the webhook/IPN URL reachable so WooCommerce can move orders from pending to paid after NeroPay confirms payment.

| Setting | Type | Required | Description |
| --- | --- | --- | --- |
| public_key | string | Required | Identifies the merchant account. |
| secret_key | string | Required | Server-side key used by the plugin for secure API actions. |
| webhook_url | string | Required | Receives payment success, failure or pending updates. |
| currency | enum | Required | Store currency such as GBP or EUR. |

Typical WooCommerce payment

```
POST /v2/hosted-payments
{
  "identifier": "wc_order_1042",
  "amount": 49.99,
  "currency": "GBP",
  "details": "WooCommerce order #1042",
  "success_url": "https://store.example/checkout/order-received/1042",
  "cancel_url": "https://store.example/checkout/pay/1042",
  "ipn_url": "https://store.example/wp-json/neropay/webhook"
}
```

Order paid webhook

```
{
  "type": "transaction.succeeded",
  "data": {
    "transaction": {
      "identifier": "wc_order_1042",
      "status": "succeeded",
      "amount": 49.99,
      "currency": "GBP"
    }
  }
}
```

## NeroPay for Wix

Use the Wix integration for a fast marketplace-style setup. Configure keys, redirect URLs and IPN URL, then send customers to NeroPay checkout.

[Open official Wix app](https://wix.to/vLFERhz)[Use hosted payment links](https://docs.neropay.app/api/#payment-links)

**Marketplace installation**

Install NeroPay from the official Wix marketplace link, then configure Public Key, success URL, cancel URL and IPN URL.

**1. Install from the marketplace**Install the official NeroPay app, then connect it to your NeroPay merchant account.

**2. Add API keys**Copy the Public Key and Secret Key from the NeroPay dashboard. Keep secret keys out of browser code.

**3. Configure URLs**Set success, cancel and IPN URLs so payment status can return to the store.

**4. Test in sandbox**Use sandbox mode before switching to live keys and live checkout.

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| public_key | string | Required | NeroPay public key configured in the Wix app. |
| success_url | string | Required | Page shown after successful payment. |
| cancel_url | string | Required | Page shown when checkout is cancelled. |
| ipn_url | string | Required | Endpoint that receives payment confirmation. |

For custom Wix backend flows, create hosted payments server-side. Browser-only flows should never expose the secret key.

Frontend checkout request

```
const payload = {
  public_key: "neropay_public_...",
  identifier: "WEB-" + Date.now(),
  currency: "GBP",
  amount: 100.00,
  details: "Purchase via NeroPay",
  ipn_url: "https://yourdomain.com/ipn",
  success_url: "https://yourdomain.com/success",
  cancel_url: "https://yourdomain.com/cancel",
  customer_name: "John Doe",
  customer_email: "john@example.com"
};
```

IPN signature check

```
const expected = crypto
  .createHmac("sha256", secret)
  .update(data.amount + identifier)
  .digest("hex")
  .toUpperCase();
```

## NeroPay for Odoo

Choose the module matching your Odoo major version. The Odoo 19 native provider supports hosted online checkout and customer invoice payments.

[Odoo 16 app](https://apps.odoo.com/apps/modules/16.0/neropay_payment)[Odoo 17 app](https://apps.odoo.com/apps/modules/17.0/neropay_payment)[Odoo 18 app](https://apps.odoo.com/apps/modules/18.0/neropay_payment)[Odoo 19 app](https://apps.odoo.com/apps/modules/19.0/neropay_payment)

[Download Odoo 19 ZIP · RC1](https://docs.neropay.app/pluginsV2/neropay-odoo19.zip)

### Odoo 19 requirements

- Odoo 19.0 with custom Python add-on support: self-hosted or Odoo.sh.
- Not installable on Odoo Online saas-19.4.
- HTTPS, Python requests, the API v2 secret and exact receiving account ID.

### Odoo 19 scope

- Website / eCommerce and customer invoice hosted payments.
- Verified payment status and scheduled recovery.
- No POS terminal, automatic subscription debit, initiated refunds or settlement reconciliation.

**1. Install matching version**Add neropay_payment to the add-ons path, install dependencies, restart Odoo and update the Apps list.

**2. Configure provider**Open Accounting → Configuration → Payment Providers → NeroPay. Set credentials, receiving account and clearing journal.

**3. Test and enable**Odoo 19 is a release candidate. Perform native installation and authorised live acceptance testing before production use. Its Test state remains intentionally blocked in this plugin release; do not assume compatibility with the Developer sandbox.

Odoo 16–18 are separate existing packages. Do not install an Odoo 19 module into an older version or assume all versions have identical capabilities.

Odoo 19 module layout

```
custom_addons/
  neropay_payment/
    __manifest__.py
    controllers/
    models/
    services/
    static/
    views/
```

## NeroPay for Perfex CRM

Enable NeroPay as a payment gateway inside Perfex CRM so clients can pay invoices online and Perfex can update invoice status from IPN callbacks.

[Download Perfex CRM module](https://docs.neropay.app/contents/docs/medias/2025/02/perfex_neropay.zip)[Invoice API reference](https://docs.neropay.app/api/#invoices)

**Perfex module package**

Download the module ZIP, extract it into the Perfex `modules` directory, then enable NeroPay Payment Gateway from Setup → Payments.

### Features

- Online invoice payments.
- GBP and EUR support where enabled.
- Instant Payment Notification updates.
- Success and failure redirects.
- API credentials managed inside Perfex CRM.

### Prerequisites

- Running Perfex CRM instance.
- NeroPay merchant account with API credentials.
- Admin access to Perfex CRM.

**1. Upload module**Extract the NeroPay payment module and upload it to the Perfex modules directory.

**2. Enable gateway**Go to Setup → Payments → Payment Gateways and enable the NeroPay gateway.

**3. Configure credentials**Enter public and secret API keys and define supported currencies.

**4. Verify IPN**Perfex should listen at {your_perfex_url}/neropay_gateway/ipn and mark invoices paid after verification.

If invoices do not update, confirm the IPN URL, secret key and server logs before retrying payment creation.

Perfex invoice payment

```
POST /v2/hosted-payments
{
  "identifier": "perfex_invoice_8731",
  "amount": 120.00,
  "currency": "GBP",
  "details": "Perfex CRM invoice #8731",
  "success_url": "https://crm.example/invoices/8731?paid=1",
  "cancel_url": "https://crm.example/invoices/8731",
  "ipn_url": "https://crm.example/neropay_gateway/ipn"
}
```

IPN handling result

```
if signature_is_valid:
    record_payment(invoice_id)
    mark_invoice_as_paid()
else:
    log_invalid_ipn()
```

## NeroPay for Zapier

Automate payment workflows between NeroPay and 5,000+ apps without code. Merchants can trigger workflows when a payment is received or create payment links from tools such as Sheets and CRMs.

[Open Zapier integration](https://zapier.com/apps/neropay/integrations)[Payment link API](https://docs.neropay.app/api/#payment-links)

**No-code automation**

Use Zapier to create payment links, log transactions, notify teams and sync payment events with CRMs, spreadsheets and 5,000+ apps.

**1. Connect account**Log in to NeroPay, copy your Public API Key and paste it into the Zapier connection window.

**2. Choose mode**Select Sandbox or Live mode depending on whether the workflow should test or process real payment links.

**3. Create action**Use the Create Payment Request action to return a checkout URL instantly.

**4. Automate follow-up**Send payment notifications to Slack, log rows in Sheets, update CRM records or create invoices automatically.

| Action field | Type | Required | Description |
| --- | --- | --- | --- |
| public_key | string | Required | NeroPay Public API key. |
| identifier | string | Required | Unique ID for the payment. |
| currency | enum | Required | Currency code such as GBP, EUR or IDR. |
| amount | decimal | Required | Payment amount. |
| details | string | Required | Description or order details. |
| ipn_url | string | Required | Instant payment notification URL. |
| customer_phone | string | Optional | Customer phone number. |
| customer_address | string | Optional | Billing address. |

Zapier action response

```
{
  "success": "ok",
  "message": "Payment Initiated. Redirect to url.",
  "url": "https://eu.neropay.app/checkout?payment_id=EJX123"
}
```

Example automations

```
- Generate a payment link when a form is submitted
- Log each transaction in Google Sheets
- Send Slack or email notifications for successful payments
- Create invoices or notes in a CRM
```

Direct install · Pilot release RC1 

## NeroPay for OpenCart 3

Hosted checkout with server-side payment verification and missed-notification recovery. Marketplace installation is not required.

[Download OpenCart 3](https://docs.neropay.app/pluginsV2/neropay-opencart3.ocmod.zip)

**Target environment**

OpenCart 3.0.3.x / 3.0.4.x; default store; PHP 7.4–8.3 as supported by the store.

**1. Install**Back up the store, then open Extensions → Installer and upload the .ocmod.zip package.

**2. Configure**Open Extensions → Extensions → Payments. Install NeroPay API v2, then open Edit.

**3. Review**Enter the receiving account ID and API v2 secret. Choose an unpaid Pending status and a paid Processing / Complete status.

**4. Verify**Configure the recovery schedule shown on the settings page. Enable real payments only after acceptance testing.

**Scope:** Not an OpenCart 4 package. Default store only; recurring profiles are not supported. Custom one-page checkout modules need separate testing.

**Validation status:** local API-contract and payment-lifecycle checks are included in the release validation, but these packages have not completed full installation and live payment acceptance tests on native stores. Use staging first; this is not a production-certification claim.

Direct download

```
https://docs.neropay.app/pluginsV2/neropay-opencart3.ocmod.zip
```

Payment safety

```
1. Save the order and payment intent.
2. Create one signed hosted checkout.
3. Reuse the existing checkout after a decline.
4. Verify merchant, reference, amount and currency.
5. Record payment once, only on verified success.

An IPN or browser redirect alone is not proof of payment.
Check this plugin release for sandbox support before using test credentials. Sandbox outcomes never confirm a real paid order.
```

Direct install · Pilot release RC1 

## NeroPay for PrestaShop

Hosted checkout with server-side payment verification and missed-notification recovery. Marketplace installation is not required.

[Download PrestaShop](https://docs.neropay.app/pluginsV2/neropay-prestashop.zip)

**Target environment**

PrestaShop 8.x / 9.x API target; single shop; single-package carts; platform-compatible PHP 7.4–8.3.

**1. Install**Back up the shop. Open Modules → Module Manager → Upload a module, then select this ZIP.

**2. Configure**Configure NeroPay API v2 in Module Manager. Enter the receiving account ID and API v2 secret.

**3. Review**Review payment currency, customer-group, country and carrier restrictions. Configure the recovery schedule displayed by the module.

**4. Verify**Complete acceptance tests before enabling real payments. Checkout creates an unpaid order before redirecting.

**Scope:** Single-shop only. Split-shipment / multi-package carts are blocked. The module is configured in Module Manager even if it is not listed in the marketplace payment catalogue.

**Validation status:** local API-contract and payment-lifecycle checks are included in the release validation, but these packages have not completed full installation and live payment acceptance tests on native stores. Use staging first; this is not a production-certification claim.

Direct download

```
https://docs.neropay.app/pluginsV2/neropay-prestashop.zip
```

Payment safety

```
1. Save the order and payment intent.
2. Create one signed hosted checkout.
3. Reuse the existing checkout after a decline.
4. Verify merchant, reference, amount and currency.
5. Record payment once, only on verified success.

An IPN or browser redirect alone is not proof of payment.
Check this plugin release for sandbox support before using test credentials. Sandbox outcomes never confirm a real paid order.
```

Direct install · Pilot release RC1 

## NeroPay for WHMCS

Hosted checkout with server-side payment verification and missed-notification recovery. Marketplace installation is not required.

[Download WHMCS](https://docs.neropay.app/pluginsV2/neropay-whmcs.zip)

**Target environment**

Self-hosted WHMCS 8.x / 9.x gateway API target; PHP 7.4–8.3 as supported by the installed WHMCS version.

**1. Install**Extract the ZIP and upload its modules and includes directories into the WHMCS installation root. Preserve existing files.

**2. Configure**Open System Settings → Payment Gateways and activate NeroPay API v2.

**3. Review**Enter the receiving account ID and API v2 secret. Do not enable WHMCS gateway currency conversion for this release.

**4. Verify**Keep the standard WHMCS cron running. Customers must sign in as the invoice owner; an unpaid invoice must use NeroPay as its payment method.

**Scope:** Pays the current invoice balance. No stored-card capture, automatic recurring debit or automatic refund operation. Invoice changes after checkout creation require manual review; the connector will not create a second link.

**Validation status:** local API-contract and payment-lifecycle checks are included in the release validation, but these packages have not completed full installation and live payment acceptance tests on native stores. Use staging first; this is not a production-certification claim.

Direct download

```
https://docs.neropay.app/pluginsV2/neropay-whmcs.zip
```

Payment safety

```
1. Save the order and payment intent.
2. Create one signed hosted checkout.
3. Reuse the existing checkout after a decline.
4. Verify merchant, reference, amount and currency.
5. Record payment once, only on verified success.

An IPN or browser redirect alone is not proof of payment.
Check this plugin release for sandbox support before using test credentials. Sandbox outcomes never confirm a real paid order.
```

Direct install · Pilot release RC1 

## NeroPay for Magento 2 / Adobe Commerce

Hosted checkout with server-side payment verification and missed-notification recovery. Marketplace installation is not required.

[Download Magento 2 / Adobe Commerce](https://docs.neropay.app/pluginsV2/neropay-magento2.zip)

**Target environment**

Magento Open Source / Adobe Commerce 2.4.x API target; standard Magento checkout; PHP 8.1–8.3 subject to the platform version.

**1. Install**Back up the site and database, then extract app/code/NeroPay/Payments into the Magento root in a development/staging environment.

**2. Configure**Run the Magento module enable and setup commands below as the Magento filesystem owner, not as root.

**3. Review**Open Stores → Configuration → Sales → Payment Methods → NeroPay API v2. Enter the receiving account ID and API v2 secret.

**4. Verify**Keep Magento cron running. Order currency must equal the website base currency, and must be GBP, EUR or USD.

**Scope:** Standard Knockout/Luma-style checkout only. Hyvä Checkout, PWA/GraphQL, express checkout and custom checkout implementations are not included. No vault, automatic refunds or recurring billing.

**Validation status:** local API-contract and payment-lifecycle checks are included in the release validation, but these packages have not completed full installation and live payment acceptance tests on native stores. Use staging first; this is not a production-certification claim.

Magento setup commands

```
bin/magento module:enable NeroPay_Payments
bin/magento setup:upgrade
bin/magento setup:di:compile
bin/magento setup:static-content:deploy -f
bin/magento cache:flush
```

Payment safety

```
1. Save the order and payment intent.
2. Create one signed hosted checkout.
3. Reuse the existing checkout after a decline.
4. Verify merchant, reference, amount and currency.
5. Record payment once, only on verified success.

An IPN or browser redirect alone is not proof of payment.
Check this plugin release for sandbox support before using test credentials. Sandbox outcomes never confirm a real paid order.
```

## Authentication

**Subscription writes:** `/v2/subscriptions*` uses a signature that binds the method, path, retry key, account and exact JSON bytes. Follow the [subscription signing examples](https://docs.neropay.app/api/#subscriptions-api); the legacy timestamp.body examples below do not apply to these routes.

**New management writes:** [NeroConnect Management & Partners](https://docs.neropay.app/api/#neroconnect-management) uses a method/path/idempotency-bound HMAC message, not the legacy timestamp.body format. Follow that section's signing and durable retry examples when calling `/v2/neroconnect/management/*`.

API v2 uses bearer authentication. Unsafe methods also require a timestamped HMAC signature and an idempotency key.

| Header | Type | Required | Description |
| --- | --- | --- | --- |
| Authorization | string | Required | `Bearer {secret_key}`. |
| Accept | string | Required | Always `application/json`. |
| Content-Type | string | Required | Use `application/json` for JSON writes or multipart/form-data for file upload endpoints. |
| X-NeroPay-Timestamp | unix timestamp | Required | Required for POST/PATCH/PUT/DELETE. |
| X-NeroPay-Signature | string | Required | HMAC SHA-256 of `timestamp.raw_body` using the secret key. |
| Idempotency-Key | string | Required | Required for writes that create or mutate state. |

Never expose secret keys in browser code. Use server-side calls for signed writes.

Node.js signature

```
const timestamp = Math.floor(Date.now() / 1000).toString();
const rawBody = JSON.stringify(payload);
const signature = crypto
  .createHmac('sha256', secretKey)
  .update(`${timestamp}.${rawBody}`)
  .digest('hex');
```

Signed POST headers

```
POST /v2/hosted-payments
Authorization: Bearer neropay_secret_...
Accept: application/json
Content-Type: application/json
Idempotency-Key: order_1001_attempt_1
X-NeroPay-Timestamp: 1777411120
X-NeroPay-Signature: 9ea0...
```

## Headers

Headers control JSON responses, connected account scoping, public/client key context and retry safety.

| Header | Type | Required | Description |
| --- | --- | --- | --- |
| NeroPay-Account | string | Optional | Connected account ID such as NPVesdTGASO23_547878. Platforms use it to act on a connected merchant. |
| X-NeroPay-Client-Key | string | Optional | Dashboard-visible account client key. If supplied, it must match the authenticated merchant or the NeroPay-Account connected account id. |
| X-NeroPay-Public-Key | string | Optional | Public key from API credentials. |
| Idempotency-Key | string | Required | Unique per business operation. Reuse only for exact retries. |

Connected account scoping works with payments, balances, identity, compliance, proof, support and resource APIs where enabled.

Connected account header

```
curl https://eu.neropay.app/v2/account \
  -H "Authorization: Bearer neropay_secret_platform_..." \
  -H "NeroPay-Account: NPVesdTGASO23_547878" \
  -H "X-NeroPay-Client-Key: NPVesdTGASO23_547878" \
  -H "Accept: application/json"
```

## Pagination

List endpoints use page-based pagination when the underlying controller returns a collection.

| Query | Type | Default | Description |
| --- | --- | --- | --- |
| page | integer | 1 | Page number. |
| per_page | integer | 25 | Number of records per page. |
| q/search | string | — | Search by supported columns. |
| status | enum | — | Filter by status where supported. |
| from / to | date/datetime | — | Filter by created date where supported. |
| sort | string | -id | Use leading minus for descending order where supported. |

Pagination appears inside `meta.pagination`. Some single-object APIs return only `data`.

Pagination response

```
{
  "success": true,
  "data": {
    "account_id": "NPVesdTGASO23_547878",
    "requests": []
  },
  "meta": {
    "pagination": {
      "current_page": 1,
      "per_page": 25,
      "total": 0,
      "last_page": 1
    }
  }
}
```

## Errors

Errors return a consistent JSON object with an application code and human-readable message. Provider terminology is normalised to NeroPay-safe wording before it is returned.

| HTTP | Code | Meaning |
| --- | --- | --- |
| 400 | bad_request | Malformed request. |
| 400 | missing_idempotency_key | A required Idempotency-Key header is missing. |
| 401 | unauthenticated | Missing or invalid API key. |
| 401 | invalid_signature | Write request signature verification failed. |
| 403 | forbidden | The authenticated account cannot access the resource. |
| 404 | endpoint_not_found / not_found | Route or object was not found. |
| 405 | resource_read_only | Resource exists but the requested write method is not enabled. |
| 409 | idempotency_conflict | The same idempotency key was reused with a different body. |
| 409 | connected_account_duplicate | A connected account with the same email or username already exists. |
| 409 | duplicate_resource | A resource with the same unique fields already exists for this account. |
| 422 | validation_error | Invalid fields. |
| 429 | rate_limited | Too many requests. |
| 500 | resource_scope_misconfigured | The resource is not safely account-scoped on this installation. |

### Authentication and account scope errors

| HTTP | Code | Meaning |
| --- | --- | --- |
| 401 | missing_credentials | No secret key was supplied. |
| 401 | invalid_credentials | The secret key was not recognised. |
| 401 | account_key_mismatch | The supplied public key, client key or account id does not match the authenticated account context. |
| 401 | invalid_signature | The HMAC signature is missing, malformed or invalid. |
| 403 | account_not_allowed | The authenticated account cannot access the supplied connected account. |
| 403 | account_inactive | The merchant account is not active. |
| 403 | neroconnect_not_enabled | NeroConnect platform access is not enabled for this account. |
| 404 | account_not_found | The connected account id was not found for this platform. |
| 422 | invalid_account_id | Use the full NeroPay account id format, for example NPVesdTGASO23_547878. |

### Connected account errors

| HTTP | Code | Meaning |
| --- | --- | --- |
| 409 | connected_account_duplicate | A connected account with the same email or username already exists. |
| 422 | connected_account_create_failed | The connected account could not be created. |
| 422 | invalid_idempotency_key | The supplied idempotency key has an invalid format. |
| 409 | duplicate_request | A request with the same idempotency key is still being processed. |

### Payment and transaction errors

| HTTP | Code | Meaning |
| --- | --- | --- |
| 403 | account_banned | The account cannot process the requested operation. |
| 403 | payments_paused | The account cannot accept payments right now. |
| 422 | currency_not_supported | The requested currency is not enabled or supported. |
| 422 | invalid_connect_application_fee | The platform fee is invalid or exceeds the transaction amount. |
| 422 | not_cancelable | The transaction can no longer be cancelled. |
| 422 | not_refundable | The transaction is not eligible for refund. |
| 422 | invalid_refund_amount | The refund amount must be greater than zero. |
| 422 | refund_exceeds_transaction | The requested refund exceeds the original transaction amount. |
| 422 | insufficient_balance | The wallet balance is not enough to complete the operation. |
| 422 | provider_reference_missing | The provider payment reference is missing. |
| 422 | cancel_failed / refund_failed | The cancellation or refund could not be processed. |

### NeroTrade errors

| HTTP | Code | Meaning |
| --- | --- | --- |
| 402 | nerotrade_subscription_required | NeroTrade is not active for this account. Activate the NeroTrade plan before calling NeroTrade endpoints. |
| 404 | endpoint_not_found | The requested NeroTrade API path under https://eu.neropay.app/v2/nerotrade was not found. |
| 404 | unknown_nerotrade_resource | The requested NeroTrade resource name is not recognised. |
| 404 | resource_unavailable | The requested NeroTrade table or resource is not available on this installation. |
| 404 | not_found | The requested NeroTrade record was not found for this merchant. |
| 404 | customer_not_found | The trade account supplied for an invoice, delivery, payment or statement could not be found. |
| 404 | invoice_not_found | The invoice supplied for a payment could not be found. |
| 405 | use_products | Inventory records should be created as products first, then stock can be updated through the inventory endpoint. |
| 422 | empty_payload | No writable NeroTrade fields were supplied for an update request. |
| 422 | validation_error | A required field is missing or a supplied field is invalid. |
| 503 | nerotrade_schema_missing | NeroTrade database tables are not installed or the NeroTrade SQL update has not been applied. |

### Terminal errors

| HTTP | Code | Meaning |
| --- | --- | --- |
| 404 | location_not_found | The location was not found for this account. |
| 422 | location_required | A location id is required. |
| 422 | location_bridge_missing | The location is not ready for terminal reader registration. |
| 409 | reader_already_registered | The location already has a linked reader. |
| 422 | reader_limit_reached | The account has reached its reader limit. |
| 422 | invalid_registration_code | The reader registration code is missing or invalid. |
| 404 | reader_not_found | The reader does not belong to this account. |
| 422 | reader_register_failed | The reader could not be registered. |
| 422 | terminal_payment_failed | The terminal payment could not be created. |
| 422 | reader_cancel_failed | The reader action could not be cancelled. |
| 500 | provider_not_configured | The NeroPay payment bridge is not configured. |

### Payout and NeroDisburse errors

| HTTP | Code | Meaning |
| --- | --- | --- |
| 403 | payouts_paused | Transfers are paused for this account. |
| 403 | kyc_required | KYC verification is required before transfers. |
| 404 | bank_account_not_found | The bank account was not found for this merchant. |
| 422 | bank_account_required | A bank account id is required. |
| 422 | bank_account_disabled | The bank account is disabled. |
| 409 | duplicate_bank_account | The bank account is already linked to this merchant. |
| 422 | bank_account_create_failed | The bank account could not be created. |
| 404 | payout_not_found | The payout was not found for this account. |

Validation error

```
{
  "success": false,
  "error": {
    "code": "validation_error",
    "message": "The amount field is required.",
    "details": {
      "amount": ["The amount field is required."]
    }
  }
}
```

NeroTrade subscription error

```
HTTP/1.1 402 Payment Required
{
  "success": false,
  "error": {
    "code": "nerotrade_subscription_required",
    "message": "NeroTrade is not active for this account. Activate the £99.95/month NeroTrade plan before using NeroTrade API endpoints."
  }
}
```

NeroPay account id error

```
HTTP/1.1 422 Unprocessable Entity
{
  "success": false,
  "error": {
    "code": "invalid_account_id",
    "message": "Use the full NeroPay account id format, for example NPVesdTGASO23_547878, or send the numeric connected merchant_id for legacy integrations."
  }
}
```

NeroTrade endpoint error

```
HTTP/1.1 404 Not Found
{
  "success": false,
  "error": {
    "code": "endpoint_not_found",
    "message": "The requested NeroTrade API endpoint was not found. Use https://eu.neropay.app/v2/nerotrade with a supported record endpoint."
  }
}
```

NeroTrade not found error

```
HTTP/1.1 404 Not Found
{
  "success": false,
  "error": {
    "code": "not_found",
    "message": "NeroTrade resource was not found."
  }
}
```

Immediate payment error

```
HTTP/1.1 422 Unprocessable Entity
{
  "success": false,
  "error": {
    "code": "terminal_payment_failed",
    "message": "Reader is currently offline. Please make sure the reader is powered on, connected to the internet and try again."
  }
}
```

## Idempotency

**Subscriptions:** completed writes replay their saved response. Preserve the same key and data after an uncertain result, and refresh the signature. See [subscription retries and limits](https://docs.neropay.app/api/#subscriptions-api) for key scope and review requirements.

Use an idempotency key for each write operation that creates or changes financial state. All write requests must also include a valid HMAC signature.

### Good

`order_1001_payment_attempt_1` reused only when retrying the same body.

### Avoid

Do not reuse one key for different payments, payouts, refunds or proof submissions.

Safe retry

```
curl https://eu.neropay.app/v2/payouts \
  -X POST \
  -H "Authorization: Bearer neropay_secret_..." \
  -H "Idempotency-Key: payout_1001_attempt_1" \
  -H "Content-Type: application/json" \
  -d '{"amount":1.00,"currency":"GBP","bank_account_id":"ba_123"}'
```

## Account API

Retrieve the authenticated merchant account, create connected merchants and manage core NeroConnect onboarding state.

GET`/account`

Returns the authenticated merchant or the connected merchant when NeroPay-Account is supplied.

GET`/connected-accounts`

Lists connected merchants for a NeroConnect platform.

### GET response attributes

| Attribute | Type | Required | Description |
| --- | --- | --- | --- |
| account_id | string | Read-only | Public API account ID. |
| business_company_name | string | Optional | Company or trading name. |
| email | string | Required | Primary merchant email. |
| status | enum | Read-only | active, pending, banned or review state. |
| kyc_status | enum | Read-only | pending, verified or rejected. |
| neroconnect_email_sms_handle | enum | Optional | neropay or platform. Controls merchant-facing notification delegation. |
| neroconnect | object | Read-only | Returned for NeroConnect platform responses. Includes fee payer, platform safety limit, merchant pricing and default connect application fee context. |
| neroconnect.fee_payer | enum | Read-only | `connected_account` or `platform`. Controls where NeroPay processing fees are charged for connected account payments. |
| neroconnect.platform_safety_limit | object\|null | Read-only | Returned when the fee payer is platform balance. `null` when the connected account pays NeroPay processing fees. |
| neroconnect.merchant_pricing | object | Read-only | Shows API, card and keyed/manual fee rates that apply to the connected merchant. In platform balance mode, platforms may manage merchant-specific pricing in the dashboard. |

POST`/connected-accounts`

Creates a connected merchant. Terms acceptance, authorised legal name and the supported-country fields are required before the account is created.

### POST /connected-accounts request attributes

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `business_company_name` | string | Required | Company or trading name for the connected merchant. |
| `firstname` | string | Required | Authorised representative first name. |
| `lastname` | string | Required | Authorised representative last name. |
| `email` | email | Required | Primary email for the connected merchant. |
| `country_code` | enum | Required | Supported NeroPay country code such as GB. Must be selected from the supported-country list, not typed freely. |
| `mobile_code` | string | Required | Dial code for the selected country, for example 44 for GB. Should be supplied from the supported-country list. |
| `mobile` | string | Required | Representative or merchant contact mobile number. |
| `business_type` | enum | Required | Business structure such as company, individual or sole_trader where supported. |
| `nationality` | enum | Required | Representative nationality country code from the supported-country list. |
| `company_registration_number` | string | Required for companies | Company registration number where the business type requires it. |
| `representative_job_title` | string | Required | Role/title of the authorised representative, for example Director. |
| `rep_is_director` | boolean | Required | Confirms whether the representative is a company director. |
| `rep_is_executive` | boolean | Required | Confirms whether the representative is an executive/control person. |
| `rep_is_owner` | boolean | Required | Confirms whether the representative is an owner. |
| `rep_ownership_percent` | number | Required when owner | Ownership percentage, for example 100. |
| `company_declaration` | boolean | Required | Confirms the company declaration has been accepted. |
| `address` | string | Required | Registered or trading address line. |
| `city` | string | Required | City/town. |
| `postcode` | string | Required | Postcode. |
| `dob` | date | Required | Representative date of birth in YYYY-MM-DD format. |
| `industry` | string | Required | Industry/MCC-style code accepted by NeroPay onboarding. |
| `merchant_website_or_product_description` | text | Required | Website URL or clear description of the products/services sold. |
| `terms_accepted` | boolean | Required | Must be true. API-created connected accounts are not created without terms acceptance. |
| `terms_accepted_at` | datetime | Required | Time the connected merchant/authorised person accepted the terms. |
| `terms_ip` | ip | Required | IP address captured when terms were accepted. |
| `legal_name` | string | Required | Authorised legal name used to generate the stored electronic signature when a PNG signature is not supplied. |
| `signature_png_base64` | string\|null | Optional | Base64 PNG signature. If omitted, NeroPay stores a generated typed-signature image from legal_name. |
| `neroconnect_email_sms_handle` | enum | Optional | neropay or platform. Controls who handles merchant-facing email/SMS communication. |
| `metadata` | object | Optional | Platform reference data, external IDs or onboarding notes. |

**Activation reminder:** creating a connected account does not complete onboarding. After creation, create an Identity verification link through the NeroPay API, ensure the merchant adds a business bank account, monitor KYC/compliance webhooks and use the Compliance Proof API for proof or document requests before treating the account as fully ready.

**Legal acceptance is required:** API-created connected accounts must include terms acceptance and an authorised legal name or signature. If `terms_accepted` is not true, or the legal acceptance data is missing, the account is not created.

**Owner and director validation:** when `rep_is_owner` is true, `rep_ownership_percent` is required and must be between 25 and 100. Use `company_has_other_owners` and the optional `owners[]` array only for additional natural-person owners or directors; do not duplicate the authorised representative. Each `owners[].email` must be unique within the array and different from the connected account's primary `email`. Supported owner attributes are `first_name`, `last_name`, `email`, `dob` (YYYY-MM-DD), `percent_ownership`, `is_owner`, `is_director` and `is_executive`.

GET`/connected-accounts/{account_id}`

Retrieves one connected merchant.

PATCH`/connected-accounts/{account_id}`

Updates connected account fields and can trigger account.update webhooks.

### PATCH /connected-accounts request attributes

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `business_company_name` | string | Optional | Updates the connected merchant company/trading name. |
| `mobile` | string | Optional | Updates the connected merchant mobile number where allowed. |
| `neroconnect_email_sms_handle` | enum | Optional | neropay or platform. |
| `neroconnect_status` | enum | Optional | Dashboard/API operational status such as active, paused or review where enabled. |
| `metadata` | object | Optional | Platform reference data or internal notes. |

Create connected account request

```
POST /v2/connected-accounts
{
  "business_company_name": "Demo Connected Merchant Ltd",
  "firstname": "Jane",
  "lastname": "Merchant",
  "email": "connected.demo@example.com",
  "country_code": "GB",
  "mobile_code": "44",
  "mobile": "7700000000",
  "business_type": "company",
  "nationality": "GB",
  "company_registration_number": "12345678",
  "representative_job_title": "Director",
  "rep_is_director": true,
  "rep_is_executive": true,
  "rep_is_owner": true,
  "rep_ownership_percent": 100,
  "company_declaration": true,
  "address": "1 Demo Street",
  "city": "London",
  "postcode": "SW1A 1AA",
  "dob": "1990-01-01",
  "industry": "5812",
  "merchant_website_or_product_description": "Online retail platform merchant",
  "terms_accepted": true,
  "terms_accepted_at": "2026-06-12T10:30:00Z",
  "terms_ip": "203.0.113.42",
  "legal_name": "Jane Merchant",
  "signature_png_base64": null
}
```

Connected account response

```
{
  "success": true,
  "data": {
    "account": {
      "id": "NPVesdTGASO23_547878",
      "business_company_name": "Demo Connected Merchant Ltd",
      "email": "connected.demo@example.com",
      "status": "pending",
      "kyc_status": "pending",
      "created_at": "2026-04-28T18:00:00.000000Z",
      "neroconnect": {
        "fee_payer": "connected_account",
        "platform_safety_limit": null,
        "merchant_pricing": {
          "api_fixed_fee": 0.00,
          "api_percent_fee": 0.30,
          "card_fixed_fee": 0.00,
          "card_percent_fee": 0.30,
          "keyed_fixed_fee": 0.00,
          "keyed_percent_fee": 0.30,
          "status": "first_month_rate",
          "help": "First month NeroPay processing rate."
        },
        "connect_application_fee_defaults": {
          "fixed": 0.00,
          "percent": 0.50
        }
      }
    }
  }
}
```

## NeroConnect connected accounts

Platforms can create merchants, view balances, manage default platform fees and operate by sending the connected account header.

| Concept | Type | Description |
| --- | --- | --- |
| Connected account ID | string | Format is unique_account_id + underscore + merchant_id. |
| NeroPay-Account header | string | Scopes API calls to a connected merchant. |
| Notification handling | enum | neropay means NeroPay sends email/SMS; platform means platform handles merchant-facing notifications from webhooks. |
| KYC, bank & compliance | workflow | Must be completed after create account. Use the Identity API to create verification links, ensure a business bank account is added, and use Compliance Proof API plus webhooks for proof/document requests. |

**Activation reminder:** connected accounts should not be treated as fully ready until KYC identity verification, business bank account setup, compliance requests and reserve/proof checks are clear. Partners can create identity verification links through the NeroPay API and should use the compliance proof endpoints whenever NeroPay requests supporting evidence.

### Fee payer and pricing context

| Mode | NeroPay processing fee payer | Platform safety limit | Notes |
| --- | --- | --- | --- |
| `connected_account` | The connected merchant pays NeroPay processing fees. | Not shown. `platform_safety_limit` returns `null`. | This is the default mode. Platform balance is not debited for NeroPay processing fees. |
| `platform` | The platform balance pays NeroPay processing fees. | Shown in the API response and dashboard. | The connected merchant sees NeroPay processing fee as zero. If the platform balance or outstanding debt reaches the safety threshold, restricted platform actions may be blocked until the balance is restored. |

**Important:** NeroPay fee payer is separate from `connect_application_fee`. The fee payer setting controls who pays NeroPay processing fees. `connect_application_fee` is your platform's own application fee and can still be supplied per payment or configured as a dashboard default.

**Response compatibility:** This is an additive v2 response update. Existing requests do not need a new required parameter. Integrations should safely ignore unknown fields.

**Duplicate protection:** `POST /connected-accounts` is idempotent when an `Idempotency-Key` is supplied. Reusing the same key with the same payload returns the original result. Reusing the same key with a different payload returns `409 idempotency_conflict`. A platform cannot create another connected account with an email or username that already exists; duplicate requests return `409 connected_account_duplicate`.

Post-create activation checklist

```
1. POST /v2/connected-accounts
2. POST /v2/identity/verification-links
3. Ask the merchant to add a business bank account
4. Use /v2/compliance/requests and proof endpoints when requested
5. Listen for kyc.*, compliance.* and connected_account.* webhooks
```

Get connected account

```
GET /v2/connected-accounts/NPVesdTGASO23_547878
```

Update connected account

```
PATCH /v2/connected-accounts/NPVesdTGASO23_547878
{
  "neroconnect_status": "active",
  "metadata": {
    "source": "platform-admin"
  }
}
```

NeroConnect fee payer context

```
{
  "neroconnect": {
    "fee_payer": "platform",
    "platform_safety_limit": {
      "blocked": false,
      "limit": 500.00,
      "lowest_wallet_balance": 560.45,
      "outstanding_debt": 4.00
    },
    "merchant_pricing": {
      "api_fixed_fee": 0.00,
      "api_percent_fee": 0.30,
      "card_fixed_fee": 0.00,
      "card_percent_fee": 0.30,
      "keyed_fixed_fee": 0.00,
      "keyed_percent_fee": 0.30
    },
    "connect_application_fee_defaults": {
      "fixed": 0.00,
      "percent": 0.50
    }
  }
}
```

## NeroConnect settings, reports and account operations

API-integrated platforms use their existing API v2 credentials. These endpoints expose platform defaults, connected account records and aggregate reporting without granting access to unrelated merchants or NeroPay-controlled pricing.

GET`/neroconnect/settings`

Returns notification handling, merchant-facing pricing defaults, reward programme configuration, payout-limit defaults and read-only NeroPay cost context.

PATCH`/neroconnect/settings`

Updates allow-listed platform defaults. Partial updates are supported.

GET`/neroconnect/accounts`

Lists only connected accounts assigned to the authenticated NeroConnect platform.

GET`/neroconnect/accounts/{account_id}`

Returns one assigned connected account and its selected, NeroPay hard-cap and effective payout limits.

GET`/neroconnect/reports/overview`

Returns account counts and transaction totals for the platform’s own connected merchants.

**Security boundary:** platform API keys cannot query arbitrary merchant IDs. Account ownership is resolved server-side. NeroPay processing costs and fee-payer responsibility are read-only. Payout defaults can only tighten connected-account behaviour; the effective values never exceed the limits set by NeroPay Admin for that merchant.

**Reward scope:** API v2 can enable the connected-account reward programme and set its public name. Reward catalogue create, edit, pause and remove operations remain dashboard-only to keep the public write surface small.

### PATCH /neroconnect/settings attributes

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `notification_handle` | enum | Optional | `neropay` or `platform`. |
| `pricing_defaults.{channel}.fixed` | decimal | Optional | Fixed merchant-facing default. Channels: `api`, `card`, `manual_keyed`, `application_fee`, `sms`, `card_origin`, `extra_payment_method`, `instant_payout`, `transaction_refund`. |
| `pricing_defaults.{channel}.percent` | decimal | Optional | Percentage default, 0–100. Available for API, card, manual keyed, application fee, card origin, extra payment method and instant payout channels. |
| `rewards.enabled` | boolean | Optional | Shows or hides the platform reward programme for connected accounts. |
| `rewards.name` | string | Optional | Public programme name, 1–120 characters. |
| `payout_limit_defaults.*` | decimal/integer | Optional | Supported keys: `standard_min`, `standard_max`, `instant_min`, `instant_max`, `weekly_standard_max`, `weekly_instant_max`, `daily_transfer_count`, `nerodisburse_daily_max`. Send `null` to clear one platform default. |

Legacy `connect_application_fee_defaults.fixed` and `.percent` remain supported aliases for `pricing_defaults.application_fee`.

Update safe platform defaults

```
PATCH /v2/neroconnect/settings
Authorization: Bearer neropay_secret_...
Idempotency-Key: nc-settings-20260907-01
X-NeroPay-Timestamp: 1788757200
X-NeroPay-Signature: <hmac_sha256>
Content-Type: application/json

{
  "pricing_defaults": {
    "extra_payment_method": {"fixed": 0.10, "percent": 0.75},
    "card_origin": {"fixed": 0.25, "percent": 3.50}
  },
  "rewards": {"enabled": true, "name": "Kepos Rewards"},
  "payout_limit_defaults": {"instant_max": 2500, "daily_transfer_count": 5}
}
```

Overview report

```
GET /v2/neroconnect/reports/overview?from=2026-06-01&to=2026-06-30
```

## Partner commissions & cash-order billing

Charge a service fee per sale, track its allocation, and bill completed cash orders without a monthly subscription. Commerce endpoints use `/v2/neroconnect/commerce`.

**Card orders:** continue using `connect_application_fee` on hosted payments or terminal payments. For a GBP 10.00 payment and GBP 0.50 commission, send `amount: 10.00` and `connect_application_fee: 0.50`. Processing costs and any parent-platform share follow your existing agreement. Do not create another transfer or report that card order as cash usage.

### Choose the right operation

| Need | Use |
| --- | --- |
| Commission on a card sale | Existing payment creation with `connect_application_fee`; automatic existing settlement. |
| Track commission | `GET /commissions` or `GET /commissions/{transaction_id}`. |
| Cash-order service fee | Set signup pricing once, then `POST /usage-events` after merchant registration. |
| Move an agreed amount between parent and partner | Existing `/v2/neroconnect/management/partners/{partnerId}/funds`. This is separate from charging a connected shop for an order. |

### Authentication and signed writes

Use live server-side credentials over HTTPS. Select one connected shop with `NeroPay-Account`. Account, destination, fee, provider identifiers and credential overrides in JSON are rejected. The platform may access only its own linked shops. A shop can read its own records, stop future cash fees, and request collection of fees already due. Use the platform account (omit `NeroPay-Account`) to configure signup pricing.

Every write requires JSON, an 8–100 character `Idempotency-Key`, `X-NeroPay-Timestamp` within five minutes and `X-NeroPay-Signature`. Commerce uses the following newline-separated HMAC message; it differs from legacy payment signing:

Example

```
timestamp + "\n" + METHOD + "\n" + exact_path + "\n"
+ idempotency_key + "\n" + exact_NeroPay_Account_header + "\n" + raw_json_body
```

Sign with HMAC-SHA256 using the authenticated secret, as lowercase hexadecimal. Include the full path, e.g. `/v2/neroconnect/commerce/usage-events`. Use an empty account-header line when configuring pricing on the platform account. Write query parameters are rejected. Send exactly the body that was signed.

Retry the same operation with the same key and body, and a fresh timestamp/signature. Saved responses return `Idempotency-Replayed: true`. Reusing a key for a different path, shop or body returns 409. An order ID is also unique across pricing versions and retry keys. Keep the same completed timestamp on retries.

### 1. Set the cash-order signup price

`POST /pricing` uses the platform credentials with **no connected-account header**. It sets prices displayed to new merchants on the existing branded registration page. It does not send a contract or move money. All fields below are required:

| Field | Meaning |
| --- | --- |
| `currency` | `GBP`. |
| `fixed_minor` | Integer pence per completed cash order, 0–100000. |
| `percent_bps` | Integer basis points, 0–5000; 500 means 5%. |
| `max_order_fee_minor` | Per-order cap, 1–100000 pence; must cover the fixed fee. |
| `daily_cap_minor` | Maximum new fees recorded per shop per application-calendar day, 1–1000000 pence. Applies across agreement versions; cancellation does not replenish the cap. |
| `collection_interval` | `daily` or `weekly`. A recorded order becomes due after one or seven days. |
| `terms_reference` | Your immutable terms/version reference, 1–100 ASCII letters, digits, dots, colons, hyphens or underscores. |

Fee = fixed fee + percentage rounded to the nearest penny, capped by the agreed per-order maximum. The resulting fee must be positive and less than the order amount. At least one of fixed fee or percentage must be non-zero.

### 2. Registration activates pricing automatically

The merchant sees the fixed fee, percentage, per-order maximum, daily limit and collection schedule on the existing branded signup form. Submitting that registration records the displayed price and balance-collection authorisation automatically. There is no separate contract email, acceptance page or API acceptance step.

`GET /pricing` with the platform credentials and no account header returns the current signup default. With a connected merchant account header it returns that merchant's active pricing. Changing signup defaults affects future registrations only. A changed price invalidates an already-open signup form; the merchant must reload to see the current price.

The existing parent/EPOS partner acceptance recorded at `/neroconnect/{platform}/partner/register` is reused for parent allocation. It is separate from the shop's own registration. Cash fees are collected from the connected shop's eligible balance. Existing accounts without recorded cash pricing are not silently activated; usage returns 403. Existing compatibility agreement endpoints remain available for integrations already using them, but are not part of this signup flow.

`nc_commerce_agreements` is internal versioned pricing/registration evidence. It retains the price and parent allocation snapshot that applied to each order; it does not initiate document sending. Previously registered shops retain their accepted price. Revoking a pricing record stops new orders, while accrued fees remain due.

**Parent allocation:** the existing partner registration acceptance is retained. Revenue-share terms continue to apply. For the Rate Model, the parent must configure a **Cash order service fees** rate through existing partner pricing controls; changing that pricing does not require a second partner signup.

### 3. Record completed cash usage

`POST /usage-events` is available to the linked platform. Required fields: unique `order_id`, integer `amount_minor` (1–10000000 pence), `currency: GBP`, `payment_method: cash`, and `completed_at` as UTC `YYYY-MM-DDTHH:MM:SSZ`. The active pricing recorded at signup is selected automatically. `agreement_id` is optional for existing integrations. The order must be completed after the pricing activation, within the past 30 days, and no more than five minutes ahead.

The response contains the server-calculated fee, `state: accrued`, and `due_at`. This is a service-fee receivable, not a customer card payment or proof that cash was physically collected. Send only completed orders your merchant has authorised you to report. `GET /usage-events` lists them.

`POST /usage-events/{id}/cancel` with `{}` cancels an order fee before collection has been reserved. Once assigned to a collection it returns 409; reconcile that collection before making a fee adjustment. Card refunds continue to use the existing transaction refund endpoint.

### 4. Collection and allocation

The existing NeroConnect web cron groups due fees under their registered pricing version. Each collection contains at most 200 orders and GBP 10,000 in fees; at least GBP 1.00 is required. `POST /collections` with `{}` requests preparation of a due collection and returns 202. It cannot override the amount, recipient or due date. If none qualifies, the response has `state: not_due`.

Automatic collection currently requires **eligible UK GBP connected payment accounts**, verified active merchants, legally effective merchant authorisation, and sufficient unrestricted local and provider balance. It is a payment-balance debit, not a bank-account Direct Debit. Cash-only shops without a funded eligible payment balance remain pending; this API does not charge their customer or invent funds.

Collection costs follow the platform's existing transfer tariff and are deducted from its fee income. Parent allocation is applied before the partner net credit. `GET /collections` and `GET /collections/{id}` return the latest state:

| State | Meaning |
| --- | --- |
| `pending` | Due fees grouped; waiting for the scheduler or eligible funds. |
| `reserved` | Local balance reserved; provider collection awaits confirmation. |
| `collected` | Provider confirmed collection; allocation is still pending. |
| `settled` | Required transfers confirmed and partner income recorded. This does not mean a bank payout has arrived. |
| `review` | An earlier outcome needs reconciliation; no fresh debit is created. |

The optional `pending_reason` explains waits such as `insufficient_available_balance`, `account_not_eligible`, `balance_or_account_restriction`, `allocation_pending` or `reconciliation_required`. A lost response retains its original provider operation. An unresolved operation beyond its safe retry window stops for reconciliation. Never send replacement orders or funding requests to bypass a pending collection. Reading a collection does not retry money movement.

### Card commission reporting and events

Commission IDs are the existing numeric transaction IDs. `application_fee_minor` is the saved requested commission; `recorded_fee_minor` requires the fee ledger; `transferred_net_minor` is returned only when the exact net allocation is known. A null amount means unconfirmed or unavailable, not zero.

`allocation_state` is `pending`, `recorded`, `transferred`, `adjustment_pending`, `reversed`, `not_earned` or `review`. Refunding the customer does not itself prove a commission transfer was reversed. Existing refund and dispute handling remains authoritative.

Subscribe in your existing webhook settings to `commerce.commission.updated` and `commerce.collection.settled`. Events use the existing webhook secret and `timestamp.body` HMAC verification. Delivery is retried through the existing notification outbox; deduplicate by event ID. Public HTTPS destinations are validated and DNS-pinned on every attempt, with redirects and private networks blocked.

Commission events reflect a bounded cron sweep of payments from the past 30 days. For older payments or immediate reconciliation, retrieve the commission directly. Collection events go only to the original merchant and platform; commission events go to the original platform.

### Dashboard visibility

In Transactions, confirmed cash commission income appears in the receiving platform's own ledger. Open transaction details to see the shop, fee total, your collected income, collection state and included order references. A reserved merchant debit remains clearly marked with its collection state. Pending usage without a ledger movement is available through the usage/collection APIs rather than appearing as received income.

In NeroConnect API Logs, choose **API requests** to inspect commerce request references, HTTP results, replay status and returned operation identifiers. Registered commerce webhook attempts remain in **Webhook deliveries**. Requests never log credentials or signatures. Only scoped account records and authorised staff financial details are visible.

### Pagination, errors and limits

Paginated list endpoints (excluding `GET /pricing`) accept `limit` (1–50, default 25) and `after` from the prior `next_cursor`. Cursors are opaque except numeric commission IDs. Results are ordered by ID, not by date. Read requests have no body. All other query/body fields are rejected.

Limits: 64 KB request body, 2048-character request URI, 60 writes per minute per principal and target, 120 reads per minute, plus the existing shared IP throttle. Handle 429 using `Retry-After`. Common errors: 401 invalid signature/credentials; 403 account, consent or policy restriction; 404 scoped record not found; 409 conflicting order/key or reserved collection; 422 invalid fields/cap/date; 503 schema/provider service unavailable.

These commerce endpoints are live-only. Test keys cannot enter them. Validate your integration and use a small agreed live amount before enabling automatic cash-order billing. Existing payment sandbox endpoints remain separate.

### Card sale: GBP 10, commission GBP 0.50

```
POST /v2/terminal/payments
Authorization: Bearer PLATFORM_SECRET
NeroPay-Account: NPexample002_2
Content-Type: application/json

{
  "amount": 10.00,
  "currency": "GBP",
  "reader_id": "YOUR_MERCHANT_READER_ID",
  "reference": "CARD-1001",
  "connect_application_fee": 0.50
}
```

Use the existing Terminal API signing rules for this existing endpoint. For hosted checkout use the same fee parameter with the required [hosted payment fields](https://docs.neropay.app/api/#hosted-payments).

### Signup pricing: 50p per cash order

```
POST /v2/neroconnect/commerce/pricing
Authorization: Bearer PLATFORM_SECRET

{
  "currency": "GBP",
  "fixed_minor": 50,
  "percent_bps": 0,
  "max_order_fee_minor": 50,
  "daily_cap_minor": 5000,
  "collection_interval": "weekly",
  "terms_reference": "EPOS-CASH-2026-01"
}
```

### Read the registered merchant price

```
GET /v2/neroconnect/commerce/pricing
Authorization: Bearer PLATFORM_SECRET
NeroPay-Account: NPexample002_2
```

The merchant registration has already activated this pricing. No acceptance request is needed.

### Completed cash order

```
POST /v2/neroconnect/commerce/usage-events
NeroPay-Account: NPexample002_2

{
  "order_id": "CASH-1001",
  "amount_minor": 1000,
  "currency": "GBP",
  "payment_method": "cash",
  "completed_at": "2026-10-16T12:30:00Z"
}
```

Use the actual completion time. One GBP 10.00 cash order records a 50p service fee under this registered price; it does not trigger a GBP 10.00 card charge.

### Node.js: sign a commerce write

```
import { createHmac, randomUUID } from 'node:crypto';

const secret = process.env.NEROPAY_SECRET;
const path = '/v2/neroconnect/commerce/usage-events';
const account = 'NPexample002_2';
const key = randomUUID(); // Save with this order; reuse on retries.
const body = JSON.stringify(order);
const timestamp = String(Math.floor(Date.now() / 1000));
const message = [timestamp, 'POST', path, key, account, body].join('\n');
const signature = createHmac('sha256', secret).update(message).digest('hex');

const response = await fetch('https://eu.neropay.app' + path, {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${secret}`,
    'Content-Type': 'application/json',
    'NeroPay-Account': account,
    'Idempotency-Key': key,
    'X-NeroPay-Timestamp': timestamp,
    'X-NeroPay-Signature': signature
  },
  body
});
const result = await response.json();
if (!response.ok) throw new Error(result.error?.message || 'Request failed');
```

### Collection response

```
{
  "success": true,
  "data": {
    "id": "COLLECTION_ID",
    "currency": "GBP",
    "fee_minor": 100,
    "order_count": 2,
    "state": "pending",
    "pending_reason": "awaiting_schedule",
    "collected": false,
    "allocated": false,
    "created_at": "2026-10-23T12:30:00+00:00",
    "settled_at": null,
    "partner_net_minor": 99
  }
}
```

The example assumes a 1% collection tariff and no parent share. Your actual net follows your account tariff and accepted partner terms. `partner_net_minor` is shown only to the platform; while pending, it is the frozen expected allocation.

## Management authentication

Shared authentication, signing and retry rules for the NeroConnect Partner API and Sales Agent API. Both use the /v2/neroconnect/management prefix.

**Authority:** use the authenticated platform's own secret API key on your server. Never give it to sales agents or put it in browser/mobile code. Agent logins remain restricted panel memberships, not management API credentials. Parent controls and account moves require a main platform with NeroConnect Partner enabled; a second-tier partner cannot manage its parent, siblings or create a further tier. Child platforms can manage their own agents only when their active agreement, API, account and report policies permit it.

[**Partner API →**Partners, pricing, account moves and funding](https://docs.neropay.app/api/#neroconnect-partner)[**Sales Agent API →**Profiles, assignments and commission reports](https://docs.neropay.app/api/#neroconnect-sales-agents)

### Signed writes and retry rules

Every POST, PUT and PATCH requires Bearer authentication, `Content-Type: application/json`, an `Idempotency-Key` of 8–100 letters, digits or `._:-`, and timestamp/signature headers. Write bodies must be JSON objects, at most 64 KB, with no query parameters or uploads. Unknown fields are rejected. GET uses Bearer authentication and allow-listed query filters.

For these management endpoints only, sign the following UTF-8 bytes with HMAC-SHA256 using your secret key, returning lowercase hexadecimal. Use a Unix timestamp within five minutes. Do not reformat the JSON after signing. The exact path includes `/v2`; do not include a host, query or trailing slash. Existing API endpoints retain their existing signature format.

Management signature input

```
timestamp + "\n" + UPPERCASE_METHOD + "\n" + PATH
          + "\n" + IDEMPOTENCY_KEY + "\n" + RAW_JSON_BODY
```

A repeated HTTP retry key for the same method/path/platform returns `409 duplicate_request` for 24 hours, not a cached success response. Inspect the current resource before retrying with a new HTTP key. For a move reuse the original `preview_id`; for funding reuse the original `request_id`. These durable operation IDs prevent a second move or second debit even when the HTTP key changes. Invalid submissions also consume their HTTP retry key.

**Limits and safety:** 60 requests/minute per IP before authentication, plus 60 reads or 20 writes/minute per platform. Maximum list page size 100; commission reports cover at most 31 calendar days. Ownership, merchant status, parent policies, KYC requirements, debt restrictions and version tokens are checked server-side. No client field can change identity verification, activate a partner, override NeroPay tariffs or choose a settlement account.

### Error handling

Responses use `{"success": true, "data": ...}` with pagination `meta` where applicable, or `{"success": false, "error": {"code": ..., "message": ...}}`. Expect 401 for credentials/signatures, 403 for restricted management, 404 for unavailable/foreign resources, 409 for duplicate keys or changed controls, 413 for body size, 422 for invalid/stale fields, 429 for throttling, and 503 for unavailable setup. Treat 5xx and timeouts as an unknown result; inspect the same durable operation ID before doing anything else. Public failures do not expose SQL, credentials, stack traces or upstream exception messages.

## NeroConnect Sales Agent API

Manage your platform’s sales-agent profiles, merchant assignments and commission reports. Agent panel logins do not grant management API credentials.

Base path: `/v2/neroconnect/management` · [Authentication & signed requests ↗](https://docs.neropay.app/api/#neroconnect-management)

**Reporting and access:** all agent resources belong to the authenticated platform. Commission reports do not trigger payouts. Child platforms need the active agreement and applicable API, account and report permissions.

### Sales agents

GET`/sales-agents`

Own platform profiles. Optional `limit` (1–100) and `page` (1–10000).

POST`/sales-agents`

Create a profile for an existing active team membership belonging to this platform. Fields: integer `staff_id`, `name` (max 150), decimal-string `commission_percent` (0–100, at most two decimal places), boolean `active`. Email matching never grants authority.

PATCH`/sales-agents/{agentId}`

Send `name`, `commission_percent`, `active` and the last read integer `revision`. Do not send `staff_id` on updates. Existing historical commission periods are preserved.

PUT`/accounts/{merchantId}/sales-agent`

Assign an own connected merchant: `agent_id` (integer or null to unassign), `override_percent` (decimal string or null for the profile default), and `expected_assignment` (current assignment ID, or 0 if unassigned). All three fields are required.

GET`/sales-agents/{agentId}/accounts`

Only accounts currently assigned to this own-platform agent, with assignment IDs and rates. Optional `limit`, `page`.

GET`/sales-agents/{agentId}/commission-report`

Optional inclusive `from`/`to` in YYYY-MM-DD, default last seven days. Calculates a percentage of the platform's recorded net income, not the merchant's gross sales; child income uses its retained share after parent allocation. Amounts are integer minor units, grouped by currency. Unreconciled income remains pending. This report does not automatically pay agents.

PHP signed management request

```
$secret = getenv('NEROPAY_SECRET_KEY'); // server only
$path = '/v2/neroconnect/management/sales-agents';
$method = 'POST';
$body = json_encode([
    'staff_id' => 463,
    'name' => 'Sales agent',
    'commission_percent' => '20.00',
    'active' => true
], JSON_UNESCAPED_SLASHES);
$timestamp = (string) time();
$httpKey = 'agent-create-' . bin2hex(random_bytes(12));
$signature = hash_hmac('sha256',
    $timestamp . "\n" . $method . "\n" . $path
    . "\n" . $httpKey . "\n" . $body, $secret);
$curl = curl_init('https://eu.neropay.app' . $path);
curl_setopt_array($curl, [
    CURLOPT_CUSTOMREQUEST => $method,
    CURLOPT_POSTFIELDS => $body,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . $secret,
        'Content-Type: application/json',
        'Idempotency-Key: ' . $httpKey,
        'X-NeroPay-Timestamp: ' . $timestamp,
        'X-NeroPay-Signature: ' . $signature
    ]
]);
$result = curl_exec($curl);
$status = curl_getinfo($curl, CURLINFO_HTTP_CODE);
curl_close($curl);
```

## NeroConnect Partner API

Manage platform partners, connected account moves, partner pricing and funded transfers. Use the main platform’s server credentials for parent operations.

Base path: `/v2/neroconnect/management` · [Authentication & signed requests ↗](https://docs.neropay.app/api/#neroconnect-management)

### Connected account moves

GET`/account-moves`

Eligible family platforms and source accounts. Optional integer `source_id` (defaults to the authenticated parent), `q` (max 100), `page`, `channel` (all, terminal, ecommerce, mixed).

POST`/account-moves/preview`

Required integer `source_id`, different integer `destination_id` and 1–20 distinct integer `account_ids`. Returns `preview_id`, `expires_in: 300` and an ownership/pricing preview. No mutation at preview time.

POST`/account-moves/commit`

Required UUID `preview_id`, `api_key_acknowledged: true` and `ownership_acknowledged: true`. Signed API confirmation replaces browser password entry. The preview must belong to this platform and API actor; browser/staff previews cannot be consumed by the API. Changed ownership, pricing, agreements, balances or expired previews require a new preview.

**Move consequences:** merchant identity and transaction history remain the same. Historical ownership and commissions are retained. No wallet balance is transferred. Future payments use the destination platform. Update merchant websites/integrations to its API key; the previous platform key loses access. Destination keys are never returned by this API.

### Partner controls

GET`/partners`

Parent-owned partners only. Optional `limit`, `page`, `state` (pending, active, suspended).

GET`/partners/{partnerId}`

Safe agreement terms/policies and opaque `pricing_version`/`management_version`. Read immediately before saving. Does not return secret keys, identity documents or provider account identifiers.

PATCH`/partners/{partnerId}/pricing`

Direct Save, not Send proposal. Required `pricing_version` and `model` (rate or revenue_share); revenue_share requires decimal-string `share_percent`. Optional `rates` and `products` as described below. An accepted initial agreement is required; pricing never activates a partner or creates consent. New rates are effective-dated; historical income is unchanged.

PUT`/partners/{partnerId}/policies`

Required current `management_version` plus a complete `policies` object. This replaces the permitted controls; it cannot bypass NeroPay's own restrictions.

POST`/partners/{partnerId}/suspend`

Required current `management_version`. Suspends an own partner without changing balances, historical commissions or KYC.

GET`/partner-settings`

Parent signup defaults, revision and existing signup status.

PUT`/partner-settings`

Required `expected_revision`, pricing-model input, and complete `policies`. Updates future defaults only. Existing signup enablement and legal URLs are retained; existing agreements are not rewritten.

GET`/partner-agreement`

A second-tier platform reads its own safe agreement. No sending, accepting, activating or replacing a legal agreement is exposed by this management API.

### Pricing and policy inputs

Send monetary/percentage inputs as decimal strings, not floats or internal minor-unit fields. Fixed partner charges and product prices use GBP. Responses use integer minor units and basis points (100 bps = 1%). Currency totals are never combined without conversion.

`rates` is keyed by `api`, `card`, `keyed`, `origin`, `extra_method`, `sms`, `cellular`, `instant_payout`, `refund`, `monthly_account` or `monthly_terminal`; values accept `fixed` and `percent`. Percentages are 0–100. Monthly account/terminal charges cannot carry a percentage. Omitted categories become zero in the saved replacement pricing. `products` maps server-recognised catalogue slugs to GBP decimal strings; unknown slugs are rejected.

A complete `policies` object requires booleans `accounts`, `pricing`, `branding`, `invoices`, `dashboard`, `store`, `reports`, `api`, `funds`, `view_as`, `settings`, `hide_share_entries`, integer `max_accounts` (0–100000) and GBP decimal-string `max_transfer`. Responses return this limit as `max_transfer_minor`; convert it when constructing an input.

### Funded partner movements

POST`/partners/{partnerId}/funds`

Parent-only. Required `direction` (send or pull), positive GBP decimal-string `amount` (max seven whole digits, two decimals), and stable UUID `request_id`. Requires an active, accepted agreement, funds policy, verified active parties and the agreed transfer cap. No arbitrary destination, provider account, currency, fee or negative amount is accepted.

GET`/funding/{fundsId}`

Read the 64-character opaque ID returned by the funding response. Only this parent's discretionary funding rows are visible; internal recovery and store allocation rows are excluded.

**No unfunded payouts:** funding uses registered parent/partner settlement accounts only, never the payment service's operating account. The source wallet is reserved without overdraft; unrestricted available settlement funds must be collected and confirmed before the recipient transfer. Existing debt, reconciliation and policy safeguards still apply. Fees follow the server's existing parent tariff: send debits amount + fee and credits amount; pull debits amount and credits amount − fee. No API endpoint creates a balance, overrides a tariff, withdraws to arbitrary bank accounts or pays sales-agent commission automatically.

Funding returns HTTP 200 only when `state=settled` and `confirmed=true`; otherwise HTTP 202 means reserved/awaiting confirmation or review, not paid. Poll the funding ID. Do not submit a new UUID after a timeout: retry with the same `request_id` and unchanged details (a newly signed HTTP retry key is allowed). Reservations survive uncertain responses and are reconciled by the existing worker. A changed request amount/direction cannot reuse an existing UUID.

Move preview body

```
{
  "source_id": 26302,
  "destination_id": 36697,
  "account_ids": [36499]
}
```

Move commit body

```
{
  "preview_id": "<UUID returned by preview>",
  "api_key_acknowledged": true,
  "ownership_acknowledged": true
}
```

Direct partner pricing save

```
PATCH /v2/neroconnect/management/partners/36697/pricing

{
  "model": "revenue_share",
  "share_percent": "50.00",
  "pricing_version": "<read current opaque token>"
}
```

Funded partner send (GBP)

```
POST /v2/neroconnect/management/partners/36697/funds

{
  "direction": "send",
  "amount": "100.00",
  "request_id": "<generate and persist one UUID per transfer>"
}

// HTTP 202 is NOT proof of payment:
{
  "success": true,
  "data": {
    "id": "<64-character funding ID>",
    "source_id": 26302,
    "destination_id": 36697,
    "currency": "GBP",
    "debit_minor": 10150,
    "credit_minor": 10000,
    "fee_minor": 150,
    "state": "reserved",
    "confirmed": false
  }
}
// Fee example assumes the existing 1.5% tariff.
// Poll GET /v2/neroconnect/management/funding/{id}.
```

## NeroPay Store API checkout links

Create a secure NeroPay Store checkout link for terminal, EPOS, scanner, scale and accessory orders. The API creates a pending hardware order and returns a hosted checkout URL; card details are collected only on NeroPay-hosted checkout.

GET`/neropay-store/products`

Lists available NeroPay Store hardware product slugs and prices.

POST`/neropay-store/checkout-links`

Creates a hosted checkout link for the authenticated merchant or a connected account.

GET`/neropay-store/orders/{id}`

Reads a NeroPay Store order created for the authenticated account context.

**Abuse prevention:** the API never accepts custom card data or arbitrary item prices. Product slugs and prices are resolved server-side from the NeroPay Store catalogue. Use `NeroPay-Account` or `account_id` only for connected accounts assigned to your platform.

### GET /neropay-store/products response attributes

| Field | Type | Description |
| --- | --- | --- |
| `slug` | string | Stable catalogue slug used for ordering. |
| `product_code` | string | Ordering code for API integrations. This is currently the same value as `slug`. |
| `code` | string | Short alias for `product_code`. |
| `name` | string | Product name displayed on the hosted checkout. |
| `price` | decimal | Server-side NeroPay Store catalogue price. Resellers cannot override this in API requests. |
| `category` | string | Hardware, terminal, EPOS, scale, printer or accessory grouping. |

**Product codes:** call `GET /neropay-store/products` for the live catalogue. Send the returned `product_code` in `items[].product_code`. Current public codes are shown below.

| product_code | Name | Category | Base price | Optional checkout options |
| --- | --- | --- | --- | --- |
| `pro-plus` | Card Terminal PRO+ | terminal | £295.00 | None |
| `ccd-tablet` | Card Terminal CCD | terminal | £135.00 | `options.device`: `no_device`, `fire8`, `ipadair11` |
| `epos` | NeroPay EPOS Kit | epos | £799.95 | None |
| `nerokiosk` | NeroPay Self-Service Kiosk | kiosk | £800.00 | `options.size`: `21_5`, `23_6`, `32`; optional `floor_standing`, `ads=ad_free` |
| `nerokds` | NeroKDS | kds | £450.00 | `options.size`: `21_5`, `24` |
| `pro-plus-charging-dock` | PRO+ Charging Dock | accessory | £60.00 | None |
| `ccd-charging-dock` | CCD Charging Dock | accessory | £60.00 | None |
| `charger` | Additional Charger | accessory | £20.00 | None |
| `caller-id-unit` | NeroPay Caller ID Unit | accessory | £110.00 | None |
| `neroscale1` | NeroPay Manual Scale | accessory | £75.00 | None |
| `neroscalepro` | NeroPay Barcode Scale Pro | accessory | £375.00 | None |
| `neroscan1` | NeroPay Wireless 1D/2D Barcode Scanner | accessory | £65.00 | None |
| `neroscanpro` | NeroPay Desktop Barcode Scanner Pro | accessory | £95.00 | None |
| `neromini` | NeroPay Mini Bluetooth Barcode Scanner | accessory | £45.00 | None |
| `receipt-printer-80mm` | 80mm Thermal Receipt Printer | accessory | £155.00 | `options.connection`: `usb`, `lan`, `wifi`, `bluetooth` |
| `cash-drawer-rj11` | Cash Drawer Base with RJ11 Connector | accessory | £55.00 | None |
| `thermal-paper-rolls` | Thermal Paper Rolls | accessory | £20.00 | `options.pack`: `5`, `10`, `20`; `options.roll_size`: `58x58`, `80x80` |

### POST /neropay-store/checkout-links attributes

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `account_id` | string | Platform only | Connected merchant account. You may also send `NeroPay-Account`. |
| `items[].product_code` | string | Required* | Product code returned by `GET /neropay-store/products`. You may send `items[].slug` or `items[].code` instead. |
| `items[].slug` | string | Alias | Catalogue slug returned by the products endpoint. |
| `items[].quantity` | integer | Optional | Quantity, 1 to 99. Defaults to 1. |
| `billing` | object | Required | Billing contact and billing address. Required keys: `name`, `email`, `phone`, `line1`, `city`, `postcode`, `country`. Optional keys: `line2`, `county`. |
| `shipping` | object | Required | Delivery contact and shipping address. Required keys: `name`, `phone`, `line1`, `city`, `postcode`, `country`. Optional keys: `email`, `line2`, `county`. |
| `reference` | string | Optional | Your reseller/order reference stored in metadata. |

**Required address details:** checkout-link requests are rejected unless both `billing` and `shipping` are supplied. NeroPay Store API v2 does not fall back to the merchant profile address for hardware checkout links.

**Order scope:** `GET /neropay-store/orders/{id}` only returns an order when the order belongs to the authenticated merchant or the connected merchant supplied with `NeroPay-Account`/`account_id`. It does not list all orders or expose another merchant’s orders.

Create terminal checkout link

```
curl https://eu.neropay.app/v2/neropay-store/checkout-links \
  -X POST \
  -H "Authorization: Bearer neropay_secret_..." \
  -H "Idempotency-Key: store-checkout-order-1001" \
  -H "Content-Type: application/json" \
  -d '{
    "account_id": "NPVesdTGASO23_547878",
    "reference": "STORE-ORDER-1001",
    "items": [
      {"product_code": "pro-plus", "quantity": 1},
      {"product_code": "pro-plus-charging-dock", "quantity": 1}
    ],
    "billing": {
      "name": "Customer Name",
      "email": "customer@example.com",
      "phone": "+447700900123",
      "line1": "1 High Street",
      "city": "London",
      "postcode": "SW1A 1AA",
      "country": "United Kingdom"
    },
    "shipping": {
      "name": "Customer Name",
      "phone": "+447700900123",
      "line1": "1 High Street",
      "city": "London",
      "postcode": "SW1A 1AA",
      "country": "United Kingdom"
    }
  }'
```

Response

```
{
  "success": true,
  "data": {
    "order": {
      "id": 12345,
      "reference": "API-STORE-ORDER-1001-AB12C",
      "total": 355.00,
      "status": "pending",
      "checkout_url": "https://eu.neropay.app/neropay-store/pay/12345/..."
    },
    "checkout": {
      "url": "https://eu.neropay.app/neropay-store/pay/12345/...",
      "payment_collection": "hosted_checkout_only"
    }
  }
}
```

## Payments

Recommended for online checkout, online ordering and website payments. Create a NeroPay-hosted checkout URL and redirect the customer there. Card details are collected by NeroPay-hosted checkout and are never submitted through your API payload.

POST`/hosted-payments`

Creates a hosted checkout URL for an order or invoice.

POST`/payment-links`

Creates a simple LinkPay checkout link when you only need a payment URL.

POST`/terminal/payments`

Sends an in-person card-present payment action to a terminal reader.

### POST payment request attributes

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| identifier | string | Required | Your unique order, invoice or checkout reference. |
| amount | decimal | Required | Payment amount in major units, e.g. 15.99. |
| currency | enum | Required | Currency such as GBP or EUR. |
| details | text | Required | Customer-facing or internal checkout description. |
| ipn_url | url | Required | Receives hosted-payment outcome updates, including a declined attempt and a later success. The initial API response is not proof of payment. |
| success_url | url | Required | Where the customer is returned after a successful payment. |
| cancel_url | url | Required | Where the customer is returned after cancellation. |
| customer_name | string | Required | Customer name shown on the checkout record. |
| customer_email | email | Required | Customer email shown on the checkout record. |
| customer_phone | string | Optional | Customer phone number, if collected. |
| account_id | string | Platform only | Connected merchant account for NeroConnect routing. You may also send the `NeroPay-Account` header. |
| connect_application_fee | decimal | Optional | Platform fee charged for a connected account payment. |
| metadata | object | Optional | Additional key/value data returned in responses and webhooks where supported. |

**Use hosted checkout by default:** do not send card number, CVC, expiry date or cardholder payment details to the API. NeroPay-hosted checkout keeps sensitive card data outside your server.

**NeroConnect routing:** when a platform API key creates a checkout for a connected merchant, send `NeroPay-Account` or `account_id` with that connected merchant account on every request.

**Hosted checkout is the recommended online payment flow:** direct card payment creation is not part of the public API. Use `/v2/hosted-payments` for online checkout or `/v2/payment-links` for a simple payment link.

### Hosted-payment result lifecycle

### Request rejected immediately

The API returns HTTP 4xx/5xx with `success: false` and an `error` object. Do not create or confirm the order as paid.

### A payment attempt is declined

NeroPay may post `status=failed` with an `error` object to the payment's `ipn_url`. This describes that attempt: the customer may retry the same checkout and subsequently pay successfully.

### Failed IPN attributes

| Field | Type | Description |
| --- | --- | --- |
| status | enum | `failed` for an unsuccessful payment attempt. A later `success` notification may follow for the same checkout. |
| identifier | string | Your original order or invoice identifier. |
| signature | string | Uppercase HMAC-SHA256 of `amount + identifier` using the merchant secret API key. |
| error.code | string | Stable machine-readable category, such as `card_declined` or `payment_failed`. |
| error.message | string | Provider-neutral support message safe to display or log. |
| error.decline_code | string | Optional more specific decline reason, such as `insufficient_funds`. |
| data | object | Payment reference, status, amount, customer, currency and a repeated error object for existing IPN consumers. |

**IPN transport:** hosted-payment IPNs use `application/x-www-form-urlencoded` for compatibility with existing integrations. The example is shown as the decoded object. Use `identifier` or `data.payment_trx` to find the order, not to discard every later update. Process each outcome safely and fulfil the order only once after confirmed success.

**Registered webhooks:** platforms using the reusable Webhooks API must subscribe to both `transaction.succeeded` and `transaction.failed`. This is separate from the per-payment `ipn_url`.

**A failed attempt is not necessarily the final order outcome.** 

For example, an attempt may be declined for `insufficient_funds`, then succeed when the customer retries using another payment method. Reopen the original NeroPay checkout URL and keep the same order identifier. Where the existing payment remains retryable, NeroPay reuses its PaymentIntent internally. Do not create another checkout or change the identifier simply because an attempt failed, a request timed out or a callback has not arrived.

If you repeat a creation request, keep the same merchant account, identifier, amount, currency and callback URL, and reuse the original `Idempotency-Key` for that same request. Retrieve the payment's current status if the response is uncertain. A cancelled, expired or conflicting payment needs verification before a replacement is created; never assume that money has not moved.

### Payment listener and order reconciliation

| Incoming update | Order handling |
| --- | --- |
| Initial API request accepted / pending | Save the reference and checkout URL. Do not mark the order as paid or print a paid order. |
| Declined attempt / failed | Record the attempt and show an appropriate retry message. Keep the listener active for a later successful payment on the same order. |
| Confirmed success after a failure | Update the same order to paid, and queue fulfilment or printing once. Do not discard success because the order already has a failed record. |
| Duplicate delivery or an older failure after success | Acknowledge the delivery without repeating fulfilment. Never change an already-paid order back to failed because of a payment-attempt event. Refunds are separate events. |

**Delivery and verification:** callbacks can be retried and can arrive out of order. Verify the applicable signature, save the event durably, then return HTTP 2xx promptly. Process printing and other slow work asynchronously. For registered JSON webhooks, verify the timestamp and HMAC against the unmodified body; use `X-NeroPay-Event-Id` to identify a delivery stream and also protect fulfilment with a unique order-level guard. For legacy form IPNs, verify the documented signature and retrieve the current payment status through the authenticated API before fulfilment, as the legacy signature covers the amount and identifier rather than the complete outcome payload. Do not treat a browser redirect as payment confirmation.

**Important distinction:** the per-payment form IPN uses `status=success` or `status=failed`; registered JSON webhooks use `transaction.succeeded` or `transaction.failed`. These are separate delivery channels. Use one authoritative fulfilment path, or deduplicate across both channels using the same merchant/order reference.

Hosted payment request

```
POST /v2/hosted-payments
NeroPay-Account: NPVesdTGASO23_547878
{
  "identifier": "ORDER-1001",
  "amount": 15.99,
  "currency": "GBP",
  "details": "Online order #1001",
  "customer_name": "Jane Guest",
  "customer_email": "jane@example.com",
  "success_url": "https://example.com/order/success",
  "cancel_url": "https://example.com/checkout",
  "ipn_url": "https://example.com/webhooks/neropay",
  "connect_application_fee": 0.30,
  "metadata": {
    "cart_id": "cart_1001"
  }
}
```

Hosted payment response

```
{
  "success": true,
  "data": {
    "id": 208198,
    "account_id": "NPVesdTGASO23_547878",
    "identifier": "ORDER-1001",
    "payment_reference": "NP-EHPS4218-ba542",
    "status": "pending",
    "amount": 15.99,
    "currency": "GBP",
    "connect_application_fee": 0.30,
    "checkout_url": "https://eu.neropay.app/initiate/payment/checkout?payment_id=..."
  }
}
```

Failed hosted-payment IPN (decoded)

```
{
  "status": "failed",
  "identifier": "ORDER-1001",
  "signature": "A6E6...",
  "error": {
    "code": "card_declined",
    "message": "Your card was declined.",
    "decline_code": "insufficient_funds"
  },
  "data": {
    "payment_trx": "NP-EHPS4218-ba542",
    "status": "failed",
    "amount": 15.99,
    "account_holder": "Jane Guest",
    "payment_type": "hosted",
    "currency": {"code": "GBP", "symbol": "£"},
    "error": {
      "code": "card_declined",
      "message": "Your card was declined.",
      "decline_code": "insufficient_funds"
    }
  }
}
```

## Payment Links / LinkPay

Create and inspect LinkPay checkout links. LinkPay records are separated from generic API payments.

GET`/payment-links`

Lists LinkPay records.

POST`/payment-links`

Creates a payment link.

GET`/payment-links/{id}`

Retrieves one payment link.

### POST /payment-links request attributes

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| currency | enum | Required | Payment currency. |
| amount | decimal | Required | Amount in major units. |
| details | text | Required | Description shown to the buyer. |
| customer_name | string | Optional | Pre-filled customer name. |
| customer_email | string | Optional | Pre-filled customer email. |
| success_url | string | Optional | Redirect URL after success. |
| cancel_url | string | Optional | Redirect URL after cancellation. |
| ipn_url | string | Optional | Webhook/IPN URL for this payment link. |

Create payment link

```
POST /v2/payment-links
{
  "currency": "GBP",
  "amount": 9.99,
  "details": "Product or service",
  "customer_name": "Jane Guest",
  "customer_email": "jane@example.com",
  "success_url": "https://example.com/success",
  "cancel_url": "https://example.com/cancel",
  "ipn_url": "https://example.com/webhooks/neropay"
}
```

Payment link response

```
{
  "success": true,
  "data": {
    "payment_link": {
      "id": 1042,
      "amount": 9.99,
      "currency": "GBP",
      "status": "pending",
      "checkout_url": "https://pay.neropay.app/link/np_..."
    }
  }
}
```

Contracts 

## Contracts API

Create merchant contracts, store contract rules, require full or deposit payments and send customers to a secure NeroPay contract review page.

GET`/contracts`

Lists contracts for the authenticated merchant or connected merchant when `NeroPay-Account` is supplied.

POST`/contracts`

Creates a contract with customer details, formatted content, signature rules and optional full or upfront payment.

GET`/contracts/{id}`

Retrieves one contract by internal ID or public contract reference where supported.

PATCH`/contracts/{id}`

Updates contract fields, customer details, signature rules, status or payment settings.

DELETE`/contracts/{id}`

Deletes an unpaid draft/sent contract where the merchant account is allowed to do so.

POST`/contracts/{id}/payment-link`

Creates a secure NeroPay checkout link for the required contract payment.

GET`/contracts/{id}/form`

Returns the contract intake/customer form definition.

GET`/contracts/{id}/form-submissions`

Lists customer form submissions for the scoped contract.

POST`/contracts/{id}/form-submissions`

Stores a validated form submission against the scoped contract.

### GET /contracts query attributes

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| status | enum | Optional | Filter by `draft`, `sent`, `signed`, `completed`, `cancelled` or `expired`. |
| payment_status | enum | Optional | Filter by `not_required`, `unpaid`, `partially_paid`, `paid` or `failed`. |
| q | string | Optional | Search contract reference, title, customer name or customer email. |
| page | integer | Optional | Pagination page number. |
| per_page | integer | Optional | Items per page. Keep this sensible for dashboard performance. |

### GET /contracts response attributes

| Field | Type | Description |
| --- | --- | --- |
| id | integer | Internal contract ID. |
| contract_ref | string | Merchant-facing reference such as `CTR-260612-ABC123`. |
| public_url | url | Secure customer review/sign/payment page. |
| title, summary, content_html | string/html | Contract title, customer-facing summary and formatted contract content. |
| customer_* fields | string | Customer name, email, phone and address snapshot. |
| contract_amount | decimal | Total contract value. |
| payment_amount | decimal | Amount due now if online payment is required. |
| status | enum | Current lifecycle status. |
| payment_status | enum | Current payment status. |
| signature_required | boolean | Whether the customer must provide an electronic signature. |
| signed_at | datetime | Set when the customer signs/accepts the contract. |
| cc_emails | array | Optional CC recipients used for contract communications. |
| fee_items | array | Optional fee breakdown shown in the contract summary. |
| intake_form | object | Customer form definition, including enabled status and field list. |
| intake_form_submissions_count | integer | Number of saved form submissions for this contract. |

### POST /contracts request attributes

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| title | string | Required | Contract title shown to the customer. |
| customer_name | string | Required | Customer or business name. |
| customer_email | email | Required | Customer email address used for send/resend. |
| customer_phone | string | Optional | Customer phone number. |
| customer_address | string | Optional | Billing/service address snapshot. |
| currency | enum | Required | Currency code such as `GBP` or `EUR`. You may also send `currency_id`. |
| contract_amount | decimal | Optional | Total contract value. Required if `payment_mode=full`. |
| show_contract_amount | boolean | Optional | Controls whether the total amount is shown in the public summary. If payment is required, the amount due now is always shown. |
| payment_required | boolean | Optional | When true, the public contract page can show a Pay button. |
| payment_mode | enum | Optional | `none`, `full`, `deposit` or `custom`. `full` collects the contract amount; `deposit/custom` collects `deposit_amount`. |
| deposit_amount | decimal | Optional | Upfront amount due now when using `deposit` or `custom`. |
| signature_required | boolean | Optional | Require customer electronic signature before the contract is treated as signed. |
| customer_details_editable | boolean | Optional | Allow the customer to correct their name, email, phone or address before signing. |
| content_html | html | Required | Main contract body. Script tags and inline event handlers are removed server-side. |
| summary | string | Optional | Short customer-facing summary for the contract list and email. |
| cc_emails[] | email[] | Optional | Up to five CC recipients. Customer email is not duplicated. |
| fee_items[] | array | Optional | Up to eight fee rows with `label`, `fee_type`, `amount` and `notes`. |
| intake_form.enabled | boolean | Optional | Enable customer form before signing. |
| intake_form.fields[] | array | Optional | Up to 20 questions. Types: `text`, `date`, `textarea`, `dropdown`, `radio`, `checkbox`. |
| intake_form.fields[].condition | object | Optional | Optional show/hide rule using `field`, `operator`, `value` and `action`. |
| expires_at | datetime | Optional | Optional expiry date/time for the public review link. |
| send_email | boolean | Optional | Send the contract email immediately after saving. Resend is rate-limited. |
| account_id | string | Platform only | Connected merchant account for NeroConnect routing. You may also send the `NeroPay-Account` header. |

### PATCH /contracts/{id} request attributes

| Field | Type | Description |
| --- | --- | --- |
| title, summary, content_html | string/html | Update contract content while the contract is not completed/cancelled. |
| customer_name, customer_email, customer_phone, customer_address | string | Update customer details snapshot. |
| contract_amount, payment_required, payment_mode, deposit_amount | decimal/boolean/enum | Update payment rules. Paid contracts cannot be changed in a way that breaks payment history. |
| signature_required, customer_details_editable, show_contract_amount | boolean | Update customer signing and display rules. |
| status | enum | `draft`, `sent`, `signed`, `completed`, `cancelled` or `expired`. |

### Contract form endpoints

| Endpoint | Purpose | Security |
| --- | --- | --- |
| `GET /contracts/{id}/form` | Read the public/customer form definition for a scoped contract. | Account-scoped; other merchants’ contracts return `contract_not_found`. |
| `GET /contracts/{id}/form-submissions` | Read saved customer form submissions. | Account-scoped and never searchable across all merchants. |
| `POST /contracts/{id}/form-submissions` | Save answers against the contract form using an `answers` object keyed by field key. | Requires signed write security and validates required/conditional fields server-side. |

**Contract form safety:** form fields are stored inside the contract `meta_json`. API v2 strips scripts/unsafe HTML, limits questions/options/submissions and rejects raw card, PIN, password or bank-account fields.

### POST /contracts/{id}/payment-link request attributes

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| success_url | url | Optional | Where the customer is returned after payment. Defaults to the public contract page. |
| cancel_url | url | Optional | Where the customer is returned after cancelling checkout. |
| metadata | object | Optional | Extra reference data stored against the payment request. |

**Payment routing:** when a contract requires payment, `POST /contracts/{id}/payment-link` creates a NeroPay hosted checkout using the merchant account. This is separate from generic LinkPay links and is tied back to the contract payment status.

**HTML safety:** send formatted HTML from your own editor, but never include scripts, external trackers or card data. The API sanitises unsafe HTML before it is shown to customers.

Create contract request

```
POST /v2/contracts
{
  "title": "Service agreement",
  "customer_name": "Demo Customer Ltd",
  "customer_email": "customer@example.com",
  "customer_phone": "07700000000",
  "currency": "GBP",
  "contract_amount": 1200.00,
  "show_contract_amount": true,
  "payment_required": true,
  "payment_mode": "deposit",
  "deposit_amount": 300.00,
  "signature_required": true,
  "customer_details_editable": true,
  "summary": "Website setup and monthly support agreement.",
  "content_html": "<h2>Agreement</h2><p>The customer agrees to...</p>",
  "fee_items": [
    {"label": "Setup", "fee_type": "one_off", "amount": "£300"}
  ],
  "intake_form": {
    "enabled": true,
    "fields": [
      {"key": "medical_conditions", "label": "Do you have any medical conditions?", "type": "radio", "required": true, "options": ["Yes", "No"]},
      {"key": "details", "label": "Please provide details", "type": "textarea", "condition": {"enabled": true, "field": "medical_conditions", "operator": "equals", "value": "Yes", "action": "show"}}
    ]
  },
  "send_email": true
}
```

Create payment link

```
POST /v2/contracts/123/payment-link
{
  "success_url": "https://example.com/thanks",
  "cancel_url": "https://example.com/cancelled"
}
```

NeroGYM 

## NeroGYM overview

NeroGYM API v2 exposes gym operations in the same menu-led style as NeroTrade: settings, clients, plans, memberships, sessions, classes, staff, programmes, check-ins and payment records.

**Security model:** every request is scoped to the authenticated merchant. Platform/reseller keys must use `NeroPay-Account` or `account_id` for an allowed connected merchant only. Write calls require HMAC signing and an `Idempotency-Key`.

GET`/nerogym/overview`

Returns dashboard-safe totals and recent activity for the scoped gym account.

### GET /nerogym/overview query attributes

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `account_id` | string | Platform only | Connected merchant account. You may also send `NeroPay-Account`. |
| `date_from` / `date_to` | date | Optional | Optional reporting window for revenue, attendance and session counts. |

### GET /nerogym/overview response attributes

| Field | Type | Description |
| --- | --- | --- |
| `account_id` | string | Scoped merchant account. |
| `settings_enabled` | boolean | Whether NeroGYM is enabled for the merchant. |
| `stats.clients` | integer | Total active/known clients. |
| `stats.memberships` | integer | Active membership count. |
| `stats.sessions` | integer | Sessions in the selected period. |
| `stats.classes` | integer | Classes in the selected period. |
| `stats.checkins` | integer | Check-ins in the selected period. |
| `stats.revenue` | decimal | Payment records total for the selected period. |
| `recent_activity[]` | array | Recent sessions, check-ins or payments with safe public fields. |

Overview

```
GET /v2/nerogym/overview?date_from=2026-07-01&date_to=2026-07-31
```

Overview response

```
{
  "success": true,
  "data": {
    "account_id": "NPVesdTGASO23_547878",
    "settings_enabled": true,
    "stats": {
      "clients": 124,
      "memberships": 93,
      "sessions": 212,
      "classes": 41,
      "checkins": 684,
      "revenue": 8420.50
    },
    "recent_activity": []
  }
}
```

NeroGYM 

## Settings

Read and update safe NeroGYM portal, billing and booking settings for a merchant.

GET`/nerogym/settings`

Reads NeroGYM settings.

PATCH`/nerogym/settings`

Updates supported settings only.

### GET /nerogym/settings query attributes

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `account_id` | string | Platform only | Connected merchant account. You may also send `NeroPay-Account`. |

### GET /nerogym/settings response attributes

| Field | Type | Description |
| --- | --- | --- |
| `enabled` | boolean | NeroGYM feature status. |
| `portal_url` | url | Merchant public NeroGYM portal URL. |
| `booking_enabled` | boolean | Allows clients to book available sessions/classes online. |
| `member_portal_enabled` | boolean | Allows member portal access. |
| `parq_required` | boolean | Whether PAR-Q/health form is required during onboarding. |
| `currency` | string | Currency used for plans and payment records. |
| `timezone` | string | Timezone used for calendar/session rendering. |

### PATCH /nerogym/settings request attributes

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `account_id` | string | Platform only | Connected merchant account. You may also send `NeroPay-Account`. |
| `enabled` | boolean | Optional | Enable or disable NeroGYM. |
| `booking_enabled` | boolean | Optional | Enable online booking. |
| `member_portal_enabled` | boolean | Optional | Enable member portal access. |
| `parq_required` | boolean | Optional | Require health/PAR-Q form before training. |
| `timezone` | string | Optional | IANA timezone, for example `Europe/London`. |

Update settings

```
PATCH /v2/nerogym/settings
Idempotency-Key: gym-settings-20260701-001
{
  "booking_enabled": true,
  "member_portal_enabled": true,
  "parq_required": true,
  "timezone": "Europe/London"
}
```

NeroGYM 

## Clients

Create and manage gym clients. Client IDs are always scoped to the authenticated merchant.

GET`/nerogym/clients`

Lists clients.

GET`/nerogym/clients/{id}`

Retrieves one client.

POST`/nerogym/clients`

Creates a client.

PATCH`/nerogym/clients/{id}`

Updates a client.

DELETE`/nerogym/clients/{id}`

Deletes or deactivates a client where allowed.

### GET /nerogym/clients query attributes

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `account_id` | string | Platform only | Connected merchant account. |
| `q` | string | Optional | Search name, email or phone. |
| `status` | enum | Optional | Filter by `active`, `inactive`, `blocked` or local configured status. |
| `page` / `per_page` | integer | Optional | Pagination controls. |

### GET /nerogym/clients response attributes

| Field | Type | Description |
| --- | --- | --- |
| `id` | integer | Client ID. |
| `name` | string | Client full name. |
| `email` | email | Client email address. |
| `phone` | string | Client phone number. |
| `status` | enum | Current client status. |
| `notes` | text | Internal notes where available. |
| `created_at`, `updated_at` | datetime | Record timestamps. |

### POST /nerogym/clients request attributes

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `name` | string | Required | Client full name. |
| `email` | email | Optional | Unique email for this merchant where provided. |
| `phone` | string | Optional | Client phone number. |
| `status` | enum | Optional | Defaults to `active`. |
| `notes` | text | Optional | Internal client note. |

### PATCH /nerogym/clients/{id} request attributes

| Field | Type | Description |
| --- | --- | --- |
| `name`, `email`, `phone`, `status`, `notes` | mixed | Only supplied fields are updated. Client must belong to the scoped merchant. |

### DELETE /nerogym/clients/{id} response attributes

| Field | Type | Description |
| --- | --- | --- |
| `deleted` | boolean | True when the record was deleted/deactivated. |

Create client

```
POST /v2/nerogym/clients
Idempotency-Key: gym-client-20260701-001
{
  "name": "Jane Member",
  "email": "jane@example.com",
  "phone": "07700000000",
  "status": "active"
}
```

List clients

```
GET /v2/nerogym/clients?q=jane&status=active&per_page=25
```

NeroGYM 

## Plans

Create and maintain membership packages, one-off session packs and public plan terms.

GET`/nerogym/plans`

Lists plans.

GET`/nerogym/plans/{id}`

Retrieves one plan.

POST`/nerogym/plans`

Creates a plan.

PATCH`/nerogym/plans/{id}`

Updates a plan.

DELETE`/nerogym/plans/{id}`

Deletes/deactivates a plan where no active dependencies block removal.

### GET /nerogym/plans query attributes

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `q` | string | Optional | Search plan name. |
| `status` | enum | Optional | Filter active/inactive plans. |
| `page` / `per_page` | integer | Optional | Pagination controls. |

### GET /nerogym/plans response attributes

| Field | Type | Description |
| --- | --- | --- |
| `id` | integer | Plan ID. |
| `name` | string | Plan name. |
| `price` | decimal | Plan price. |
| `billing_cycle` | enum | `monthly`, `weekly`, `one_off` or local cycle. |
| `session_limit` | integer/null | Included session limit if configured. |
| `general_terms` | html/text | Customer-facing plan terms. |
| `status` | enum | Plan status. |

### POST /nerogym/plans request attributes

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `name` | string | Required | Plan name. |
| `price` | decimal | Required | Plan price, server validated as a non-negative amount. |
| `billing_cycle` | enum | Required | `monthly`, `weekly`, `one_off` or supported cycle. |
| `session_limit` | integer | Optional | Included session allowance. |
| `general_terms` | text/html | Optional | Plan terms shown to customers before checkout/acceptance. |
| `status` | enum | Optional | Defaults to active. |

### PATCH /nerogym/plans/{id} request attributes

| Field | Type | Description |
| --- | --- | --- |
| `name`, `price`, `billing_cycle`, `session_limit`, `general_terms`, `status` | mixed | Only supplied fields are updated. Plan must belong to the scoped merchant. |

Create monthly plan

```
POST /v2/nerogym/plans
Idempotency-Key: gym-plan-20260701-001
{
  "name": "Monthly PT Plan",
  "price": 49.99,
  "billing_cycle": "monthly",
  "session_limit": 4,
  "general_terms": "Client agrees to the gym membership terms."
}
```

NeroGYM 

## Memberships

Link clients to plans and manage membership lifecycle. Cross-merchant `client_id` or `plan_id` values are rejected.

GET`/nerogym/memberships`

Lists memberships.

GET`/nerogym/memberships/{id}`

Retrieves one membership.

POST`/nerogym/memberships`

Creates a membership.

PATCH`/nerogym/memberships/{id}`

Updates a membership.

DELETE`/nerogym/memberships/{id}`

Cancels/deletes a membership where allowed.

### GET /nerogym/memberships query attributes

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `client_id` | integer | Optional | Filter by client belonging to the scoped merchant. |
| `plan_id` | integer | Optional | Filter by plan belonging to the scoped merchant. |
| `status` | enum | Optional | `active`, `paused`, `cancelled`, `expired` or local status. |
| `page` / `per_page` | integer | Optional | Pagination controls. |

### GET /nerogym/memberships response attributes

| Field | Type | Description |
| --- | --- | --- |
| `id` | integer | Membership ID. |
| `client_id`, `client` | integer/object | Linked client ID and safe client summary. |
| `plan_id`, `plan` | integer/object | Linked plan ID and safe plan summary. |
| `status` | enum | Membership status. |
| `starts_at`, `ends_at` | date | Membership date range. |
| `next_payment_at` | date/null | Next expected payment date when used. |

### POST /nerogym/memberships request attributes

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `client_id` | integer | Required | Client ID from the same scoped merchant. |
| `plan_id` | integer | Required | Plan ID from the same scoped merchant. |
| `status` | enum | Optional | Defaults to active. |
| `starts_at` | date | Required | Membership start date. |
| `ends_at` | date | Optional | Membership end date. |
| `next_payment_at` | date | Optional | Next expected billing/payment date. |

### PATCH /nerogym/memberships/{id} request attributes

| Field | Type | Description |
| --- | --- | --- |
| `plan_id`, `status`, `starts_at`, `ends_at`, `next_payment_at` | mixed | Only supplied fields are updated. Linked IDs must belong to the same merchant. |

Create membership

```
POST /v2/nerogym/memberships
Idempotency-Key: gym-membership-20260701-001
{
  "client_id": 123,
  "plan_id": 8,
  "status": "active",
  "starts_at": "2026-07-01",
  "next_payment_at": "2026-08-01"
}
```

NeroGYM 

## Sessions & classes

Manage one-to-one sessions and group classes. Staff and client references are validated against the scoped merchant.

GET`/nerogym/sessions`

Lists PT sessions.

POST`/nerogym/sessions`

Creates a PT session.

PATCH`/nerogym/sessions/{id}`

Updates a PT session.

DELETE`/nerogym/sessions/{id}`

Deletes/cancels a session where allowed.

GET`/nerogym/classes`

Lists group classes.

POST`/nerogym/classes`

Creates a group class.

PATCH`/nerogym/classes/{id}`

Updates a group class.

DELETE`/nerogym/classes/{id}`

Deletes/cancels a class where allowed.

### GET /nerogym/sessions or /classes query attributes

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `client_id` | integer | Sessions optional | Filter sessions by client. ID must belong to the scoped merchant. |
| `staff_id` | integer | Optional | Filter by trainer/staff member. |
| `date_from` / `date_to` | date | Optional | Calendar window filter. |
| `status` | enum | Optional | `scheduled`, `completed`, `cancelled` or local status. |

### GET response attributes

| Field | Type | Description |
| --- | --- | --- |
| `id` | integer | Session/class ID. |
| `name` | string | Class name or generated session title. |
| `client_id` | integer/null | Client for PT sessions. |
| `staff_id` | integer/null | Assigned trainer/staff member. |
| `starts_at` | datetime | Start date/time. |
| `duration_minutes` | integer | Duration in minutes. |
| `capacity` | integer/null | Class capacity where applicable. |
| `status` | enum | Current status. |

### POST /nerogym/sessions request attributes

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `client_id` | integer | Required | Client ID from the scoped merchant. |
| `staff_id` | integer | Optional | Trainer/staff ID from the scoped merchant. |
| `starts_at` | datetime | Required | Session start date/time. |
| `duration_minutes` | integer | Optional | Defaults to merchant/session setting. |
| `status` | enum | Optional | Defaults to scheduled. |

### POST /nerogym/classes request attributes

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `name` | string | Required | Class name. |
| `staff_id` | integer | Optional | Trainer/staff ID from the scoped merchant. |
| `starts_at` | datetime | Required | Class start date/time. |
| `duration_minutes` | integer | Optional | Class duration. |
| `capacity` | integer | Optional | Max attendees. |
| `status` | enum | Optional | Defaults to scheduled/active. |

### PATCH /nerogym/sessions/{id} or /classes/{id} request attributes

| Field | Type | Description |
| --- | --- | --- |
| `client_id`, `staff_id`, `name`, `starts_at`, `duration_minutes`, `capacity`, `status` | mixed | Only supplied fields are updated. Linked IDs must belong to the same merchant. |

Create PT session

```
POST /v2/nerogym/sessions
Idempotency-Key: gym-session-20260701-001
{
  "client_id": 123,
  "staff_id": 14,
  "starts_at": "2026-07-10T14:00:00+01:00",
  "duration_minutes": 60,
  "status": "scheduled"
}
```

Create class

```
POST /v2/nerogym/classes
Idempotency-Key: gym-class-20260701-001
{
  "name": "Strength Class",
  "staff_id": 14,
  "starts_at": "2026-07-11T18:00:00+01:00",
  "duration_minutes": 45,
  "capacity": 12
}
```

NeroGYM 

## Staff & programmes

Manage trainers/staff records and client training programmes. The API accepts both `programmes` and `programs` as resource aliases.

GET`/nerogym/staff`

Lists gym staff.

POST`/nerogym/staff`

Creates gym staff.

PATCH`/nerogym/staff/{id}`

Updates gym staff.

DELETE`/nerogym/staff/{id}`

Deletes/deactivates gym staff where allowed.

GET`/nerogym/programmes`

Lists training programmes.

POST`/nerogym/programmes`

Creates a programme.

PATCH`/nerogym/programmes/{id}`

Updates a programme.

DELETE`/nerogym/programmes/{id}`

Deletes/deactivates a programme where allowed.

### GET query attributes

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `q` | string | Optional | Search staff/programme name and supported text fields. |
| `client_id` | integer | Programmes optional | Filter programmes by client. |
| `status` | enum | Optional | Filter active/inactive/status values. |
| `page` / `per_page` | integer | Optional | Pagination controls. |

### GET response attributes

| Field | Type | Description |
| --- | --- | --- |
| `id` | integer | Record ID. |
| `name` | string | Staff/programme name. |
| `email`, `phone` | string | Staff contact details where applicable. |
| `role` | string | Staff role where applicable. |
| `client_id` | integer/null | Programme client where applicable. |
| `starts_at`, `ends_at` | date | Programme date range where applicable. |
| `status`, `is_active` | enum/boolean | Current active/status value. |

### POST /nerogym/staff request attributes

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `name` | string | Required | Staff full name. |
| `email` | email | Optional | Staff email address. |
| `phone` | string | Optional | Staff phone number. |
| `role` | string | Optional | Trainer, coach, manager or local role. |
| `is_active` | boolean | Optional | Defaults to true. |

### POST /nerogym/programmes request attributes

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `name` | string | Required | Programme name. |
| `client_id` | integer | Optional | Client ID from the scoped merchant. |
| `description` | text | Optional | Programme details. |
| `starts_at`, `ends_at` | date | Optional | Programme start/end dates. |
| `status` | enum | Optional | Programme status. |

### PATCH request attributes

| Field | Type | Description |
| --- | --- | --- |
| `name`, `email`, `phone`, `role`, `is_active`, `client_id`, `description`, `starts_at`, `ends_at`, `status` | mixed | Only supplied fields are updated. Linked IDs must belong to the same merchant. |

Create staff

```
POST /v2/nerogym/staff
Idempotency-Key: gym-staff-20260701-001
{
  "name": "Alex Trainer",
  "email": "trainer@example.com",
  "role": "Personal Trainer",
  "is_active": true
}
```

Create programme

```
POST /v2/nerogym/programmes
Idempotency-Key: gym-programme-20260701-001
{
  "name": "8 Week Strength Programme",
  "client_id": 123,
  "starts_at": "2026-07-01",
  "ends_at": "2026-08-26",
  "status": "active"
}
```

NeroGYM 

## Check-ins & payments

Record attendance and gym payment ledger events. Check-in/payment audit records are intentionally protected from API delete.

GET`/nerogym/checkins`

Lists check-ins.

POST`/nerogym/checkins`

Creates a check-in.

PATCH`/nerogym/checkins/{id}`

Updates a safe check-in field where allowed.

GET`/nerogym/payments`

Lists gym payment records.

POST`/nerogym/payments`

Creates a manual gym payment record. Raw card details are not accepted.

PATCH`/nerogym/payments/{id}`

Updates safe payment metadata/status only.

### GET query attributes

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `client_id` | integer | Optional | Filter by client from the scoped merchant. |
| `membership_id` | integer | Optional | Filter by membership from the scoped merchant. |
| `date_from` / `date_to` | date | Optional | Filter by checked-in or paid date. |
| `status` | enum | Payments optional | Filter payment status. |
| `page` / `per_page` | integer | Optional | Pagination controls. |

### GET response attributes

| Field | Type | Description |
| --- | --- | --- |
| `id` | integer | Record ID. |
| `client_id`, `membership_id` | integer/null | Linked client and membership IDs. |
| `checked_in_at` | datetime | Check-in timestamp for check-in records. |
| `source` | enum | Check-in source such as `manual`, `qr` or `api`. |
| `amount`, `currency` | decimal/string | Payment amount and currency for payment records. |
| `method` | enum | Payment method such as `cash`, `bank`, `card`, `link` or `manual`. |
| `paid_at` | datetime | Payment timestamp. |
| `status` | enum | Payment status where applicable. |

### POST /nerogym/checkins request attributes

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `client_id` | integer | Required | Client ID from the scoped merchant. |
| `membership_id` | integer | Optional | Membership ID from the scoped merchant. |
| `checked_in_at` | datetime | Optional | Defaults to now. |
| `source` | enum | Optional | Defaults to `api`. |

### POST /nerogym/payments request attributes

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `client_id` | integer | Required | Client ID from the scoped merchant. |
| `membership_id` | integer | Optional | Membership ID from the scoped merchant. |
| `amount` | decimal | Required | Manual payment amount. |
| `currency` | string | Optional | Defaults to merchant currency. |
| `method` | enum | Required | `cash`, `bank`, `card`, `link` or `manual`. Raw card data is rejected. |
| `paid_at` | datetime | Optional | Defaults to now. |
| `status` | enum | Optional | Defaults to paid/recorded. |
| `reference` | string | Optional | Your integration payment reference. |

### PATCH request attributes

| Field | Type | Description |
| --- | --- | --- |
| `checked_in_at`, `source`, `status`, `reference`, `notes` | mixed | Only safe metadata/status fields are updated. Audit deletes are not exposed in API v2. |

**Abuse protection:** payment writes reject raw card numbers, PINs, CVV, bank credentials, access tokens and secret keys. Use hosted NeroPay checkout links for card collection.

Create check-in

```
POST /v2/nerogym/checkins
Idempotency-Key: gym-checkin-20260701-001
{
  "client_id": 123,
  "membership_id": 44,
  "checked_in_at": "2026-07-10T09:30:00+01:00",
  "source": "api"
}
```

Create manual payment

```
POST /v2/nerogym/payments
Idempotency-Key: gym-payment-20260701-001
{
  "client_id": 123,
  "membership_id": 44,
  "amount": 49.99,
  "currency": "GBP",
  "method": "cash",
  "paid_at": "2026-07-10T10:00:00+01:00",
  "reference": "PT payment"
}
```

Invoices 

## Invoices API

Create professional merchant invoices with customer details, VAT lines, discounts, due dates and a secure NeroPay payment page.

GET`/invoices`

Lists invoices for the merchant or connected merchant account.

POST`/invoices`

Creates a professional invoice and returns the public payment/review URL.

GET`/invoices/{id}`

Retrieves one invoice with line items.

PATCH`/invoices/{id}`

Updates an unpaid invoice. Paid/refunded invoices are locked.

DELETE`/invoices/{id}`

Deletes a draft/unpaid invoice where no payment has been recorded.

### GET /invoices query attributes

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| status | enum | Optional | Filter by `draft`, `unpaid`, `paid`, `cancelled`, `refunded` or your configured invoice status. |
| q | string | Optional | Search invoice number, customer name, email or reference. |
| date_from / date_to | date | Optional | Limit by invoice creation date or issue date. |
| page / per_page | integer | Optional | Pagination controls. |

### GET /invoices response attributes

| Field | Type | Description |
| --- | --- | --- |
| id | integer | Internal invoice ID. |
| invoice_number | string | Merchant/customer-facing invoice number. |
| public_url | url | Public invoice review and payment page. |
| invoice_to, email, address | string | Customer billing details. |
| items[] | array | Invoice line items with name, quantity, unit amount, VAT and row total. |
| subtotal_amount, vat_amount, discount_amount, shipping_amount, total_amount | decimal | Calculated invoice totals. |
| payment_required, partial_payment_allowed, deposit_amount | boolean/decimal | Payment collection settings. |
| status, paid_at | enum/datetime | Current invoice status and payment timestamp when available. |

### POST /invoices request attributes

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| invoice_to | string | Required | Customer or company name. |
| email | email | Required | Customer email address. |
| address | string | Required | Customer billing address. |
| invoice_title | string | Optional | Short title/subject for the invoice. |
| reference | string | Optional | Your internal reference. |
| po_number | string | Optional | Customer purchase order number. |
| currency | string | Optional | Currency code such as GBP. You may send `currency_id` instead. |
| items[] | array | Required | Each item requires `name` and `unit_amount`. `qty`, `description` and `vat_rate` are optional. |
| discount_amount | decimal | Optional | Flat invoice discount. |
| shipping_amount | decimal | Optional | Shipping, delivery or service amount added to the invoice. |
| due_date | date | Optional | Invoice due date. |
| payment_required | boolean | Optional | When true, the public invoice includes NeroPay checkout. |
| partial_payment_allowed | boolean | Optional | Allows the customer to pay a deposit/partial amount first. |
| deposit_amount | decimal | Optional | Deposit or upfront amount when partial payments are allowed. |
| invoice_note | text | Optional | Customer-facing invoice note. |
| footer_note | text | Optional | Optional footer shown on the invoice. |
| send_email | boolean | Optional | Send the invoice email after creation. |
| account_id | string | Platform only | Connected merchant account for NeroConnect routing. |

### PATCH /invoices/{id} request attributes

| Field | Type | Description |
| --- | --- | --- |
| invoice_to, email, address, invoice_title, reference, po_number | string | Update customer and reference details. |
| items[] | array | Replace line items and recalculate totals. Paid/refunded invoices are locked. |
| discount_amount, shipping_amount, due_date | decimal/date | Update invoice totals and due date. |
| payment_required, partial_payment_allowed, deposit_amount | boolean/decimal | Update payment collection rules before payment is recorded. |
| status | enum | Update status only where allowed by the invoice lifecycle. |

**Safety:** paid and refunded invoices cannot be edited through API v2. Provider errors are logged server-side and returned as NeroPay-safe messages.

Create invoice request

```
POST /v2/invoices
{
  "invoice_to": "Demo Customer Ltd",
  "email": "accounts@example.com",
  "address": "1 Demo Street, London",
  "currency": "GBP",
  "invoice_title": "Website setup",
  "po_number": "PO-1001",
  "due_date": "2026-06-30",
  "items": [
    {"name": "Setup fee", "qty": 1, "unit_amount": 500.00, "vat_rate": 20},
    {"name": "Monthly support", "qty": 3, "unit_amount": 75.00, "vat_rate": 20}
  ],
  "discount_amount": 25.00,
  "shipping_amount": 0.00,
  "payment_required": true,
  "partial_payment_allowed": true,
  "deposit_amount": 150.00,
  "invoice_note": "Please pay by the due date.",
  "send_email": true
}
```

Subscriptions · advanced billing · live API 

## Subscriptions & recurring pricing

Use the same products, prices, discounts and usage ledger as Merchant → Subscriptions. Create monthly or annual prices, quantity tiers and agreed future price changes, then send the customer to hosted review and card authorisation.

**Live only:** these routes are not part of the Developer sandbox simulator. They require the installed subscription workspace and its existing database setup. Never expose secret API keys in a browser, mobile app or public repository.

### Signed writes and account access

Every request uses HTTPS and a live Bearer secret. GET/HEAD use the Bearer credential; POST/PATCH/PUT also require a current timestamp, an `Idempotency-Key` and the bound signature below. For a permitted NeroConnect account, send its exact `NeroPay-Account` header. Body/query account aliases are rejected. Each record must belong to that selected account and the existing partner API policy must permit access.

HMAC-SHA256(timestamp + "\n" + METHOD + "\n" + path + "\n" + Idempotency-Key + "\n" + NeroPay-Account + "\n" + exact_raw_JSON, secret_key)

Use the uppercase HTTP method and path including `/v2`, without a query string or trailing slash. Use an empty account line when the header is omitted. The timestamp is Unix seconds within five minutes. The signature is a 64-character hexadecimal digest. Encode once, sign those exact UTF-8 bytes, then send them unchanged.

**This subscription signature differs from the older timestamp.body signature.** It binds the method, endpoint, retry key and selected account, so a signature cannot be moved to a different operation. The Node.js and PHP examples remain complete server-side examples when the page’s language selector changes.

### Retries and limits

- Persist one 8–100 character key per logical write (letters, digits, dot, underscore, colon, hyphen). Reuse the same key and data with a fresh timestamp/signature after a timeout; do not automatically generate a new key. 
- A completed request replays its original status and body with `Idempotency-Replayed: true`. The scope is authenticated merchant, selected account, HTTP method and path. A changed payload under the same key is rejected. A replay is a saved result; GET the subscription for its latest status. 
- Request references remain in the database ledger. An unconfirmed request older than 23 hours requires a billing review and returns 409; it is not blindly sent again. Invalid attempts can reserve their key; use a new key after correcting a validation-only error. 
- 20 writes and 120 reads per minute per authenticated account and selected account, plus IP/global API limits. A 429 response includes `Retry-After: 60`. 
- JSON object only for writes, maximum 64 KB/depth 16; no query parameters, uploads, raw cards, credentials, custom provider URLs or unknown fields. GET filters are explicitly allow-listed; pages contain at most 100 records.

### Endpoints

GET`/subscriptions`

List this account’s subscription snapshots, with pagination.

POST`/subscriptions`

Create an agreement with billing phases, an existing price, or a simple new price. Returns a hosted customer review link; no automatic activation or email/SMS.

GET`/subscriptions/{id}`

Read one owned subscription and its agreed phases.

PATCH / PUT`/subscriptions/{id}`

Change name/email only before an advanced agreement has been reviewed. Billing status and agreed pricing are not writable.

POST`/subscriptions/{id}/sync`

Refresh verified billing status. Does not authorise a card or activate an unsigned agreement.

POST`/subscriptions/{id}/cancel`

Confirm immediate cancellation through the billing workflow. No automatic refund, invoice settlement or proration.

GET`/subscriptions/products`

List product IDs and names. This is the recurring catalogue, separate from /products (NeroPOS).

POST`/subscriptions/products`

Create a product with up to six prices, or add prices to an owned product_id. Existing prices/agreements stay unchanged.

GET`/subscriptions/prices`

List full pricing rules. Optional product_id and active=0/1 filters.

POST`/subscriptions/prices/{priceId}/archive`

Archive an owned price for new agreements. Send {}. Existing agreements keep their price.

GET`/subscriptions/discounts`

List owned discount codes and availability.

POST`/subscriptions/discounts`

Create a percentage/amount code with duration, product, expiry and redemption limits.

PATCH`/subscriptions/discounts/{discountId}`

Enable/disable a code using {"active":true/false}; discountId is the id returned by creation/listing.

GET`/subscriptions/{id}/usage`

List that subscription’s usage ledger. accepted means recorded for processing, not paid.

POST`/subscriptions/{id}/usage`

Record measured usage for the current open billing period. Returns HTTP 202.

### Products and prices

| Field | Type | Rules |
| --- | --- | --- |
| product_name | string | Required; 1–190 characters. For additional prices, product_id must belong to the selected account. |
| prices | array | 1–6 price objects. Fixed, per_unit, volume, graduated or metered. |
| prices[].model | enum | flat: one fixed amount; per_unit: amount × quantity; volume: total quantity chooses one tier; graduated: each band is priced separately; metered: measured units billed in arrears. |
| prices[].label / unit | string | Required; label up to 80 characters, unit up to 40. |
| prices[].currency | enum | GBP, EUR or USD. Amounts are major currency units, not pennies/cents. |
| prices[].interval / interval_count | enum / integer | day (1–365), week (1–52), month (1–12), year (1). Required; billing periods cannot exceed one year. |
| prices[].amount | decimal | Required for flat/per_unit/metered. 0–9,999,999; flat has up to two decimal places, other unit rates up to four. Decimal strings are recommended. |
| prices[].tiers | array | Required for volume/graduated: 2–8 ordered tiers with up_to, amount and optional flat_amount. Increasing integer limits up to 1,000,000,000; final up_to is "inf". Flat tier fees support two decimal places. |
| prices[].metered | boolean | Optional for volume/graduated: true uses measured usage in arrears. The metered model is always measured usage. |

### Create an agreement

| Field | Type | Rules |
| --- | --- | --- |
| customer_name / customer_email | string / email | Required; maximum 190 characters each. |
| phases | array | 1–6 phases, all using active prices from this account, the same product, currency and billing period. Do not mix measured usage with quantity pricing. |
| phases[].price_id | string | Required; use an ID returned by /subscriptions/prices or product creation. |
| phases[].quantity | integer | Required; 1–1,000,000. Fixed and measured-usage prices use quantity 1; licensed prices multiply by the agreed quantity. |
| phases[].cycles | integer / null | Introductory phases: 1–36 billing periods. Final phase: null or omitted, continuing until cancellation. |
| start_date | YYYY-MM-DD | Optional: today or a future date less than one year away, using the application’s configured timezone. Omit for activation after customer authorisation. |
| offer_code | string | Optional; 3–24 letters/digits. Code eligibility is checked again when the customer reviews the agreement. |
| Simple creation | alternative | Instead of phases, send an existing price_id with optional quantity, or product_name/amount/currency/interval with optional interval_count/quantity. Do not mix these forms. Both return the same hosted consent flow. |

For “£10 now, £20 from next month”, create two monthly prices on one product, then use cycles=1 on the £10 phase and cycles=null on the £20 phase. “Now” means after customer authorisation, unless a future start date was agreed. The rules are fixed in the agreement; catalogue changes do not silently change an existing customer’s terms.

### Discount codes

| Field | Type | Rules |
| --- | --- | --- |
| code | string | Required; 3–24 letters/digits, normalised to uppercase. Code terms cannot be overwritten; create a new code for new terms. |
| type / value | enum / decimal | percent: greater than 0 and at most 100. amount: positive major currency amount with two decimals, at most 9,999,999. |
| currency | enum | Required: GBP/EUR/USD. For amount discounts it must match the agreement currency. |
| cycles | integer | Required: 1–36 monthly bills, or 0 for ongoing. These codes apply only to interval=month, interval_count=1 agreements. |
| product_id | string / null | Optional; restrict to one product belonging to this account. |
| max_redemptions | integer / null | Optional; 1–1,000,000, enforced when the offer is redeemed. |
| expires_at | YYYY-MM-DD / null | Optional; today or later, valid through the end of that day in the application’s configured timezone. |
| Presets | examples | First month free: percent/100/cycles=1. Three months free: percent/100/cycles=3. First month half price: percent/50/cycles=1. |

Disabling a code prevents new uses. Existing agreements keep their agreed discount. When limited cycles end, the agreed undiscounted phase price resumes automatically. Use `offer_code` on creation, or let the customer enter an eligible code during review.

### Usage events

| Field | Type | Rules |
| --- | --- | --- |
| quantity | integer | Required; 1–1,000,000,000 measured units. The API never accepts a caller-supplied charge amount. |
| reference | string | Required; 3–80 letters, digits, dot, underscore or hyphen. Unique business-event reference across this merchant. Retrying the same event does not bill twice, including with a new HTTP key. |
| used_at | ISO 8601 timestamp | Required with timezone, e.g. 2026-10-15T12:00:00+00:00. Within the past 34 days and the current open billing period; future/closed-period events are rejected. |
| Eligibility | rule | Only active, unpaused subscriptions whose current price uses measured usage. Negative adjustments and retroactive closed-period billing are not supported. |

### Read results and lifecycle

List endpoints return `{success, data: [...], meta: {current_page, per_page, total, last_page}}`. Use `page` (1–10,000) and `per_page` (1–100). Subscriptions accept `q` (up to 190 characters) and a supported `status`; prices accept `product_id` and `active=0/1`.

Subscription results expose nested `customer`, current price and period fields, `checkout_url`, `portal_url`, `invoice_status`, and, for advanced agreements, the agreed `phases`, start date and offer code. Tiered/metered billing cannot be represented by the top-level `amount` alone; use pricing rules and the final invoice. A subscription’s `active` status is not proof that its latest invoice was paid.

The customer must review the agreement, authorise their card and confirm activation in the hosted flow. API creation and sync do not bypass that flow. Checkout/portal URLs contain private customer tokens; give them only to that customer and do not put them in public logs or analytics. Subscription status is read from the billing workflow; it cannot be set to paid/active/canceled with PATCH.

### Compatibility with the previous subscription API

GET/list paths and simple amount/price creation remain available. New requests always use the advanced hosted agreement flow. Writes now require the bound signature. PATCH/PUT permit only `customer_name`/`customer_email` on a new, unreviewed agreement. Existing legacy records remain readable but are not silently migrated. Use POST `/{id}/cancel` with `{"confirm":true}` for cancellation; DELETE is not supported.

Use `start_date`, billing phases and discount codes instead of legacy `trial_days`, `first_charge_date` or `bill_day_of_month`. Unsupported fields such as body account routing, metadata, customer_phone, caller-supplied status, provider IDs and return URLs are rejected rather than silently ignored. Update older clients before switching these routes to this contract.

### HTTP results

| Status | Meaning | Action |
| --- | --- | --- |
| 200 / 201 / 202 | Success | Read/update, creation, or accepted usage respectively. |
| 400 / 401 | Request / authentication | Missing retry key, HTTPS/signature/timestamp/key failure. |
| 403 / 404 | Access / ownership | Account access denied or resource not found in the selected account. |
| 409 | Conflict | Different data for a used key, an immutable/consented agreement, or an old unconfirmed write requiring review. |
| 413 / 415 / 422 | Input | Size, content type, unknown fields, invalid pricing/offer/usage or lifecycle constraints. |
| 429 | Rate limit | Retry after the documented delay, preserving the operation key. |
| 503 | Unconfirmed / unavailable | The outcome is not confirmed. Check the response reference; retry only the same logical operation/key. Provider exceptions and secrets are not returned. |

Server-side Node.js · signed product creation

```
// Server-side Node.js (.mjs). Never place this secret in browser code.
import { createHmac } from 'node:crypto';
const secret = process.env.NEROPAY_SECRET_KEY;
if (!secret) throw new Error('Missing NEROPAY_SECRET_KEY');

async function subscriptionWrite(method, path, payload, idempotencyKey, account = '') {
  const rawBody = JSON.stringify(payload);
  const timestamp = Math.floor(Date.now() / 1000).toString();
  const message = [timestamp, method, path, idempotencyKey, account, rawBody].join('\n');
  const signature = createHmac('sha256', secret).update(message, 'utf8').digest('hex');
  const response = await fetch('https://eu.neropay.app' + path, {
    method, redirect: 'error',
    headers: {
      Authorization: `Bearer ${secret}`,
      'Content-Type': 'application/json', Accept: 'application/json',
      'Idempotency-Key': idempotencyKey,
      'X-NeroPay-Timestamp': timestamp,
      'X-NeroPay-Signature': signature,
      ...(account ? {'NeroPay-Account': account} : {})
    },
    body: rawBody
  });
  const result = await response.json();
  if (!response.ok) throw new Error(`${response.status}: ${result.error?.code}`);
  return result.data;
}

// Persist this key before sending. Reuse it with the exact same logical request on retry.
const created = await subscriptionWrite('POST', '/v2/subscriptions/products', {
  product_name: 'Monthly membership',
  prices: [
    {model:'flat', label:'Introductory', unit:'membership', amount:'10.00',
     currency:'GBP', interval:'month', interval_count:1},
    {model:'flat', label:'Standard', unit:'membership', amount:'20.00',
     currency:'GBP', interval:'month', interval_count:1}
  ]
}, 'catalog-operation-0001');
// Store created.product_id and created.price_ids for the agreement request.
```

Server-side PHP · signed agreement creation

```
<?php
// Server-side PHP. Sign and send the same exact raw JSON bytes.
function subscriptionWrite(string $method, string $path, array $payload,
    string $key, string $account = ''): array {
    $secret = getenv('NEROPAY_SECRET_KEY');
    if (!$secret) throw new RuntimeException('Missing NEROPAY_SECRET_KEY');
    $raw = json_encode($payload ?: (object) [], JSON_UNESCAPED_SLASHES | JSON_THROW_ON_ERROR);
    $timestamp = (string) time();
    $message = implode("\n", [$timestamp, $method, $path, $key, $account, $raw]);
    $headers = ['Authorization: Bearer '.$secret, 'Content-Type: application/json',
        'Accept: application/json', 'Idempotency-Key: '.$key,
        'X-NeroPay-Timestamp: '.$timestamp,
        'X-NeroPay-Signature: '.hash_hmac('sha256', $message, $secret)];
    if ($account !== '') $headers[] = 'NeroPay-Account: '.$account;
    $curl = curl_init('https://eu.neropay.app'.$path);
    curl_setopt_array($curl, [CURLOPT_CUSTOMREQUEST=>$method,
        CURLOPT_HTTPHEADER=>$headers, CURLOPT_POSTFIELDS=>$raw,
        CURLOPT_RETURNTRANSFER=>true, CURLOPT_FOLLOWLOCATION=>false,
        CURLOPT_CONNECTTIMEOUT=>10, CURLOPT_TIMEOUT=>60]);
    $body = curl_exec($curl);
    $status = curl_getinfo($curl, CURLINFO_RESPONSE_CODE);
    if ($body === false) { curl_close($curl); throw new RuntimeException('Unknown result; retry the same key.'); }
    curl_close($curl);
    $result = json_decode($body, true, 32, JSON_THROW_ON_ERROR);
    if ($status >= 400) throw new RuntimeException($status.': '.($result['error']['code'] ?? 'request_failed'));
    return $result['data'];
}

$agreement = subscriptionWrite('POST', '/v2/subscriptions', [
    'customer_name'=>'Alex Example', 'customer_email'=>'alex@example.com',
    'phases'=>[
        ['price_id'=>'price_intro', 'quantity'=>1, 'cycles'=>1],
        ['price_id'=>'price_standard', 'quantity'=>1, 'cycles'=>null]
    ]
], 'agreement-operation-0001');
// Replace the example price IDs with returned catalogue IDs.
// Present $agreement['checkout_url'] to this customer for explicit review/authorisation.
```

£10 first month, then £20 · HTTP payload

```
POST /v2/subscriptions
Content-Type: application/json
Authorization: Bearer <server-secret>
Idempotency-Key: <persisted-unique-operation-key>
X-NeroPay-Timestamp: <current-unix-seconds>
X-NeroPay-Signature: <bound-hmac-sha256>

{
  "customer_name": "Alex Example",
  "customer_email": "alex@example.com",
  "phases": [
    {
      "price_id": "price_intro",
      "quantity": 1,
      "cycles": 1
    },
    {
      "price_id": "price_standard",
      "quantity": 1,
      "cycles": null
    }
  ]
}
```

Usage-based graduated price · HTTP payload

```
POST /v2/subscriptions/products
Content-Type: application/json
Authorization: Bearer <server-secret>
Idempotency-Key: <persisted-unique-operation-key>
X-NeroPay-Timestamp: <current-unix-seconds>
X-NeroPay-Signature: <bound-hmac-sha256>

{
  "product_name": "API requests",
  "prices": [
    {
      "model": "graduated",
      "label": "Requests",
      "unit": "request",
      "currency": "GBP",
      "interval": "month",
      "interval_count": 1,
      "metered": true,
      "tiers": [
        {
          "up_to": 1000,
          "amount": "0.05",
          "flat_amount": "0"
        },
        {
          "up_to": "inf",
          "amount": "0.02",
          "flat_amount": "0"
        }
      ]
    }
  ]
}
```

Three months free · HTTP payload

```
POST /v2/subscriptions/discounts
Content-Type: application/json
Authorization: Bearer <server-secret>
Idempotency-Key: <persisted-unique-operation-key>
X-NeroPay-Timestamp: <current-unix-seconds>
X-NeroPay-Signature: <bound-hmac-sha256>

{
  "code": "WELCOME3",
  "type": "percent",
  "value": 100,
  "currency": "GBP",
  "cycles": 3,
  "max_redemptions": 100
}
```

Usage event · HTTP payload

```
POST /v2/subscriptions/123/usage
Content-Type: application/json
Authorization: Bearer <server-secret>
Idempotency-Key: <persisted-unique-operation-key>
X-NeroPay-Timestamp: <current-unix-seconds>
X-NeroPay-Signature: <bound-hmac-sha256>

{
  "quantity": 250,
  "reference": "order-1045-requests",
  "used_at": "2026-10-15T12:00:00+00:00"
}
```

Cancel an agreement · HTTP payload

```
POST /v2/subscriptions/123/cancel
Content-Type: application/json
Authorization: Bearer <server-secret>
Idempotency-Key: <persisted-unique-operation-key>
X-NeroPay-Timestamp: <current-unix-seconds>
X-NeroPay-Signature: <bound-hmac-sha256>

{
  "confirm": true
}
```

Public agreement response · excerpt

```
{
  "success": true,
  "data": {
    "id": 123,
    "status": "pending",
    "customer": {
      "name": "Alex Example",
      "email": "alex@example.com",
      "phone": null
    },
    "checkout_url": "https://eu.neropay.app/s/subscription/agreement/<customer-token>",
    "agreement": {
      "state": "pending",
      "start_date": null,
      "offer_code": "",
      "phases": [
        {
          "price_id": "price_intro",
          "quantity": 1,
          "cycles": 1,
          "pricing": {
            "model": "flat",
            "amount": "10",
            "currency": "GBP",
            "interval": "month",
            "interval_count": 1
          }
        },
        {
          "price_id": "price_standard",
          "quantity": 1,
          "cycles": null,
          "pricing": {
            "model": "flat",
            "amount": "20",
            "currency": "GBP",
            "interval": "month",
            "interval_count": 1
          }
        }
      ]
    }
  }
}
```

## Transactions

List, retrieve, refund and cancel transactions. High-risk transactions can expose proof and reserve state, so check transaction state before treating funds as fully available.

POST`/transactions`

Legacy alias for creating hosted/API transaction where enabled.

**Note:** for new online checkout integrations, use `POST /hosted-payments` or `POST /payment-links`. The transaction table below describes transaction records returned by list/retrieve/refund/cancel flows.

GET`/transactions`

Lists transactions.

GET`/transactions/{id}`

Retrieves one transaction.

POST`/transactions/{id}/refund`

Refunds a transaction.

POST`/transactions/{id}/cancel`

Cancels a pending transaction.

### Transaction response attributes

| Field | Type | Description |
| --- | --- | --- |
| id | integer | Internal transaction ID. |
| account_id | string | Merchant or connected account ID. |
| reference | string | NeroPay or merchant reference. |
| np_main_trx | string | Processor/payment identifier where available. |
| status | enum | pending, succeeded, failed, cancelled, refunded. |
| is_reserve | boolean | Whether funds are currently held in reserve. |
| reserve_amount | decimal | Amount held from available balance. |
| proof_status | enum | requested, submitted, approved, rejected or null. |

A succeeded transaction can still have reserve/proof state. Do not show funds as fully available until reserve_amount is zero and proof status is clear.

List transactions

```
GET /v2/transactions?per_page=25&status=succeeded
```

Transaction response

```
{
  "success": true,
  "data": {
    "transaction": {
      "id": 5663230,
      "account_id": "NPVesdTGASO23_547878",
      "reference": "NP-EGQX9271-cf013",
      "np_main_trx": "pi_3TENnmPwvCBPpp9M1H3YZnSr",
      "status": "succeeded",
      "amount": 1.00,
      "currency": "GBP",
      "is_reserve": false,
      "reserve_amount": 0,
      "proof_status": "approved",
      "created_at": "2026-03-24T05:47:00.000000Z"
    }
  }
}
```

Refund request

```
POST /v2/transactions/5663230/refund
{
  "amount": 1.00,
  "reason": "requested_by_customer"
}
```

## Transaction Proof API

Submit proof files and buyer/shipping details when a transaction is flagged by high-value or risk logic. This API is available for merchants and connected accounts.

GET`/transactions/{id}/proof`

Returns proof request and transaction reserve state.

POST`/transactions/{id}/proof`

Submits proof details and invoice/shipping file.

### POST proof upload request attributes

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| invoice_file | file | Optional | JPG, PNG, PDF or WEBP. Required when staff/platform requested an invoice file. |
| courier_company | string | Optional | Shipping courier. |
| tracking_number | string | Optional | Tracking number. |
| buyer_firstname | string | Optional | Buyer first name. |
| buyer_lastname | string | Optional | Buyer last name. |
| buyer_phone | string | Optional | Buyer phone number. |
| buyer_email | string | Optional | Buyer email. |
| merchant_notes | text | Optional | Merchant notes for the review team. |

Submitting proof publishes `transaction.proof_submitted`. Staff approval/rejection publishes `transaction.proof_approved` or `transaction.proof_rejected`. Approved reserve releases can publish `balance.reserve_released`.

Get proof request response

```
{
  "success": true,
  "data": {
    "transaction": {
      "id": 5663230,
      "account_id": "NPVesdTGASO23_547878",
      "reference": "NP-EGQX9271-cf013",
      "np_main_trx": "pi_3TENnmPwvCBPpp9M1H3YZnSr",
      "status": "succeeded",
      "amount": 1,
      "remark": "make_payment",
      "details": "Payment successful via Online | PI: pi_3TENnmPwvCBPpp9M1H3YZnSr",
      "is_reserve": false,
      "reserve_amount": 0,
      "created_at": "2026-03-24T05:47:00.000000Z"
    },
    "proof_request": {
      "id": 925,
      "transaction_id": 5663230,
      "status": "approved",
      "courier_company": null,
      "tracking_number": null,
      "buyer": {
        "firstname": null,
        "lastname": null,
        "phone": null,
        "email": null
      },
      "has_invoice_file": false,
      "merchant_notes": null,
      "staff_notes": "test"
    }
  }
}
```

Submit proof multipart

```
POST /v2/transactions/5663230/proof
Content-Type: multipart/form-data

invoice_file=@invoice.pdf
courier_company=Royal Mail
tracking_number=RM123456789GB
buyer_firstname=Jane
buyer_lastname=Customer
buyer_phone=+447700900000
buyer_email=jane@example.com
merchant_notes=Invoice and delivery information attached.
```

## Balances

Read available, pending, instant, standard, reserve and NeroBusiness balances. Reserve balance is important for high-risk and proof-held funds.

GET`/balances`

Returns wallet balances.

GET`/balance`

Alias of /balances.

### Balance response attributes

| Field | Type | Description |
| --- | --- | --- |
| `wallets[].currency` | object | Wallet currency `id`, ISO `code` and display `symbol`. |
| `wallets[].available` | decimal | Current main wallet balance. |
| `wallets[].pending_funds` | decimal | Funds still subject to settlement timing. |
| `wallets[].standard_available` | decimal | Main wallet balance after pending settlement funds are excluded. |
| `wallets[].instant_available` | decimal | Current instant availability reported for the wallet. |
| `wallets[].reserve_balance` | decimal | Funds held in reserve due to proof, risk or compliance controls. |
| `wallets[].nerobusiness_balance` | decimal | Latest locally recorded available balance for the merchant's NeroBusiness Financial Account in this wallet currency. |
| `nerocard` / `issuing` | object | Read-only card programme summaries where available. |

**Balance integrity:** balance values are read-only snapshots scoped to the authenticated merchant or the connected account selected by `NeroPay-Account`. Never accept a client-supplied balance as proof of funds and never attempt to change balances through generic resource endpoints; use the documented money-movement endpoints and reconcile completed operations.

Balances response

```
{
  "success": true,
  "data": {
    "account_id": "NPVesdTGASO23_547878",
    "wallets": [
      {
        "id": 91,
        "account_id": "NPVesdTGASO23_547878",
        "currency": { "id": 1, "code": "GBP", "symbol": "£" },
        "available": 50.01,
        "reserve_balance": 0,
        "nerobusiness_balance": 1250.75,
        "pending_funds": 0,
        "standard_available": 50.01,
        "instant_available": 50.01,
        "updated_at": "2026-09-03T10:15:00+01:00"
      }
    ]
  }
}
```

## Wallet Transactions

Audit wallet movement separately from payment transactions. Use this for balance ledger reconciliation.

GET`/wallet-transactions`

Lists wallet balance transactions.

GET`/wallet-transactions/{id}`

Retrieves one wallet ledger row.

### Wallet ledger response attributes

| Field | Type | Description |
| --- | --- | --- |
| txn_id | string | Ledger transaction ID. |
| type | enum | credit/debit/hold/release depending on implementation. |
| amount | decimal | Amount moved. |
| balance_before | decimal | Balance before movement where available. |
| balance_after | decimal | Balance after movement where available. |
| notes | text | Internal or merchant-facing notes. |

Wallet transactions request

```
GET /v2/wallet-transactions?per_page=25
```

Wallet transaction response

```
{
  "success": true,
  "data": [
    {
      "id": 901,
      "txn_id": "wb_901",
      "currency": "GBP",
      "amount": 15.99,
      "type": "credit",
      "status": "completed",
      "notes": "Payment received",
      "created_at": "2026-04-28T18:00:00.000000Z"
    }
  ]
}
```

## Payouts & Schedule

Create and inspect payout requests and manage payout schedule. Payout creation can trigger proof/risk checks and Perfex review tasks.

GET`/payouts`

Lists payouts.

POST`/payouts`

Creates a payout request.

POST`/payouts/request`

Legacy alias for create payout.

GET`/payouts/{id}`

Retrieves one payout.

GET`/payout-schedule`

Gets payout schedule.

PATCH`/payout-schedule`

Updates payout schedule.

GET`/transfer-schedule`

Legacy alias of payout schedule.

PATCH`/transfer-schedule`

Legacy alias of update payout schedule.

### POST/PATCH payout request attributes

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| amount | decimal | Required | Payout amount. |
| currency | enum | Required | Currency. |
| transfer_speed | enum | Optional | instant or standard. |
| bank_account_id | string | Optional | Destination bank reference. |
| reference | string | Optional | Statement or internal reference. |

Transfers and payouts should not bypass risk controls. High-risk payout requests can create proof requirements, risk flags, tasks and webhooks.

Create payout

```
POST /v2/payouts
{
  "amount": 25.00,
  "currency": "GBP",
  "transfer_speed": "instant",
  "bank_account_id": "ba_123",
  "reference": "PAYOUT-1001"
}
```

Schedule response

```
{
  "success": true,
  "data": {
    "mode": "manual",
    "instant_enabled": true,
    "standard_enabled": true
  }
}
```

## Disputes

List and inspect payment disputes, submit final counter evidence or explicitly accept a claim. Responses use NeroPay case IDs and never expose processor account IDs, provider object IDs, storage paths or internal ledger fields.

GET`/disputes`

Lists account-scoped cases. Filters: `status`, `action_required`, `q` and `per_page` (maximum 50).

GET`/disputes/{case_id}`

Retrieves one case such as `DISPUTE-1057`.

POST`/disputes/{case_id}/counter`

Submits final counter-dispute evidence to NeroPay.

POST`/disputes/{case_id}/accept`

Accepts the claim. This action is final and cannot be reversed.

**Connected accounts:** a platform may send `NeroPay-Account: {connected_account_id}`. NeroPay verifies that the account belongs to the authenticated platform. Supplying another merchant ID returns not found/forbidden rather than cross-account data.

**Mandatory write protection:** counter and accept requests require a unique `Idempotency-Key`, a timestamp no more than five minutes old and `X-NeroPay-Signature = HMAC-SHA256(timestamp + "." + exact_raw_body, secret_key)`. They are additionally limited to six attempts per minute. Reusing an idempotency key returns `409 duplicate_request`.

### Counter evidence attributes

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `product_description` | string | Required | What was sold or delivered; maximum 20,000 characters. |
| `evidence_statement` | string | Required | Clear explanation supporting the counter dispute; maximum 20,000 characters. |
| `service_date` | date | Optional | ISO date, for example `2026-09-01`. |
| `shipping_tracking_number` | string | Optional | Carrier tracking reference; maximum 255 characters. |
| `proof_file` | file | Optional | PDF, JPG or PNG, maximum 4.5 MB. Use `multipart/form-data` and sign the exact multipart body. |
| `confirm_submission` | boolean | Required | Must be `true`. Evidence is submitted as final. |

For accept requests, send `{"confirm_accept":true}`. Original cardholder claim artefacts remain available only in the authenticated dashboard secure viewer; API v2 does not proxy or publish provider-hosted claim documents.

List action-required cases

```
GET /v2/disputes?action_required=true&per_page=25
Authorization: Bearer neropay_secret_...
NeroPay-Account: NPVesdTGASO23_547878
```

Safe dispute response

```
{
  "success": true,
  "data": [
    {
      "case_id": "DISPUTE-1057",
      "transaction_reference": "NP-EGQX9271-cf013",
      "reason": "fraudulent",
      "status": "needs_response",
      "amount": 42.00,
      "currency": "GBP",
      "action_required": true,
      "response_deadline": "2026-09-15T00:59:00+01:00",
      "merchant_action": "none",
      "evidence_files": []
    }
  ]
}
```

Submit counter evidence

```
POST /v2/disputes/DISPUTE-1057/counter
Authorization: Bearer neropay_secret_...
Idempotency-Key: dispute-1057-counter-v1
X-NeroPay-Timestamp: 1788757200
X-NeroPay-Signature: <hmac_sha256>
Content-Type: application/json

{
  "product_description": "Order #1842, delivered goods",
  "evidence_statement": "Delivery was completed and acknowledged.",
  "service_date": "2026-09-01",
  "shipping_tracking_number": "TRACK-1842",
  "confirm_submission": true
}
```

## Loans

Loans and financing records are grouped under Money Movement and are read-only through API v2.

GET`/loans`

Lists loan/financing records.

GET`/loans/{id}`

Retrieves one loan record.

### Loan response attributes

| Field | Type | Description |
| --- | --- | --- |
| offer_id | string | Financing offer identifier. |
| status | enum | offer, active, paid, declined etc. |
| campaign_type | string | Campaign/category. |
| financing_type | string | Financing type. |
| created_at | datetime | Record creation time. |

List loans

```
GET /v2/loans?per_page=25
```

Loan response

```
{
  "success": true,
  "data": [
    {
      "id": 88,
      "offer_id": "offer_123",
      "status": "active",
      "campaign_type": "working_capital",
      "financing_type": "merchant_cash_advance"
    }
  ]
}
```

## NeroDisburse Bank Accounts

Bank accounts are documented under NeroDisburse. API responses never expose full account numbers, sort codes, IBANs or routing numbers.

GET`/bank-accounts`

Lists safe payout bank references.

POST`/bank-accounts`

Adds a payout bank account through the NeroDisburse bridge.

GET`/nerodisburse/bank-accounts`

Lists NeroDisburse bank accounts.

POST`/nerodisburse/bank-accounts`

Creates a NeroDisburse bank account.

### POST bank-account request attributes

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| account_name | string | Required | Display name for the destination. |
| bank_name | string | Optional | Bank name. |
| sort_code | string | Optional | GB sort code for GB bank accounts. |
| account_number | string | Optional | GB account number for GB bank accounts. |
| iban | string | Optional | IBAN for supported international accounts. |
| currency | enum | Required | Currency. |
| use_for | enum | Optional | supplier, payout, merchant etc. |
| is_default | boolean | Optional | Set as default destination. |

Create GB bank account

```
POST /v2/nerodisburse/bank-accounts
{
  "account_name": "Main payout account",
  "bank_name": "Demo Bank",
  "sort_code": "000000",
  "account_number": "00012345",
  "currency": "GBP",
  "use_for": "supplier",
  "is_default": true,
  "full_name": "Jane Merchant",
  "email": "jane@example.com",
  "country": "GB"
}
```

Create IBAN account

```
POST /v2/nerodisburse/bank-accounts
{
  "account_name": "EUR payout account",
  "iban": "LT121000011101001000",
  "currency": "EUR",
  "use_for": "supplier",
  "full_name": "Jane Merchant",
  "email": "jane@example.com",
  "country": "LT"
}
```

## NeroDisburse Payouts

Create supplier or connected disbursement payouts using saved bank accounts.

GET`/nerodisburse/payouts`

Lists NeroDisburse payouts.

POST`/nerodisburse/payouts`

Creates pending NeroDisburse payout.

GET`/nerodisburse/payouts/{id}`

Retrieves one NeroDisburse payout.

### POST payout request attributes

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| amount | decimal | Required | Amount. |
| currency | enum | Required | Currency. |
| transfer_speed | enum | Optional | instant or standard. |
| bank_account_id | string | Required | Saved bank account reference. |
| reference | string | Optional | Payout reference. |
| note | text | Optional | Supplier payout note. |

Create NeroDisburse payout

```
POST /v2/nerodisburse/payouts
{
  "amount": 10.00,
  "currency": "GBP",
  "transfer_speed": "instant",
  "bank_account_id": "ba_123",
  "reference": "ND1001",
  "note": "Supplier payout"
}
```

NeroDisburse payout response

```
{
  "success": true,
  "data": {
    "payout": {
      "id": 3021,
      "status": "pending",
      "amount": 10.00,
      "currency": "GBP",
      "reference": "ND1001"
    }
  }
}
```

## NeroDisburse Payments

Create disbursement payment records where enabled by the NeroDisburse controller.

POST`/nerodisburse/payments`

Creates a NeroDisburse payment/disbursement request.

Use this endpoint where your integration needs NeroDisburse payment creation rather than a standard payout request. Risk and payout availability rules still apply.

Create NeroDisburse payment

```
POST /v2/nerodisburse/payments
{
  "amount": 10.00,
  "currency": "GBP",
  "recipient": {
    "name": "Supplier Ltd",
    "email": "supplier@example.com"
  },
  "bank_account_id": "ba_123",
  "reference": "SUP-1001"
}
```

## Terminal Payments

Send in-person payment actions to a registered terminal reader.

POST`/terminal/payments`

Creates and sends a terminal payment action.

POST`/terminal/readers/{reader_id}/cancel-action`

Cancels the current reader action.

### POST terminal payment request attributes

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| amount | decimal | Required | Amount. |
| currency | enum | Required | Currency. |
| reader_id | string | Required | Terminal reader ID. |
| location_id | string | Required | Terminal location ID. |
| description | text | Optional | Payment description. |
| reference | string | Optional | Reference shown in transaction history. |

Create terminal payment

```
POST /v2/terminal/payments
{
  "amount": 12.50,
  "currency": "GBP",
  "reader_id": "tmr_123",
  "location_id": "tml_123",
  "description": "Counter payment",
  "reference": "TERM-1001"
}
```

Cancel reader action

```
POST /v2/terminal/readers/tmr_123/cancel-action
{}
```

## Terminal Locations

Create, list and manage terminal locations. Location creation uses the terminal bridge route.

GET`/locations`

Lists terminal/NeroPOS locations.

POST`/locations`

Legacy alias to create terminal location.

GET`/terminal/locations`

Lists terminal locations.

POST`/terminal/locations`

Creates a terminal location.

GET`/locations/{id}`

Retrieves one location.

PATCH`/locations/{id}`

Updates a location.

DELETE`/locations/{id}`

Deletes where enabled.

### POST/PATCH location request attributes

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| display_name | string | Required | Location display name. |
| line1 | string | Required | Address line 1. |
| city | string | Required | City. |
| postal_code | string | Required | Postal code. |
| country | enum | Required | ISO country code. |
| is_default | boolean | Optional | Set default location. |
| tipping_enabled | boolean | Optional | Enable tipping for terminal/POS flows. |

Create location

```
POST /v2/terminal/locations
{
  "display_name": "Main Store",
  "line1": "1 Demo Street",
  "city": "London",
  "postal_code": "SW1A 1AA",
  "country": "GB",
  "is_default": true
}
```

Location response

```
{
  "success": true,
  "data": {
    "id": 100,
    "display_name": "Main Store",
    "neropay_location_id": "tml_...",
    "city": "London",
    "country": "GB"
  }
}
```

## Terminal Readers

Register, list and remove terminal readers.

GET`/readers`

Lists readers.

POST`/readers`

Legacy alias to register reader.

GET`/terminal/readers`

Lists terminal readers.

POST`/terminal/readers`

Registers a reader.

DELETE`/terminal/readers/{id}`

Deletes/unlinks a reader.

### POST reader registration request attributes

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| label | string | Required | Reader label. |
| location_id | string | Required | Location ID. |
| registration_code | string | Required | Pairing/registration code from device. |
| reader_serial_number | string | Read-only | Device serial where available. |
| reader_status | enum | Read-only | online/offline/unknown. |

Create reader

```
POST /v2/terminal/readers
{
  "label": "Front counter",
  "location_id": "tml_123",
  "registration_code": "simulated-wpe"
}
```

Reader response

```
{
  "success": true,
  "data": {
    "id": 44,
    "label": "Front counter",
    "reader_id": "tmr_123",
    "status": "online"
  }
}
```

## Devices

Read-only terminal/device records for fleet visibility.

GET`/devices`

Lists device rows.

GET`/devices/{id}`

Retrieves one device.

### Device response attributes

| Field | Type | Description |
| --- | --- | --- |
| device_id | string | Internal device ID. |
| reader_id | string | Linked reader ID. |
| reader_serial | string | Serial number. |
| reader_label | string | Display label. |
| status | enum | Device status. |

List devices

```
GET /v2/devices?per_page=25
```

Device response

```
{
  "success": true,
  "data": [
    { "id": 10, "device_id": "dev_123", "reader_id": "tmr_123", "reader_label": "Front counter" }
  ]
}
```

## NeroCard Cards

NeroCard card records are read-only in API v2 and are separate from payment cards.

GET`/nerocards`

Lists NeroCard cards.

GET`/nerocards/{id}`

Retrieves one NeroCard card.

### Card response attributes

| Field | Type | Description |
| --- | --- | --- |
| cardholder_name | string | Cardholder name. |
| brand | string | Card brand. |
| last4 | string | Last four digits. |
| status | enum | active, inactive, cancelled, blocked etc. |
| card_pin | hidden | Never returned. |

List NeroCards

```
GET /v2/nerocards?status=active
```

NeroCard response

```
{
  "success": true,
  "data": [
    {
      "id": 77,
      "cardholder_name": "Jane Merchant",
      "brand": "Visa",
      "last4": "4242",
      "status": "active"
    }
  ]
}
```

## NeroCard Cardholders

Read NeroCard cardholder records.

GET`/nerocardholders`

Lists cardholders.

GET`/nerocardholders/{id}`

Retrieves one cardholder.

### Cardholder response attributes

| Field | Type | Description |
| --- | --- | --- |
| name | string | Cardholder name. |
| email | string | Email. |
| phone | string | Phone. |
| neropay_cardholder_id | string | NeroPay cardholder identifier when present. |
| created_at | datetime | Creation time. |

List cardholders

```
GET /v2/nerocardholders?per_page=25
```

Cardholder response

```
{
  "success": true,
  "data": [
    { "id": 5, "name": "Jane Merchant", "email": "jane@example.com", "phone": "+447700900000" }
  ]
}
```

## NeroCard Transactions

Read NeroCard issuing transactions separately from wallet and payment transactions.

GET`/nerocard-transactions`

Lists issuing/NeroCard transactions.

GET`/nerocard-transactions/{id}`

Retrieves one NeroCard transaction.

### NeroCard transaction response attributes

| Field | Type | Description |
| --- | --- | --- |
| neropay_issuing_transaction_id | string | NeroPay issuing transaction ID. |
| merchant_name | string | Merchant name on the card transaction. |
| amount | decimal | Transaction amount. |
| merchant_amount | decimal | Original merchant amount where different. |
| platform_fee_amount | decimal | Platform fee amount if applied. |
| net_amount | decimal | Net ledger amount. |
| status | enum | pending, posted, reversed etc. |
| type | enum | purchase, refund, adjustment etc. |

List NeroCard transactions

```
GET /v2/nerocard-transactions?per_page=25
```

NeroCard transaction response

```
{
  "success": true,
  "data": [
    {
      "id": 1901,
      "neropay_issuing_transaction_id": "ipi_...",
      "merchant_name": "Demo Store",
      "amount": 12.50,
      "status": "posted",
      "type": "purchase"
    }
  ]
}
```

## NeroTrade API

NeroTrade API v2 lets eligible merchants create, retrieve, list and update wholesale trade records from their own systems.

**Subscription required:** NeroTrade endpoints require an active NeroTrade subscription and return `402 nerotrade_subscription_required` when the plan is not active for the merchant.

### Records covered

Trade accounts, product categories, products, inventory stock fields, drivers, deliveries, invoices, collections, statement views, campaign records, route plans and driver check-ins.

### Response envelope

Successful calls return `{ success: true, data: ... }`. List calls also include pagination `meta`. Failed calls return `{ success: false, error: { code, message } }`.

GET`/nerotrade/dashboard`

Returns NeroTrade summary, subscription status, stock alerts and operational totals.

GET`/nerotrade/{resource}`

Lists supported NeroTrade records for the authenticated merchant.

POST`/nerotrade/{resource}`

Creates a supported NeroTrade record where the resource supports creation.

GET`/nerotrade/{resource}/{id}`

Retrieves one supported NeroTrade record.

PATCH`/nerotrade/{resource}/{id}`

Updates one supported NeroTrade record.

Dashboard response

```
GET /v2/nerotrade/dashboard
{
  "success": true,
  "data": {
    "account_id": "NPVesdTGASO23_547878",
    "nerotrade_active": true,
    "subscription": {
      "id": 12,
      "account_id": "NPVesdTGASO23_547878",
      "status": "active",
      "current_period_end": "2026-06-18T00:00:00Z"
    },
    "stats": {
      "outstanding": 1280.50,
      "overdue": 210.00,
      "customers": 18,
      "drivers": 3,
      "products": 142,
      "due_this_week": 480.00,
      "stop_supply": 1,
      "failed_collections": 0,
      "campaigns": 4,
      "routes_today": 2
    },
    "inventory_alerts": []
  }
}
```

List response envelope

```
GET /v2/nerotrade/trade-accounts?limit=2
{
  "success": true,
  "data": [
    {
      "id": 104,
      "account_id": "NPVesdTGASO23_547878",
      "business_name": "Acme Builders Ltd",
      "contact_name": "Jane Smith",
      "email": "accounts@acme.example",
      "phone": "+447700900000",
      "payment_terms": "30_days",
      "credit_limit": "2500.00",
      "current_balance": "149.94",
      "overdue_balance": "0.00",
      "collection_method": "payment_link",
      "risk_status": "good",
      "stop_supply": 0,
      "created_at": "2026-07-18T10:00:00Z",
      "updated_at": "2026-07-18T10:00:00Z"
    }
  ],
  "meta": {
    "current_page": 1,
    "per_page": 2,
    "total": 1,
    "last_page": 1
  }
}
```

NeroTrade entitlement error

```
HTTP/1.1 402 Payment Required
{
  "success": false,
  "error": {
    "code": "nerotrade_subscription_required",
    "message": "NeroTrade is not active for this account. Activate the £99.95/month NeroTrade plan before using NeroTrade API endpoints."
  }
}
```

## NeroTrade Trade Accounts

Create and maintain wholesale customer accounts, credit terms, collection preferences and account risk status.

GET`/nerotrade/trade-accounts`

Lists trade accounts. Alias: `/nerotrade/customers`.

POST`/nerotrade/trade-accounts`

Creates a trade account.

GET`/nerotrade/trade-accounts/{id}`

Retrieves one trade account.

PATCH`/nerotrade/trade-accounts/{id}`

Updates account details, limits, terms, collection method or supply status.

### POST/PATCH trade account request attributes

| Field | Type | Description |
| --- | --- | --- |
| business_name | string | Required on create. Trade customer business name. |
| contact_name | string | Main contact name. |
| email | string | Billing or account email. |
| phone | string | Contact phone number. |
| billing_address | string | Billing address. |
| delivery_address | string | Default delivery address. |
| payment_terms | enum | cod, 7_days, 14_days, 30_days, weekly or monthly. |
| credit_limit | decimal | Approved trade account credit limit. |
| opening_balance | decimal | Optional opening balance on create. |
| collection_method | enum | Collection preference such as payment_link or bank_transfer. |
| risk_status | string | Internal account risk label, for example good or watch. |
| stop_supply | boolean | Stops supply for the account when true. |

Create trade account

```
POST /v2/nerotrade/trade-accounts
{
  "business_name": "Acme Builders Ltd",
  "contact_name": "Jane Smith",
  "email": "accounts@acme.example",
  "phone": "+447700900000",
  "billing_address": "10 Trade Road, Manchester, M1 1AA",
  "delivery_address": "10 Trade Road, Manchester, M1 1AA",
  "payment_terms": "30_days",
  "credit_limit": 2500.00,
  "opening_balance": 0,
  "collection_method": "payment_link",
  "risk_status": "good",
  "notes": "Preferred delivery before 12pm"
}
```

Create trade account response

```
HTTP/1.1 201 Created
{
  "success": true,
  "data": {
    "id": 104,
    "account_id": "NPVesdTGASO23_547878",
    "business_name": "Acme Builders Ltd",
    "contact_name": "Jane Smith",
    "email": "accounts@acme.example",
    "phone": "+447700900000",
    "billing_address": "10 Trade Road, Manchester, M1 1AA",
    "delivery_address": "10 Trade Road, Manchester, M1 1AA",
    "payment_terms": "30_days",
    "credit_limit": "2500.00",
    "current_balance": "0.00",
    "overdue_balance": "0.00",
    "collection_method": "payment_link",
    "risk_status": "good",
    "stop_supply": 0,
    "notes": "Preferred delivery before 12pm",
    "created_at": "2026-07-18T10:00:00Z",
    "updated_at": "2026-07-18T10:00:00Z"
  }
}
```

Update trade account response

```
PATCH /v2/nerotrade/trade-accounts/104
{
  "credit_limit": 3500.00,
  "payment_terms": "45_days",
  "risk_status": "good",
  "stop_supply": false
}

HTTP/1.1 200 OK
{
  "success": true,
  "data": {
    "id": 104,
    "account_id": "NPVesdTGASO23_547878",
    "business_name": "Acme Builders Ltd",
    "payment_terms": "45_days",
    "credit_limit": "3500.00",
    "risk_status": "good",
    "stop_supply": 0,
    "updated_at": "2026-07-18T11:10:00Z"
  }
}
```

## NeroTrade Products & Pricing

Manage trade product categories, SKUs, wholesale pricing, VAT settings and stock-tracked product records.

GET`/nerotrade/product-categories`

Lists trade product categories. Alias: `/nerotrade/categories`.

POST`/nerotrade/product-categories`

Creates a product category.

PATCH`/nerotrade/product-categories/{id}`

Updates a product category.

GET`/nerotrade/products`

Lists trade products and pricing.

POST`/nerotrade/products`

Creates a trade product.

GET`/nerotrade/products/{id}`

Retrieves one trade product.

PATCH`/nerotrade/products/{id}`

Updates product details, stock settings or pricing.

### POST/PATCH product and pricing request attributes

| Field | Type | Description |
| --- | --- | --- |
| name | string | Required product name. |
| base_unit | string | Required unit such as pcs, box, case or pallet. |
| unit_price | decimal | Required default trade unit price. |
| category_id | integer | Linked NeroTrade product category. |
| category_name | string | Creates or links a category by name when supplied. |
| sku | string | Stock keeping unit. |
| description | string | Product description. |
| vat_rate | decimal | VAT rate applied to the product. |
| vat_included | boolean | Whether price includes VAT. |
| track_stock | boolean | Enables inventory tracking for the product. |
| stock_qty | decimal | Current available stock. |
| reorder_level | decimal | Manual reorder threshold. |
| preferred_supplier | string | Supplier reference. |

Create product category response

```
POST /v2/nerotrade/product-categories
{
  "name": "Wholesale Boxes",
  "description": "Bulk trade products",
  "sort_order": 10,
  "active": true
}

HTTP/1.1 201 Created
{
  "success": true,
  "data": {
    "id": 12,
    "account_id": "NPVesdTGASO23_547878",
    "name": "Wholesale Boxes",
    "description": "Bulk trade products",
    "sort_order": 10,
    "active": 1,
    "created_at": "2026-07-18T10:05:00Z",
    "updated_at": "2026-07-18T10:05:00Z"
  }
}
```

Create product

```
POST /v2/nerotrade/products
{
  "category_id": 12,
  "name": "Trade Box 10kg",
  "sku": "TB-10KG",
  "description": "10kg wholesale box",
  "base_unit": "box",
  "unit_price": 24.99,
  "vat_rate": 20,
  "vat_included": false,
  "pack_unit": "case",
  "pack_quantity": 1,
  "track_stock": true,
  "stock_qty": 120,
  "reorder_level": 25,
  "preferred_supplier": "Acme Supply"
}
```

Create product response

```
HTTP/1.1 201 Created
{
  "success": true,
  "data": {
    "id": 501,
    "account_id": "NPVesdTGASO23_547878",
    "category_id": 12,
    "name": "Trade Box 10kg",
    "sku": "TB-10KG",
    "description": "10kg wholesale box",
    "base_unit": "box",
    "unit_price": "24.99",
    "vat_rate": "20.00",
    "vat_included": 0,
    "pack_unit": "case",
    "pack_quantity": "1.0000",
    "track_stock": 1,
    "stock_qty": "120.0000",
    "reorder_level": "25.0000",
    "preferred_supplier": "Acme Supply",
    "active": 1,
    "source": "api",
    "created_at": "2026-07-18T10:08:00Z",
    "updated_at": "2026-07-18T10:08:00Z"
  }
}
```

Product list response

```
GET /v2/nerotrade/products?q=box&limit=1
{
  "success": true,
  "data": [
    {
      "id": 501,
      "account_id": "NPVesdTGASO23_547878",
      "name": "Trade Box 10kg",
      "sku": "TB-10KG",
      "base_unit": "box",
      "unit_price": "24.99",
      "vat_rate": "20.00",
      "stock_qty": "120.0000",
      "active": 1
    }
  ],
  "meta": {
    "current_page": 1,
    "per_page": 1,
    "total": 1,
    "last_page": 1
  }
}
```

## NeroTrade Inventory

Read stock-tracked products and update stock quantity, reorder level, supplier and active status.

GET`/nerotrade/inventory`

Lists products where stock tracking is enabled.

GET`/nerotrade/inventory/{id}`

Retrieves one stock-tracked product.

PATCH`/nerotrade/inventory/{id}`

Updates stock quantity, reorder level, preferred supplier or active status.

Create inventory items by creating products with `track_stock` enabled. Stock fields can then be updated through the inventory endpoint.

### PATCH inventory request attributes

| Field | Type | Description |
| --- | --- | --- |
| track_stock | boolean | Enables stock tracking. |
| stock_qty | decimal | Current stock quantity. |
| reorder_level | decimal | Manual reorder threshold. |
| preferred_supplier | string | Supplier reference. |
| active | boolean | Whether the product is active. |

Update stock response

```
PATCH /v2/nerotrade/inventory/501
{
  "track_stock": true,
  "stock_qty": 85,
  "reorder_level": 20,
  "preferred_supplier": "Acme Supply"
}

HTTP/1.1 200 OK
{
  "success": true,
  "data": {
    "id": 501,
    "account_id": "NPVesdTGASO23_547878",
    "name": "Trade Box 10kg",
    "sku": "TB-10KG",
    "track_stock": 1,
    "stock_qty": "85.0000",
    "reorder_level": "20.0000",
    "preferred_supplier": "Acme Supply",
    "active": 1,
    "updated_at": "2026-07-18T11:20:00Z"
  }
}
```

Inventory create guard response

```
HTTP/1.1 405 Method Not Allowed
{
  "success": false,
  "error": {
    "code": "use_products",
    "message": "Create inventory items with POST /v2/nerotrade/products, then update stock with PATCH /v2/nerotrade/inventory/{id}."
  }
}
```

## NeroTrade Drivers & Deliveries

Manage driver records and delivery note workflows for trade orders. Delivery creation supports line items and can optionally create a linked invoice.

GET`/nerotrade/drivers`

Lists delivery drivers.

POST`/nerotrade/drivers`

Creates a driver record.

GET`/nerotrade/drivers/{id}`

Retrieves one driver record.

PATCH`/nerotrade/drivers/{id}`

Updates driver details or active status.

GET`/nerotrade/deliveries`

Lists deliveries and delivery notes.

POST`/nerotrade/deliveries`

Creates a delivery record.

GET`/nerotrade/deliveries/{id}`

Retrieves one delivery record with items.

PATCH`/nerotrade/deliveries/{id}`

Updates delivery status, driver, proof name, notes or line items.

### POST/PATCH driver and delivery request attributes

| Field | Type | Description |
| --- | --- | --- |
| customer_id | integer | Required linked trade account. |
| driver_id | integer | Assigned driver. |
| delivery_number | string | Optional delivery reference. Generated if omitted. |
| status | enum | scheduled, delivered, invoiced, failed or cancelled. |
| delivered_at | datetime | Delivery timestamp. |
| proof_name | string | Recipient name for proof of delivery. |
| create_invoice | boolean | Creates an invoice from the delivery items when true. |
| items | array | Line items with item_name, quantity, unit, unit_price, vat_rate and optional product_id. |

Create driver response

```
POST /v2/nerotrade/drivers
{
  "name": "Alex Driver",
  "phone": "+447700900111",
  "email": "alex.driver@example.com",
  "vehicle_registration": "AB12 CDE",
  "default_route": "North Manchester",
  "notes": "Morning deliveries",
  "active": true
}

HTTP/1.1 201 Created
{
  "success": true,
  "data": {
    "id": 9,
    "account_id": "NPVesdTGASO23_547878",
    "name": "Alex Driver",
    "phone": "+447700900111",
    "email": "alex.driver@example.com",
    "vehicle_registration": "AB12 CDE",
    "default_route": "North Manchester",
    "notes": "Morning deliveries",
    "active": 1,
    "created_at": "2026-07-18T10:12:00Z",
    "updated_at": "2026-07-18T10:12:00Z"
  }
}
```

Create delivery

```
POST /v2/nerotrade/deliveries
{
  "customer_id": 104,
  "driver_id": 9,
  "delivery_number": "ND-1001",
  "status": "scheduled",
  "delivered_at": "2026-07-20T10:00:00Z",
  "create_invoice": true,
  "notes": "Leave with goods-in team",
  "items": [
    {
      "product_id": 501,
      "item_name": "Trade Box 10kg",
      "quantity": 5,
      "unit": "box",
      "unit_price": 24.99,
      "vat_rate": 20
    }
  ]
}
```

Create delivery response

```
HTTP/1.1 201 Created
{
  "success": true,
  "data": {
    "id": 7001,
    "account_id": "NPVesdTGASO23_547878",
    "customer_id": 104,
    "driver_id": 9,
    "delivery_number": "ND-1001",
    "status": "invoiced",
    "delivered_at": "2026-07-20T10:00:00Z",
    "total_net": "124.95",
    "total_vat": "24.99",
    "total_gross": "149.94",
    "notes": "Leave with goods-in team",
    "items": [
      {
        "id": 1,
        "account_id": "NPVesdTGASO23_547878",
        "delivery_id": 7001,
        "product_id": 501,
        "item_name": "Trade Box 10kg",
        "quantity": "5.0000",
        "unit": "box",
        "unit_price": "24.99",
        "vat_rate": "20.00",
        "line_total": "149.94"
      }
    ],
    "invoice": {
      "id": 3301,
      "account_id": "NPVesdTGASO23_547878",
      "customer_id": 104,
      "source_delivery_id": 7001,
      "invoice_number": "NT-A1B2C3D4",
      "status": "sent",
      "total_gross": "149.94",
      "balance_due": "149.94"
    }
  }
}
```

Update delivery response

```
PATCH /v2/nerotrade/deliveries/7001
{
  "status": "delivered",
  "proof_name": "Alex Customer",
  "delivered_at": "2026-07-20T14:22:00Z"
}

HTTP/1.1 200 OK
{
  "success": true,
  "data": {
    "id": 7001,
    "account_id": "NPVesdTGASO23_547878",
    "status": "delivered",
    "proof_name": "Alex Customer",
    "delivered_at": "2026-07-20T14:22:00Z",
    "updated_at": "2026-07-20T14:22:05Z",
    "items": []
  }
}
```

## NeroTrade Invoices & Collections

Create trade invoices, record payment collections and keep outstanding trade account balances in sync.

GET`/nerotrade/invoices`

Lists trade invoices.

POST`/nerotrade/invoices`

Creates a trade invoice with line items.

GET`/nerotrade/invoices/{id}`

Retrieves one trade invoice with items and payments.

PATCH`/nerotrade/invoices/{id}`

Updates invoice status, dates, terms, collection method, notes or items.

GET`/nerotrade/collections`

Lists collection/payment records. Alias: `/nerotrade/payments`.

POST`/nerotrade/collections`

Creates a collection/payment record.

PATCH`/nerotrade/collections/{id}`

Updates collection amount, method, invoice link, paid date or reference.

### POST/PATCH invoice and collection request attributes

| Field | Type | Description |
| --- | --- | --- |
| customer_id | integer | Required linked trade account. |
| invoice_number | string | Optional merchant invoice reference. Generated if omitted. |
| status | enum | sent, part_paid, paid, overdue or collection_failed. |
| issue_date | date | Invoice issue date. |
| due_date | date | Invoice due date. Calculated from terms if omitted. |
| payment_terms | enum | cod, 7_days, 14_days, 30_days, weekly or monthly. |
| collection_method | string | Collection preference. |
| items | array | Line items with item_name, quantity, unit, unit_price, vat_rate and optional product_id. |
| amount | decimal | Required collection amount. |
| method | string | Required payment method for collections. |
| reference | string | External collection reference. |
| paid_at | datetime | Payment received timestamp. |

Create invoice

```
POST /v2/nerotrade/invoices
{
  "customer_id": 104,
  "invoice_number": "NT-INV-1001",
  "status": "sent",
  "issue_date": "2026-07-18",
  "due_date": "2026-06-17",
  "payment_terms": "30_days",
  "collection_method": "payment_link",
  "notes": "May wholesale order",
  "items": [
    {
      "product_id": 501,
      "item_name": "Trade Box 10kg",
      "quantity": 5,
      "unit": "box",
      "unit_price": 24.99,
      "vat_rate": 20
    }
  ]
}
```

Create invoice response

```
HTTP/1.1 201 Created
{
  "success": true,
  "data": {
    "id": 3301,
    "account_id": "NPVesdTGASO23_547878",
    "customer_id": 104,
    "invoice_number": "NT-INV-1001",
    "status": "sent",
    "issue_date": "2026-07-18",
    "due_date": "2026-06-17",
    "payment_terms": "30_days",
    "collection_method": "payment_link",
    "total_net": "124.95",
    "total_vat": "24.99",
    "total_gross": "149.94",
    "paid_amount": "0.00",
    "balance_due": "149.94",
    "items": [
      {
        "id": 1,
        "account_id": "NPVesdTGASO23_547878",
        "invoice_id": 3301,
        "product_id": 501,
        "item_name": "Trade Box 10kg",
        "quantity": "5.0000",
        "unit": "box",
        "unit_price": "24.99",
        "vat_rate": "20.00",
        "line_total": "149.94"
      }
    ],
    "payments": []
  }
}
```

Collection response

```
POST /v2/nerotrade/collections
{
  "customer_id": 104,
  "invoice_id": 3301,
  "amount": 149.94,
  "method": "bank_transfer",
  "reference": "BACS-1001",
  "paid_at": "2026-07-21T10:15:00Z",
  "notes": "Paid in full"
}

HTTP/1.1 201 Created
{
  "success": true,
  "data": {
    "id": 601,
    "account_id": "NPVesdTGASO23_547878",
    "customer_id": 104,
    "invoice_id": 3301,
    "amount": "149.94",
    "method": "bank_transfer",
    "reference": "BACS-1001",
    "paid_at": "2026-07-21T10:15:00Z",
    "notes": "Paid in full",
    "created_at": "2026-07-21T10:15:03Z"
  }
}
```

## NeroTrade Statements

Read statement-style account summaries, outstanding balances, invoices and payments for trade accounts.

GET`/nerotrade/statements`

Lists trade accounts in statement format. Use `outstanding_only=1` to return accounts with balance due.

GET`/nerotrade/statements/{customer_id}`

Retrieves one trade account statement with invoices and payments.

### Statement response attributes

| Query / Field | Type | Description |
| --- | --- | --- |
| outstanding_only | boolean | Only return trade accounts with outstanding balances. |
| limit | integer | Number of records to return. Maximum 100 for the list endpoint and 200 for invoice/payment lines. |
| customer | object | Trade account details. |
| summary | object | Current balance, overdue balance, invoice count and payment count. |
| invoices | array | Recent invoices for the trade account. |
| payments | array | Recent payment/collection records. |

Statements list response

```
GET /v2/nerotrade/statements?outstanding_only=1&limit=2
{
  "success": true,
  "data": [
    {
      "id": 104,
      "account_id": "NPVesdTGASO23_547878",
      "business_name": "Acme Builders Ltd",
      "current_balance": "149.94",
      "overdue_balance": "0.00",
      "payment_terms": "30_days",
      "collection_method": "payment_link"
    }
  ],
  "meta": {
    "current_page": 1,
    "per_page": 2,
    "total": 1,
    "last_page": 1
  }
}
```

Customer statement response

```
GET /v2/nerotrade/statements/104
{
  "success": true,
  "data": {
    "customer": {
      "id": 104,
      "account_id": "NPVesdTGASO23_547878",
      "business_name": "Acme Builders Ltd",
      "current_balance": "149.94",
      "overdue_balance": "0.00"
    },
    "summary": {
      "current_balance": 149.94,
      "overdue_balance": 0,
      "invoice_count": 1,
      "payment_count": 1
    },
    "invoices": [
      {
        "id": 3301,
        "account_id": "NPVesdTGASO23_547878",
        "invoice_number": "NT-INV-1001",
        "status": "sent",
        "total_gross": "149.94",
        "balance_due": "149.94",
        "items": [],
        "payments": []
      }
    ],
    "payments": [
      {
        "id": 601,
        "account_id": "NPVesdTGASO23_547878",
        "amount": "149.94",
        "method": "bank_transfer",
        "reference": "BACS-1001"
      }
    ]
  }
}
```

## NeroTrade Sales & Marketing

Manage campaign records linked to trade accounts for merchant-supplied sales and marketing workflows.

GET`/nerotrade/campaigns`

Lists sales and marketing campaign records.

POST`/nerotrade/campaigns`

Creates a campaign record.

GET`/nerotrade/campaigns/{id}`

Retrieves one campaign record.

PATCH`/nerotrade/campaigns/{id}`

Updates campaign details, schedule or status.

### POST/PATCH campaign request attributes

| Field | Type | Description |
| --- | --- | --- |
| name | string | Required campaign name. |
| campaign_type | string | Required campaign type, for example promotion or retention. |
| channel | enum | Required channel such as email, sms, phone, field_sales or manual. |
| target_segment | string | Target segment. Defaults to all_trade_accounts. |
| status | enum | draft, scheduled, active, completed or cancelled. |
| message | string | Merchant-supplied campaign message or note. |
| scheduled_at | datetime | Optional scheduled time. |

Campaign response

```
POST /v2/nerotrade/campaigns
{
  "name": "June trade offer",
  "campaign_type": "promotion",
  "channel": "email",
  "target_segment": "all_trade_accounts",
  "status": "draft",
  "message": "Manual campaign note",
  "scheduled_at": "2026-06-03T09:30:00Z"
}

HTTP/1.1 201 Created
{
  "success": true,
  "data": {
    "id": 41,
    "account_id": "NPVesdTGASO23_547878",
    "name": "June trade offer",
    "campaign_type": "promotion",
    "channel": "email",
    "target_segment": "all_trade_accounts",
    "status": "draft",
    "message": "Manual campaign note",
    "scheduled_at": "2026-06-03T09:30:00Z",
    "created_at": "2026-07-18T10:35:00Z",
    "updated_at": "2026-07-18T10:35:00Z"
  }
}
```

## NeroTrade Routes & Tracking

Create delivery route plans and update driver check-in records.

GET`/nerotrade/route-plans`

Lists planned routes. Alias: `/nerotrade/routes`.

POST`/nerotrade/route-plans`

Creates a route plan.

GET`/nerotrade/route-plans/{id}`

Retrieves one route plan.

PATCH`/nerotrade/route-plans/{id}`

Updates route name, date, status, driver, vehicle or notes.

GET`/nerotrade/driver-checkins`

Lists driver tracking/check-in records.

POST`/nerotrade/driver-checkins`

Creates a driver check-in record.

PATCH`/nerotrade/driver-checkins/{id}`

Updates a driver check-in record.

### POST/PATCH route request attributes

| Field | Type | Description |
| --- | --- | --- |
| route_name | string | Required route name. |
| driver_id | integer | Assigned driver. |
| route_date | date | Route date. Defaults to today when omitted. |
| status | enum | planned, in_progress, completed or cancelled. |
| vehicle_registration | string | Vehicle registration. |
| start_area | string | Route start area. |
| end_area | string | Route end area. |
| estimated_stops | integer | Estimated stop count. |
| estimated_value | decimal | Estimated route value. |
| checkin_type | enum | Required check-in type such as start_route, delivery_stop, issue or end_route. |
| latitude / longitude | decimal | Optional GPS coordinates. |

Route plan response

```
POST /v2/nerotrade/route-plans
{
  "driver_id": 9,
  "route_name": "North route",
  "route_date": "2026-07-20",
  "status": "planned",
  "vehicle_registration": "AB12 CDE",
  "start_area": "Manchester",
  "end_area": "Bolton",
  "estimated_stops": 8,
  "estimated_value": 1240.50,
  "notes": "Priority trade accounts first"
}

HTTP/1.1 201 Created
{
  "success": true,
  "data": {
    "id": 66,
    "account_id": "NPVesdTGASO23_547878",
    "driver_id": 9,
    "route_code": "NR-A1B2C3D4",
    "route_name": "North route",
    "route_date": "2026-07-20",
    "status": "planned",
    "vehicle_registration": "AB12 CDE",
    "start_area": "Manchester",
    "end_area": "Bolton",
    "estimated_stops": 8,
    "estimated_value": "1240.50",
    "created_at": "2026-07-18T10:40:00Z"
  }
}
```

Driver check-in response

```
POST /v2/nerotrade/driver-checkins
{
  "route_plan_id": 66,
  "driver_id": 9,
  "customer_id": 104,
  "checkin_type": "delivery_stop",
  "checked_in_at": "2026-07-20T11:05:00Z",
  "latitude": 53.4808,
  "longitude": -2.2426,
  "address": "10 Trade Road, Manchester",
  "notes": "Arrived at customer site"
}

HTTP/1.1 201 Created
{
  "success": true,
  "data": {
    "id": 901,
    "account_id": "NPVesdTGASO23_547878",
    "route_plan_id": 66,
    "driver_id": 9,
    "customer_id": 104,
    "checkin_type": "delivery_stop",
    "checked_in_at": "2026-07-20T11:05:00Z",
    "latitude": "53.480800",
    "longitude": "-2.242600",
    "address": "10 Trade Road, Manchester",
    "notes": "Arrived at customer site",
    "created_at": "2026-07-20T11:05:01Z"
  }
}
```

## Customers

Create and manage NeroPOS/NeroPay customer records.

GET`/customers`

Lists customers.

POST`/customers`

Creates a customer.

GET`/customers/{id}`

Retrieves one customer.

PATCH`/customers/{id}`

Updates a customer.

### POST/PATCH customer request attributes

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| name | string | Required | Customer name. |
| email | string | Optional | Email. |
| phone | string | Optional | Phone. |
| postcode | string | Optional | Postcode. |
| address1/address2 | string | Optional | Address lines. |
| city | string | Optional | City. |
| instructions | text | Optional | Delivery or customer notes. |

Create customer

```
POST /v2/customers
{
  "name": "Jane Customer",
  "email": "jane@example.com",
  "phone": "01234567890",
  "postcode": "SW1A 1AA",
  "address1": "1 Demo Street",
  "city": "London"
}
```

Customer response

```
{
  "success": true,
  "data": {
    "id": 1001,
    "name": "Jane Customer",
    "email": "jane@example.com",
    "phone": "01234567890"
  }
}
```

## Categories & Products

Keep product catalogue data in sync for NeroPOS and NeroWeb menus.

GET`/categories`

Lists categories.

POST`/categories`

Creates category.

PATCH`/categories/{id}`

Updates category.

DELETE`/categories/{id}`

Deletes category where enabled.

GET`/products`

Lists products.

POST`/products`

Creates product.

PATCH`/products/{id}`

Updates product.

DELETE`/products/{id}`

Deletes product where enabled.

### POST/PATCH category and product request attributes

| Product field | Type | Required | Description |
| --- | --- | --- | --- |
| category_id | integer | Optional | Category ID. |
| name | string | Required | Product name. |
| sku | string | Optional | SKU. |
| price | decimal | Required | Product price. |
| vat_rate | decimal | Optional | VAT rate. |
| image_url | string | Optional | Image URL. |
| stock_qty | integer | Optional | Stock quantity. |
| description | text | Optional | Product description. |

Create category

```
POST /v2/categories
{
  "name": "Drinks",
  "sort_order": 1,
  "is_active": 1
}
```

Create product

```
POST /v2/products
{
  "category_id": 10,
  "name": "Latte",
  "sku": "LATTE-REG",
  "price": 3.50,
  "vat_rate": 20,
  "is_active": 1,
  "description": "Regular latte"
}
```

## Product Options & Rules

Manage product-specific options and global option groups for POS and online ordering.

GET`/product-option-groups`

Lists product option groups.

POST`/product-option-groups`

Creates product option group.

GET`/product-options`

Lists product options.

POST`/product-options`

Creates product option.

GET`/global-option-groups`

Lists global option groups.

POST`/global-option-groups`

Creates global option group.

GET`/global-option-items`

Lists global option items.

POST`/global-option-items`

Creates global option item.

GET`/global-option-assignments`

Lists assignments.

POST`/global-option-assignments`

Creates assignment.

GET`/product-option-rules`

Alias for assignments/rules.

### POST/PATCH option request attributes

| Field | Type | Description |
| --- | --- | --- |
| name | string | Option or group name. |
| required | boolean | Require selection. |
| min_select / max_select | integer | Selection limits. |
| price_delta | decimal | Price change for selected item. |
| rules_json | object | Rule configuration. |
| scope / scope_id | enum/integer | Where a global option applies. |

Create option group

```
POST /v2/product-option-groups
{
  "product_id": 42,
  "name": "Milk choice",
  "required": true,
  "min_select": 1,
  "max_select": 1,
  "sort_order": 1
}
```

Create option item

```
POST /v2/product-options
{
  "product_id": 42,
  "group_id": 9,
  "name": "Oat milk",
  "price_delta": 0.40,
  "sort_order": 1
}
```

## Tables & Areas

Manage table maps, areas and table status for dine-in flows.

GET`/table-areas`

Lists table areas.

POST`/table-areas`

Creates table area.

GET`/tables`

Lists tables.

POST`/tables`

Creates table.

PATCH`/tables/{id}`

Updates table.

DELETE`/tables/{id}`

Deletes table where enabled.

### POST/PATCH table and area request attributes

| Field | Type | Description |
| --- | --- | --- |
| area_id | integer | Area ID. |
| name / label | string | Display name. |
| seats | integer | Seat count. |
| status | enum | available, occupied, reserved etc. |
| x,y,w,h,rotation | decimal/integer | Table-map coordinates. |

Create table area

```
POST /v2/table-areas
{
  "name": "Main Floor",
  "label": "Main Floor",
  "sort_order": 1,
  "is_active": 1
}
```

Create table

```
POST /v2/tables
{
  "area_id": 1,
  "name": "T1",
  "label": "Table 1",
  "seats": 4,
  "status": "available",
  "x": 20,
  "y": 40,
  "w": 120,
  "h": 80
}
```

## Cashiers, Registers & Staff

Manage POS cashiers, registers, POS staff and team members.

GET`/cashiers`

Lists cashiers.

POST`/cashiers`

Creates cashier.

GET`/registers`

Lists registers.

POST`/registers`

Creates register.

GET`/pos-staffs`

Lists POS staff.

POST`/pos-staffs`

Creates POS staff.

GET`/team-members`

Lists merchant team members.

POST`/team-members`

Creates team member.

### POST/PATCH staff request attributes

| Field | Type | Description |
| --- | --- | --- |
| name | string | Staff/cashier name. |
| email / username | string | Login identifier. |
| password / new_password | string | Accepted on create/update but never returned. |
| role | enum | Role/register type. |
| permissions | object/array | Permission payload. |
| location_ids / till_ids | array | Allowed locations or tills. |

Create cashier

```
POST /v2/cashiers
{
  "name": "Front Till",
  "email": "till@example.com",
  "phone": "01234567890",
  "role": "cashier",
  "is_active": 1,
  "permissions": ["orders:create", "refunds:view"],
  "location_ids": [1]
}
```

Create team member

```
POST /v2/team-members
{
  "name": "Manager User",
  "email": "manager@example.com",
  "password": "temporary-secret",
  "role": "manager",
  "status": "active",
  "permissions": ["neroconnect:view", "orders:view"]
}
```

## Invoices

Create and manage merchant invoices and invoice line items.

GET`/invoices`

Lists invoices.

POST`/invoices`

Creates invoice.

GET`/invoices/{id}`

Retrieves invoice.

PATCH`/invoices/{id}`

Updates invoice.

GET`/invoice-items`

Lists invoice items.

POST`/invoice-items`

Creates invoice item.

### POST/PATCH invoice request attributes

| Field | Type | Description |
| --- | --- | --- |
| invoice_number | string | Invoice number. |
| invoice_to | string | Recipient name. |
| invoice_email | string | Recipient email. |
| currency_id | integer | Currency ID. |
| sub_total / total_amount | decimal | Amounts. |
| items_json | object/array | Line items payload. |
| due_date | date | Due date. |
| status | enum | draft, sent, paid etc. |

Create invoice

```
POST /v2/invoices
{
  "invoice_number": "INV-1001",
  "invoice_to": "Jane Customer",
  "invoice_email": "jane@example.com",
  "currency_id": 1,
  "sub_total": 100.00,
  "total_vat": 20.00,
  "total_amount": 120.00,
  "due_date": "2026-07-31",
  "status": "sent",
  "items_json": [
    { "item_name": "Consulting", "qty": 1, "amount": 100.00 }
  ]
}
```

## NeroPOS Orders

NeroPOS order APIs are separate from NeroWeb orders and generic store orders.

GET`/neropos-orders`

Lists NeroPOS orders.

POST`/neropos-orders`

Creates NeroPOS order.

GET`/neropos-orders/{id}`

Retrieves order.

PATCH`/neropos-orders/{id}`

Updates order.

GET`/neropos-order-items`

Lists order items.

POST`/neropos-order-items`

Creates order item.

GET`/orders`

Generic orders resource.

GET`/store-orders`

Read-only NeroPay Store orders.

### POST/PATCH order request attributes

| Field | Type | Description |
| --- | --- | --- |
| order_number / order_ref | string | Order reference. |
| status | enum | Order status. |
| fulfilment_type | enum | dine_in, takeaway, delivery etc. |
| payment_method / payment_status | enum | Payment fields. |
| customer_* | string | Customer details. |
| subtotal, total, tip_fee | decimal | Money fields. |
| location_id / table_id / staff_id | integer | Operational links. |
| meta_json | object | Extra order metadata. |

Create NeroPOS order

```
POST /v2/neropos-orders
{
  "order_ref": "POS-1001",
  "status": "open",
  "fulfilment_type": "dine_in",
  "payment_method": "card",
  "payment_status": "pending",
  "customer_name": "Jane Customer",
  "subtotal": 10.00,
  "tax_fee": 2.00,
  "total": 12.00,
  "location_id": 1,
  "table_id": 3
}
```

Create order item

```
POST /v2/neropos-order-items
{
  "order_id": 1001,
  "product_id": 42,
  "name": "Latte",
  "qty": 2,
  "unit_price": 3.50,
  "total_price": 7.00,
  "options_json": [{"name":"Oat milk","price_delta":0.40}]
}
```

## NeroPOS Payment Settings

Manage POS payment, tax, service fee, cashiers, tables and receipt defaults.

GET`/payment-settings`

Lists POS payment settings.

POST`/payment-settings`

Creates settings where no row exists.

GET`/payment-settings/{id}`

Retrieves settings.

PATCH`/payment-settings/{id}`

Updates settings.

### POST/PATCH payment settings request attributes

| Field | Type | Description |
| --- | --- | --- |
| default_tax_fee | decimal | Default tax fee. |
| default_service_fee | decimal | Default service fee. |
| default_tip_fee | decimal | Default tip fee. |
| pos_mode / default_channel | enum | POS operating mode. |
| enable_tables / enable_cashiers | boolean | Feature toggles. |
| receipt_footer / receipt_logo_url | text/string | Receipt branding. Use `receipt_logo_url` for the logo image URL. |
| min_card_charge | decimal | Minimum card charge. |

Update payment settings

```
PATCH /v2/payment-settings/1
{
  "default_tax_fee": 20,
  "default_service_fee": 0,
  "default_tip_fee": 0,
  "enable_tables": true,
  "enable_cashiers": true,
  "receipt_width": "80mm",
  "receipt_logo_url": "https://eu.neropay.app/assets/images/logoIcon/logo.png",
  "min_card_charge": 1.00
}
```

## NeroWeb Settings

Configure online ordering storefront settings separately from POS resources.

GET`/neroweb-settings`

Lists NeroWeb settings.

POST`/neroweb-settings`

Creates settings.

GET`/neroweb-settings/{id}`

Retrieves settings.

PATCH`/neroweb-settings/{id}`

Updates settings.

### POST/PATCH NeroWeb settings request attributes

| Field | Type | Description |
| --- | --- | --- |
| store_name | string | Online store name. |
| slug | string | Store slug. |
| is_active | boolean | Enable store. |
| disable_delivery / disable_collect | boolean | Fulfilment toggles. |
| enable_card / enable_cash | boolean | Payment toggles. |
| working_hours_json | object | Working hours. |
| delivery_bands_json | object | Delivery pricing/areas. |
| theme_json | object | Brand/theme settings, including `logo_url`. |

Update NeroWeb settings

```
PATCH /v2/neroweb-settings/1
{
  "store_name": "Demo Store",
  "slug": "demo-store",
  "is_active": true,
  "disable_delivery": false,
  "disable_collect": false,
  "enable_card": true,
  "working_hours_json": {
    "monday": [{"from":"09:00","to":"17:00"}]
  },
  "theme_json": {
    "logo_url": "https://eu.neropay.app/assets/images/logoIcon/logo.png"
  }
}
```

## NeroWeb Pages

Manage public pages used by the NeroWeb storefront builder.

GET`/neroweb-pages`

Lists pages.

POST`/neroweb-pages`

Creates page.

GET`/neroweb-pages/{id}`

Retrieves page.

PATCH`/neroweb-pages/{id}`

Updates page.

GET`/nerowebpages`

Legacy alias for pages.

### POST/PATCH NeroWeb page request attributes

| Field | Type | Description |
| --- | --- | --- |
| title | string | Page title. |
| slug | string | URL slug. |
| page_type | enum | home, content, legal etc. |
| is_home / is_published / show_in_nav | boolean | Page flags. |
| nav_label / nav_order | string/integer | Navigation settings. |
| html_content / content / css_content | text | Page content. |
| builder_json | object | Builder state. |
| meta_title / meta_description | string/text | SEO metadata. |

Create NeroWeb page

```
POST /v2/neroweb-pages
{
  "title": "About us",
  "slug": "about",
  "page_type": "content",
  "is_published": true,
  "show_in_nav": true,
  "nav_label": "About",
  "html_content": "<h1>About us</h1>",
  "meta_title": "About Demo Store"
}
```

## NeroWeb Orders

NeroWeb orders are separate from NeroPOS orders and store orders.

GET`/neroweb-orders`

Lists NeroWeb orders.

POST`/neroweb-orders`

Creates NeroWeb order.

GET`/neroweb-orders/{id}`

Retrieves order.

PATCH`/neroweb-orders/{id}`

Updates order.

GET`/neroweb-order-items`

Lists order items.

POST`/neroweb-order-items`

Creates order item.

### POST/PATCH NeroWeb order request attributes

| Field | Type | Description |
| --- | --- | --- |
| order_ref | string | Order reference. |
| status | enum | Order status. |
| fulfilment_type | enum | delivery, collect etc. |
| payment_method / payment_status | enum | Payment fields. |
| subtotal_pence / total_pence | integer/decimal | Money fields. |
| customer_name / email / phone | string | Customer fields. |
| meta_json | object | Extra metadata. |

Create NeroWeb order

```
POST /v2/neroweb-orders
{
  "order_ref": "WEB-1001",
  "status": "pending",
  "fulfilment_type": "delivery",
  "payment_method": "card",
  "payment_status": "pending",
  "customer_name": "Jane Customer",
  "customer_email": "jane@example.com",
  "subtotal_pence": 1000,
  "delivery_fee_pence": 250,
  "total_pence": 1250
}
```

## Booking Settings

Booking settings are separate from booking transactions and should appear under the Booking menu.

GET`/booking-settings`

Lists booking settings.

POST`/booking-settings`

Creates booking settings.

GET`/booking-settings/{id}`

Retrieves booking settings.

PATCH`/booking-settings/{id}`

Updates booking settings.

### POST/PATCH booking settings request attributes

| Field | Type | Description |
| --- | --- | --- |
| booking_company_name | string | Booking brand/company name. |
| booking_type | enum | Booking mode/type. |
| weekly_hours | object | Weekly opening hours. |
| auto_approve_booking | boolean | Auto approve bookings. |
| slot_duration | integer | Minutes per slot. |
| max_bookings_per_slot / max_guests | integer | Capacity. |
| services | object/array | Bookable services. |
| holiday_days | array | Closed dates. |
| staff_enabled / staff | boolean/object | Staff booking assignment. |

Update booking settings

```
PATCH /v2/booking-settings/1
{
  "booking_company_name": "Demo Booking",
  "booking_type": "appointment",
  "slot_duration": 30,
  "max_bookings_per_slot": 1,
  "max_guests": 4,
  "auto_approve_booking": true,
  "weekly_hours": {
    "monday": [{"from":"09:00","to":"17:00"}]
  },
  "services": [
    {"name":"Consultation","duration":30,"price":25.00}
  ]
}
```

## Bookings

Create and manage booking transactions.

GET`/bookings`

Lists bookings.

POST`/bookings`

Creates booking.

GET`/bookings/{id}`

Retrieves booking.

PATCH`/bookings/{id}`

Updates booking.

### POST/PATCH booking request attributes

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| booking_date | date | Required | Booking date. |
| booking_time | time | Required | Booking time. |
| guest_count | integer | Optional | Guests. |
| service | string | Optional | Service name. |
| amount | decimal | Optional | Booking amount. |
| staff_id / staff_name | integer/string | Optional | Staff assignment. |
| location_id | integer | Optional | Location. |
| customer_name / phone / email | string | Required | Customer details. |
| customer_note | text | Optional | Customer note. |
| status | enum | Optional | pending, confirmed, cancelled etc. |

Create booking

```
POST /v2/bookings
{
  "booking_date": "2026-07-10",
  "booking_time": "14:00",
  "guest_count": 2,
  "service": "Consultation",
  "amount": 25.00,
  "customer_name": "Jane Customer",
  "customer_phone": "+447700900000",
  "customer_email": "jane@example.com",
  "status": "pending"
}
```

## KYC / Identity API

Identity API is not only for NeroConnect. Direct merchants can also use it to create verification links and read verification sessions.

POST`/identity/verification-links`

Creates a document verification link.

GET`/identity/verification-sessions`

Lists verification sessions.

GET`/identity/verification-sessions/{id}`

Retrieves one verification session.

### POST identity verification-link request attributes

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| type | enum | Optional | document or supported verification type. |
| return_url | string | Optional | Where the user should return after verification. |
| first_name / last_name | string | Optional | Prefill name if known. |
| country | enum | Optional | ISO country code. |
| metadata | object | Optional | Developer metadata. |

NeroPay should not expose the underlying verification provider in your customer-facing copy. Use the returned `short_url` or `verification_url` in your own dashboard.

Create verification link

```
POST /v2/identity/verification-links
{
  "type": "document",
  "return_url": "https://platform.example.com/kyc/return",
  "first_name": "Jane",
  "last_name": "Merchant",
  "country": "GB",
  "metadata": {
    "user_id": "user_1001"
  }
}
```

List verification sessions response

```
{
  "success": true,
  "data": {
    "account_id": "NPVesdTGASO23_547878",
    "sessions": [
      {
        "id": 54484,
        "verification_id": "vs_1TJS0MLWX9o5cjig2zgdWrN7",
        "status": "requires_input",
        "type": "document",
        "verification_url": "https://verify.example/start/...",
        "short_url": "https://verify.neropay.app/7d06xoyeop3nv",
        "first_name": null,
        "last_name": null,
        "country": null,
        "document_type": null,
        "created_at": "2026-04-07T05:16:50.000000Z",
        "updated_at": "2026-04-07T05:16:50.000000Z"
      },
      {
        "id": 49138,
        "verification_id": "vs_1SSkwHLWX9o5cjig03xCd6c9",
        "status": "verified",
        "type": "document",
        "verification_url": null,
        "short_url": "https://verify.neropay.app/sarbz424yicb3",
        "first_name": "HASAN",
        "last_name": "BOZKUS",
        "country": "GB",
        "document_type": "driving_license",
        "created_at": "2025-11-12T20:46:49.000000Z",
        "updated_at": "2026-03-30T21:04:11.000000Z"
      }
    ]
  }
}
```

## Compliance API

Compliance APIs let merchants or platforms read file and information requests, then submit requested document evidence back to NeroPay.

GET`/compliance/requests`

Lists compliance requests, including each request's `response_type`.

GET`/compliance/requests/{id}`

Retrieves one compliance request.

POST`/compliance/requests/{id}/documents`

Uploads evidence for a request whose `response_type` is `file`.

### Compliance request response attributes

| Field | Type | Description |
| --- | --- | --- |
| response_type | enum | `file` or `text`. Text requests appear as **Information needed** in the merchant dashboard. |
| docs_number | integer | Maximum required file slots for file requests. |
| has_text_response | boolean | Whether the merchant has submitted the requested information. |
| text_submitted_at | datetime\|null | ISO 8601 submission time for a text response, when present. |
| has_file_1 / has_file_2 / has_file_3 | boolean | Whether each available upload slot contains a file. |

### POST compliance document attributes

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| file_1 | file | Required* | First requested file. At least one of `file_1`, `file_2` or `file_3` is required. |
| file_2 / file_3 | file | Optional | Additional files when the request allows multiple upload slots. |
| note | text | Optional | Submission note, maximum 2,000 characters. |

Text requests are completed through the secure NeroPay merchant Compliance panel and the document upload endpoint rejects them with `text_response_required`. Live account-requirement updates create a NeroForce review task automatically; document requirements also create a file request, while value requirements appear as **Information needed**. Locally disabled merchant accounts are logged and skipped. Webhooks notify document requested/submitted/approved/rejected.

List compliance requests response

```
{
  "success": true,
  "data": {
    "account_id": "NPVesdTGASO23_547878",
    "requests": [
      {
        "id": 123,
        "type": "Account information",
        "title": "Confirm legal owner name",
        "status": "Pending",
        "response_type": "text",
        "docs_number": 1,
        "has_text_response": false,
        "text_submitted_at": null,
        "has_file_1": false,
        "has_file_2": false,
        "has_file_3": false
      }
    ]
  },
  "meta": {
    "pagination": {
      "current_page": 1,
      "per_page": 25,
      "total": 0,
      "last_page": 1
    }
  }
}
```

Submit compliance document

```
POST /v2/compliance/requests/123/documents
Content-Type: multipart/form-data

file_1=@proof-of-address.pdf
note=Latest proof of address attached.
```

## Support Tickets

White-label support APIs allow a platform to create tickets for NeroPay staff while keeping the platform as the customer-facing brand.

GET`/support/tickets`

Lists tickets.

POST`/support/tickets`

Creates ticket.

GET`/support/tickets/{id}`

Retrieves ticket.

### POST support ticket request attributes

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| subject | string | Required | Ticket subject. |
| message | text | Required | Initial message. |
| priority | enum | Optional | low, normal, high, urgent. |
| category | string | Optional | Support category. |
| attachments[] | file | Optional | Optional files where supported. Max 2mb. PNG, JPG, WEBP or PDF |

When NeroPay answers a ticket, your platform can receive `support.ticket_updated` and `support.message_created` webhooks and display the answer in your own UI.

Create support ticket

```
POST /v2/support/tickets
{
  "subject": "Customer payout question",
  "message": "The connected merchant needs help with a payout.",
  "priority": "normal",
  "category": "payouts"
}
```

Ticket response

```
{
  "success": true,
  "data": {
    "ticket": {
      "id": 501,
      "subject": "Customer payout question",
      "status": "open",
      "priority": "normal",
      "created_at": "2026-04-28T18:00:00.000000Z"
    }
  }
}
```

## Webhooks

Use webhooks for payment outcomes, account updates, KYC, compliance, proof, reserve release and support events.

GET`/webhooks`

Lists configured endpoints.

GET`/webhook-deliveries`

Lists delivery attempts.

`transaction.succeeded``transaction.failed``transaction.proof_requested``transaction.proof_submitted``transaction.proof_approved``transaction.proof_rejected``payout.proof_requested``transfer.proof_requested``balance.reserve_released``compliance.document_requested``compliance.document_submitted``compliance.document_approved``compliance.document_rejected``kyc.pending``kyc.verified``kyc.rejected``account.update``account.updated``connected_account.created``connected_account.updated``support.ticket_created``support.ticket_updated``support.message_created`

### Webhook response attributes

| Field | Type | Description |
| --- | --- | --- |
| id | string | Webhook event ID. |
| type | enum | Event type. |
| created_at | datetime | Event creation time. |
| account | object | Merchant or connected account payload. |
| data | object | Event-specific payload. Failed transaction events include `data.error.code`, `data.error.message` and optional `data.error.decline_code`. |
| signature | string | Use your endpoint signing secret where configured. |

**Payment failures:** subscribe to `transaction.failed` to receive asynchronous terminal or hosted-payment failures. Treat delivery as an event notification: acknowledge the webhook with HTTP 2xx, then use `data.status` and `data.error` for the payment result.

If the platform handles email/SMS, NeroPay skips delegated merchant-facing messages and publishes webhooks so the platform can send branded communication.

Webhook payload

```
{
  "id": "evt_np_01HX...",
  "type": "transaction.proof_requested",
  "created_at": "2026-04-28T18:00:00.000000Z",
  "account": {
    "id": "NPVesdTGASO23_547878",
    "business_company_name": "Demo Connected Merchant Ltd"
  },
  "data": {
    "transaction": {
      "id": 5663230,
      "amount": 5000.00,
      "currency": "GBP",
      "is_reserve": true,
      "reserve_amount": 5000.00
    },
    "proof_request": {
      "id": 925,
      "status": "requested"
    }
  }
}
```

Failed transaction webhook

```
{
  "id": "evt_78d..._12",
  "type": "transaction.failed",
  "created_at": "2026-09-04T21:30:00.000000Z",
  "merchant_id": 34321,
  "account_id": "NPVesdTGASO23_547878",
  "data": {
    "status": "failed",
    "payment_reference": "NP-EHPS4218-ba542",
    "identifier": "ORDER-1001",
    "error": {
      "code": "card_declined",
      "message": "Your card was declined.",
      "decline_code": "insufficient_funds"
    }
  },
  "transaction": {
    "reference": "NP-EHPS4218-ba542",
    "status": "failed",
    "amount": 15.99
  }
}
```

List deliveries

```
GET /v2/webhook-deliveries?status=failed&per_page=25
```

## Sandbox environment

The Developer sandbox uses separate test keys and isolated simulated payments. It is not a full live-API replica.

Use the 10-2026 selector above to open the dedicated Sandbox reference, including supported endpoints, request fields, checkout outcomes, callbacks and limits. Balances, payouts, transfers, refunds, terminals and generic resource writes are unavailable in sandbox.

## Resource Reference

API v2 exposes explicit controller endpoints plus direct resource aliases. Resources are grouped by product area in this documentation; they are not all hidden under NeroPOS.

### Core

accountconnected-accountsbalancesbalanceapi-logs

### Payments

hosted-paymentspayment-linkscontractsinvoicessubscriptionstransactionstransaction proof

### Money Movement

wallet-transactionspayoutspayout-scheduletransfer-scheduledisputesloans

### NeroDisburse

bank-accountsnerodisburse/bank-accountsnerodisburse/payoutsnerodisburse/paymentsnerodisburse-bank-accountsnerodisburse-payoutsnerodisburse-payments

### Terminal

terminal/paymentslocationsterminal/locationsreadersterminal/readersdevices

### NeroCard

nerocardsnerocardholdersnerocard-transactions

### NeroTrade

nerotrade/trade-accountsnerotrade/product-categoriesnerotrade/productsnerotrade/inventorynerotrade/driversnerotrade/deliveriesnerotrade/invoicesnerotrade/collectionsnerotrade/statementsnerotrade/campaignsnerotrade/route-plansnerotrade/driver-checkins

### NeroPOS

customerscategoriesproductsproduct-option-groupsproduct-optionsglobal-option-groupsglobal-option-itemsglobal-option-assignmentsproduct-option-rulescashiersregisterspos-staffsteam-memberstablestable-areasinvoicesinvoice-itemsordersneropos-ordersneropos-order-itemspayment-settings

### NeroWeb

neroweb-settingsneroweb-pagesnerowebpagesneroweb-ordersneroweb-order-itemsstore-orders

### Bookings

booking-settingsbookings

### Risk & Ops

identity/verification-linksidentity/verification-sessionscompliance/requestssupport/ticketswebhookswebhook-deliveries

Direct aliases such as `/v2/disputes`, `/v2/bank-accounts`, `/v2/neroweb-orders`, `/v2/nerocard-transactions`, `/v2/nerotrade/trade-accounts` and `/v2/bookings` should not return route-not-found when the v2 route patch is deployed and route cache is cleared.

Generic resource pattern

```
GET    /v2/{resource}
POST   /v2/{resource}
GET    /v2/{resource}/{id}
PATCH  /v2/{resource}/{id}
DELETE /v2/{resource}/{id}

# Explicit namespace also works:
GET    /v2/resources/{resource}
POST   /v2/resources/{resource}
GET    /v2/resources/{resource}/{id}
PATCH  /v2/resources/{resource}/{id}
DELETE /v2/resources/{resource}/{id}
```

10-2026 · Sandbox API reference

# Test the payment journey.
No real money moves.

[View Markdown ↗](https://docs.neropay.app/api/sandbox.md)

Use isolated test credentials to create a simulated checkout, choose its outcome and inspect test webhooks and API logs. This is a payment-flow simulator, not a full replica of the live API.

**/v2/sandbox**Dedicated test base path

**Separate keys**Live keys are not accepted

**3 outcomes**Success, failure, cancellation

**No real funds**No payouts or balance changes

## Quick start

From a test key to a verified result in four steps.

- Open [Developer → Sandbox mode → API](https://eu.neropay.app/merchant/developer?mode=test&tab=api) and generate your sandbox keys.
- Create a payment from your server using the sandbox secret and a unique `Idempotency-Key`.
- Open the returned `checkout_url` and select a simulated result. The checkout does not collect card details.
- Read the payment back and check Developer → Webhooks / API Logs with Sandbox mode on.

Configure a separate sandbox callback endpoint. A test success must never fulfil a live order. Check `livemode: false` and `environment: sandbox`.

Create a sandbox payment

```
POST /v2/sandbox/hosted-payments
Authorization: Bearer YOUR_TEST_SECRET_KEY
Content-Type: application/json
Accept: application/json
Idempotency-Key: sandbox-order-0001

{
  "identifier": "sandbox-order-0001",
  "amount": 10.00,
  "currency": "GBP",
  "details": "Sandbox order"
}
```

## Authentication & headers

Base URL: `https://eu.neropay.app/v2/sandbox`

Send `Authorization: Bearer YOUR_TEST_SECRET_KEY`. Sandbox secrets start with `np_test_sk_` followed by 64 hexadecimal characters. The merchant is resolved from this key; do not send an account ID.

| Header | Sandbox requirement |
| --- | --- |
| Authorization | Bearer sandbox secret. Alternatively, use `X-NeroPay-Secret-Key`. |
| Accept | `application/json` |
| Content-Type | `application/json` for POST requests. |
| Idempotency-Key | Required for create: 8–100 letters, digits or `. _ : -`. |
| X-NeroPay-Public-Key | Optional. If supplied, it must match the sandbox secret. |
| X-NeroPay-Account | Not supported for sandbox creation. No connected-account routing. |

Sandbox API requests do not require the live write-signature headers. Callback signatures are separate and are described below.

Keep secrets server-side, including when using JavaScript examples. Live keys cannot authenticate in sandbox; test keys cannot authenticate against the live API. Rotating sandbox keys does not rotate live keys.

List sandbox payments

```
GET /v2/sandbox/hosted-payments?page=1
Authorization: Bearer YOUR_TEST_SECRET_KEY
Accept: application/json
```

## Simulated payments & payment links

Three aliases share the same isolated payment records.

POST`/v2/sandbox/hosted-payments`

Create a simulated hosted payment.

POST`/v2/sandbox/payment-links`

Create the same simulated checkout through the payment-link alias.

POST`/v2/sandbox/transactions`

Create the same simulated checkout through the transactions alias.

### Request fields

| Field | Validation |
| --- | --- |
| identifier | Required string, maximum 100 characters. |
| amount | Required number, 0.01–10,000; rounded to two decimal places. |
| currency | GBP, EUR or USD (lowercase is also accepted). |
| details | Optional string, maximum 500 characters. |
| ipn_url | Optional public HTTPS callback URL, maximum 1,000 characters. |
| success_url / cancel_url | Optional public HTTPS return URLs, maximum 1,000 characters each. |

Do not send `account_id`, `merchant_id`, connected-account routing fields, `on_behalf_of`, `card_number`, `cvc`, `cvv` or `payment_method`. They are rejected; no real payment instrument is used.

### Idempotency

The same key and same validated parameters return the existing test payment. Reusing a key with different parameters returns HTTP 409. Use a new key for a new simulated order. The aliases share this key space; repeated requests do not create separate copies.

Example create response · HTTP 201

```
{
  "success": true,
  "data": {
    "id": "test_pay_<64-hex-id>",
    "account_id": "test_account_123",
    "identifier": "sandbox-order-0001",
    "reference": "NP-TEST-0123456789ABCDEF",
    "payment_reference": "NP-TEST-0123456789ABCDEF",
    "amount": "10.00",
    "currency": "GBP",
    "details": "Sandbox order",
    "status": "open",
    "payment_status": null,
    "payment_link": "https://eu.neropay.app/developer-sandbox/checkout/test_pay_<64-hex-id>/<token>",
    "checkout_url": "https://eu.neropay.app/developer-sandbox/checkout/test_pay_<64-hex-id>/<token>",
    "sandbox": true,
    "livemode": false,
    "environment": "sandbox",
    "expires_at": "2026-10-02 12:00:00"
  }
}
```

IDs, timestamps and tokens shown here are illustrative. Use the exact URL and ID returned by your request. Checkout URLs contain a capability token; keep them private.

## Lookups & pagination

GET`/v2/sandbox/hosted-payments`

List your test records.

GET`/v2/sandbox/hosted-payments/{id}`

Retrieve one test payment.

The same GET collection and GET-by-ID paths are supported under `/payment-links` and `/transactions`. The ID is `test_pay_` followed by 64 hexadecimal characters. Only records belonging to the authenticated merchant are returned.

Lists are newest first, 20 records per page. Use `?page=2` for the next page. The response has `data`, `page` and `last_page`; it does not use the live API pagination envelope.

Retrieve a sandbox payment

```
GET /v2/sandbox/transactions/test_pay_<64-hex-id>
Authorization: Bearer YOUR_TEST_SECRET_KEY
Accept: application/json
```

Empty list response

```
{
  "success": true,
  "data": [],
  "page": 1,
  "last_page": 1
}
```

## Checkout simulator

Choose one terminal outcome. Nothing is charged.

| Choice | status | payment_status | Event |
| --- | --- | --- | --- |
| Success | paid | succeeded | transaction.succeeded |
| Failure | failed | failed | transaction.failed |
| Cancellation | cancelled | cancelled | transaction.cancelled |

A fresh payment starts with `status: open` and `payment_status: null`. Completion is single-use: choosing a result again does not create another event. Create a new payment with a new idempotency key to test another outcome.

Checkout expires after 24 hours (HTTP 410). The simulator presents a return button instead of automatically redirecting to an external site. Do not treat arrival at a return URL as proof of payment; verify the API result or signed notification.

This flow does not test real card authorisation, 3DS, settlement, refunds, terminals or bank payouts. It does not require Stripe test-card numbers.

Successful result fields

```
{
  "status": "paid",
  "payment_status": "succeeded",
  "sandbox": true,
  "livemode": false,
  "environment": "sandbox"
}
```

## Sandbox webhooks & retries

Configure up to three endpoints in [Developer → Sandbox mode → Webhooks](https://eu.neropay.app/merchant/developer?mode=test&tab=webhooks). Only enabled endpoints subscribed to the selected outcome receive the JSON event.

Supported events: `transaction.succeeded`, `transaction.failed`, `transaction.cancelled`. Sandbox endpoint management is in the dashboard, not a public sandbox webhook-management API.

### Verify JSON signatures

Compute lowercase hexadecimal `HMAC-SHA256(timestamp + "." + raw_request_body, webhook_signing_secret)` and compare it in constant time with `X-NeroPay-Signature`. Read the timestamp from `X-NeroPay-Timestamp`, enforce an appropriate freshness window and deduplicate `X-NeroPay-Event-Id`. Do not re-serialize the JSON before verifying.

Headers also include `X-NeroPay-Environment: sandbox` and `X-NeroPay-Delivery`. The event contains `id`, `type`, `created_at`, `livemode: false`, `environment: sandbox` and payment `data`. Checkout URLs and capability tokens are excluded from the callback.

### Delivery behaviour

The first attempt runs after the simulator response. Failed or pending deliveries can be retried from Developer: at least one minute apart, at most five total attempts, and within ten hours of creation. Sandbox deliveries do not use the live automatic redelivery queue. A delivered event is not resent by this test retry action.

Return HTTP 2xx promptly. Test callbacks have a five-second timeout and a 64 KB response limit. Public HTTPS hostnames on port 443 with publicly routable IPv4 resolution are required; localhost, private/reserved IPs, embedded credentials, URL fragments and redirects are not supported.

Example JSON webhook headers

```
Content-Type: application/json
X-NeroPay-Environment: sandbox
X-NeroPay-Event-Id: evt_test_<event-hash>
X-NeroPay-Delivery: evt_test_<event-hash>
X-NeroPay-Timestamp: <unix-seconds>
X-NeroPay-Signature: <lowercase-hmac-sha256>
```

Use the endpoint's sandbox signing secret for JSON webhooks, not your live API key. Store callback results in a separate test system.

## Hosted-payment IPN

Supplying `ipn_url` when creating a payment adds a form-encoded callback for the simulated outcome. This is separate from the dashboard's JSON webhook endpoints; do not process the same order twice if you enable both.

Validate the form field `signature` using uppercase hexadecimal `HMAC-SHA256(data.amount + identifier, sandbox_API_secret)`. Use the exact amount string received, including eight decimal places, not a floating-point reconstruction.

Top-level `status` is `success`, `failed` or `cancelled`; success is not named `succeeded` in this form payload. The form includes `environment=sandbox` and `livemode=false` as strings. `data.currency` is an object with `code` and `symbol`.

Decoded form fields · example

```
status=success
identifier=sandbox-order-0001
environment=sandbox
livemode=false
signature=<UPPERCASE_HMAC_SHA256>
data[payment_trx]=NP-TEST-0123456789ABCDEF
data[amount]=10.00000000
data[currency][code]=GBP
data[currency][symbol]=£
data[account_holder]=Test Customer
data[charge]=0
data[status]=success
data[payment_type]=hosted
data[payment_timestamp]=2026-10-01 12:00:00
```

## Limits, errors & unavailable operations

| Limit | Value |
| --- | --- |
| API requests | 30 per minute per merchant. |
| Request body | 16 KB maximum. |
| New test payments | 100 in the preceding 24 hours. |
| Stored test payments | 2,000 maximum. |
| Webhook endpoints | 3 per merchant. |
| Checkout validity | 24 hours. |

Developer → API → Clear expired test records removes expired test payments and test logs/deliveries older than seven days. It does not delete keys, webhook settings or live data. Test API logs retain up to 500 recent entries and exclude credentials and raw request bodies.

### Common HTTP results

**401:** invalid/mismatched test credentials. **403:** unsupported operation. **404:** payment not found for this merchant. **409:** idempotency conflict. **410:** checkout expired. **413:** request too large. **422:** invalid fields or unsafe URL. **429:** rate, payment or storage limit. **503:** sandbox storage has not been installed.

Balances, transfers, payouts, refunds, bank accounts, connected-account routing, terminals and live resource changes are unavailable. Requests outside the supported paths/methods fail closed; they do not fall back to the live API. Old generic dry-run resource and terminal endpoints are not supported by this sandbox.

Blocked operation response · HTTP 403

```
{
  "success": false,
  "error": {
    "code": "sandbox_operation_disabled",
    "message": "Sandbox supports simulated hosted payments and payment links only. Balances, transfers, payouts, refunds, account changes and live resources are unavailable."
  }
}
```

## Ready plugins & going live

Prefer an existing integration? Browse [ready-to-use plugins](https://eu.neropay.app/merchant/integrations#npReadyPlugins). Supported plugin flows do not require custom API development, but installation, account setup and credentials may still be required. Check each plugin's own sandbox compatibility; this simulator does not automatically add sandbox support to every plugin.

When ready, switch this reference to LIVE and follow the live authentication and endpoint contract. Configure separate production credentials and callbacks. Sandbox records and outcomes are not migrated into real transactions, and this selector does not activate live payments.

## AI Reception API

Manage your business receptionist, opening hours, voices, service availability and customer follow-ups from your server.

Base URL: `https://eu.neropay.app/v2/ai-reception`. This is a live, merchant-owned management API. Calls, orders and bookings continue through the existing receptionist and integrated business workflows.

### Access and subscription

An active AI Reception subscription is required on every request. Starter includes 100 minutes and one UK number for £49.90/month including VAT. Growth includes 250 minutes and one UK number for £99.90/month including VAT. Cancelling renewal preserves access until the paid period ends. Pending, failed or expired subscriptions do not grant API access. Explicit complimentary access grants are reported as `complimentary: true`; they are not paid subscriptions.

The subscription can be purchased and managed in [AI Reception in the merchant dashboard](https://eu.neropay.app/merchant/ai-reception). The API does not purchase, cancel or change subscription payment methods. Additional minutes on paid plans cost £1 per started minute when enabled, subject to the spending cap. SMS uses the merchant's existing SMS tariff and GBP balance. Complimentary grants have a calendar-month allowance and do not support additional-minute billing.

### Authentication

Send `Authorization: Bearer YOUR_LIVE_SECRET_KEY` and `Accept: application/json`. The alternative `X-NeroPay-Secret-Key` header is supported; conflicting credentials are rejected. `X-NeroPay-Public-Key` is optional and must match when supplied. Keys remain on your server.

Use the workspace owner's credentials. `NeroPay-Account`, merchant/account routing fields and connected-account delegation are not supported. An active merchant account is required. These software endpoints do not require payment-account onboarding or a browser bot challenge; authentication, subscription and request limits still apply. No sandbox endpoints are provided.

### Writes and retries

For every POST, PUT or PATCH, send a JSON object, `Content-Type: application/json` and an 8–100 character `Idempotency-Key` containing letters, numbers, dots, underscores, colons or hyphens. Send write fields in the body, without query parameters. Reuse the same key and exact body for a retry of the same operation. A completed result is replayed with `Idempotency-Replayed: true`. Reusing a key with a different method, path or body returns 409. Use a new unique key for each new operation.

If your merchant account requires signed API writes, also send `X-NeroPay-Timestamp` (Unix seconds, within five minutes) and `X-NeroPay-Signature` (hexadecimal HMAC-SHA256 of `timestamp + "." + raw_json_body` using your secret key). Supplied signatures are always checked, even when optional. A retry can use a fresh signature with the same body and idempotency key.

An uncertain service failure is marked for review and is not automatically run again. Inspect the dashboard state before attempting a new operation; contact support if the result is unclear. Idempotency records follow the workspace retention policy, and keys must never be recycled.

### Limits and errors

Maximum body: 128 KiB. Maximum JSON depth: 32. Writes: 20/minute per merchant; reads: up to 120/minute per merchant. Application and IP limits also apply. Use `Retry-After` on 429. Uploads, raw card credentials and live outbound-call creation are not supported.

| HTTP | Meaning |
| --- | --- |
| 401 | Missing, invalid or expired credentials/signature. |
| 402 | No active AI Reception entitlement. |
| 403 | Inactive account or operation not permitted. |
| 404 | Resource not found in this merchant workspace. |
| 409 | Workspace busy, retry-key conflict, setup incomplete or prior result needs review. |
| 413 / 415 / 422 | Oversized request, wrong content type or invalid fields. |
| 429 | Request limit reached. |
| 503 | Service or installation unavailable. Inspect workspace state before retrying writes. |

Read your workspace

```
curl 'https://eu.neropay.app/v2/ai-reception' \
  -H "Authorization: Bearer $NEROPAY_SECRET_KEY" \
  -H 'Accept: application/json'
```

Example error · HTTP 402

```
{
  "success": false,
  "error": {
    "code": "subscription_required",
    "message": "An active AI Reception subscription is required."
  }
}
```

## Settings, tools & MCP

`GET /settings` returns the settings object in `data`. `PUT /settings` saves a complete settings object. Read, edit and send it back so omitted values are not accidentally reset. This is not a partial PATCH endpoint. Voice and instructions take effect after `POST /publish`; integration and accepting switches apply immediately.

| Fields | Rules |
| --- | --- |
| business_name / prompt / first_message | Required strings, up to 120 / 10,000 / 1,000 characters. |
| knowledge / guardrails | Optional text, up to 20,000 / 5,000 characters. |
| voice_id | ID returned by GET /voices; required before publishing. |
| language / languages | Primary language must be in the 1–11 supported languages: en, tr, de, fr, es, it, pt, nl, pl, ar, hi. |
| timezone / hours | IANA timezone and exactly seven objects, Monday first. Each needs closed (boolean), open and close (HH:mm). Earlier closing time means overnight. Equal times provide no open interval. |
| outside_hours | callback or hangup; closed_message and busy_message required, up to 500 characters each. |
| transfer_number / transfer_sip | Optional supported UK +44 geographic/mobile destination, or sip:user@host (optional port 5060/5061). Cannot loop to the reception number. |
| receipt_channel / sms_enabled | email, sms or none; SMS requires the merchant setting and recipient consent. Existing SMS fees apply. |
| save_customer / record_voice | Boolean preferences; existing consent and disclosure rules apply. No raw card data. |
| retention_days / max_call_seconds | 7–90 days / 60–600 seconds. Re-publish to apply the call duration. |
| overage_enabled / spend_cap_pence / wallet_fallback | Boolean / integer 0–50,000 pence / boolean. Additional minutes cannot be enabled on complimentary grants. |
| integrations / accepting | Boolean maps for neroweb, nerobooking, nerogym, nerotrade and neroconnect. Accepting does not enable an unconfigured integration. |
| tools / mcp | Arrays, including empty arrays. Maximum 10 custom tools and 4 MCP connections. |

### Custom tools and MCP

Custom tool fields: `name` (custom_ followed by 1–40 lowercase letters, digits or underscores), public HTTPS `url`, `description` up to 1,000 characters, object JSON `schema` up to 10 KB, optional bearer `token` and `approved: true`.

MCP fields: unique `name` up to 80 characters, public HTTPS `url`, `transport` (SSE or STREAMABLE_HTTP), optional `token` and `approved: true`. Approval authorises the endpoint's tools to run during conversations. Private IPs, URL credentials, fragments and non-443 ports are rejected.

Tokens and internal connection identifiers are never returned. GET reports `has_token`. Omitting or leaving a token empty preserves the existing secret only when connection name and URL match. Remove the connection to remove its credential. The `whatsapp_enabled` field is read-only here; use POST /whatsapp.

Complete settings example · PUT /settings

```
{
  "business_name": "Example Kitchen",
  "prompt": "Be a helpful receptionist. Ask one question at a time.",
  "knowledge": "We serve collection orders during opening hours.",
  "guardrails": "Do not invent prices or availability. Offer a callback when unsure.",
  "first_message": "Hello, you are speaking with our AI receptionist. How can I help?",
  "voice_id": "VOICE_ID_FROM_VOICES",
  "language": "en",
  "languages": [
    "en"
  ],
  "timezone": "Europe/London",
  "hours": [
    {
      "closed": false,
      "open": "09:00",
      "close": "17:00"
    },
    {
      "closed": false,
      "open": "09:00",
      "close": "17:00"
    },
    {
      "closed": false,
      "open": "09:00",
      "close": "17:00"
    },
    {
      "closed": false,
      "open": "09:00",
      "close": "17:00"
    },
    {
      "closed": false,
      "open": "09:00",
      "close": "17:00"
    },
    {
      "closed": false,
      "open": "09:00",
      "close": "17:00"
    },
    {
      "closed": false,
      "open": "09:00",
      "close": "17:00"
    }
  ],
  "outside_hours": "callback",
  "transfer_number": "",
  "transfer_sip": "",
  "closed_message": "Our team is unavailable. I can take a callback request.",
  "busy_message": "We are not accepting orders or bookings right now.",
  "receipt_channel": "email",
  "sms_enabled": false,
  "save_customer": false,
  "record_voice": false,
  "retention_days": 30,
  "max_call_seconds": 300,
  "overage_enabled": false,
  "spend_cap_pence": 5000,
  "wallet_fallback": true,
  "integrations": {
    "neroweb": false,
    "nerobooking": false,
    "nerogym": false,
    "nerotrade": false,
    "neroconnect": false
  },
  "accepting": {
    "neroweb": true,
    "nerobooking": true,
    "nerogym": true,
    "nerotrade": true,
    "neroconnect": true
  },
  "tools": [],
  "mcp": []
}
```

Example custom tool entry

```
{
  "name": "custom_delivery_info",
  "url": "https://your-business.example/reception/delivery",
  "description": "Look up published delivery information",
  "schema": {
    "type": "object",
    "properties": {
      "postcode": {
        "type": "string"
      }
    },
    "required": [
      "postcode"
    ]
  },
  "approved": true
}
```

## Publish & live availability

Paths below are relative to `/v2/ai-reception`. Writes require the authentication and retry headers above.

| Method & path | Body / result |
| --- | --- |
| GET / | Active/enabled state, number, readiness, publication date, subscription, number_billing, minutes, plan definitions, SMS fee and WhatsApp availability. |
| GET /voices | Voice catalogue: id, name and labels. Choose a returned ID; preview is available in the dashboard. |
| GET /numbers?type=national&contains=0333 | UK number catalogue. type: local, national or tollfree. contains: optional 2–6 digit UK prefix, e.g. 020, 0333, 0800 or 0808. Response data contains items, cursor and message. Each item contains number, label, locality, type, premium, monthly_extra_pence, currency, vat_included, quote and quote_expires_at. Pass the returned cursor with unchanged filters to refresh for different available choices. Quotes and cursors are merchant-bound and expire after 15 minutes. Numbers and prices are rechecked before purchase. |
| POST /number | {"number":"+442012345678","quote":"RETURNED_QUOTE"} — reserve one included UK number. The matching quote is required. Premium choices return 402 and require the merchant to approve the extra £20/month including VAT in the dashboard payment drawer. The API cannot silently start premium billing. Existing assigned numbers are not replaced by refresh. A number with unpaid premium rental cannot activate reception. |
| POST /publish | {} — publish current voice, prompt, hours and tools. Choose a voice first. Number selection and activating reception are separate steps. |
| PATCH /availability | Optional enabled boolean and/or accepting map. Activating reception requires a number and a published agent. Disabling pauses reception and a connected messaging agent. |
| POST /whatsapp | {"enabled":true} — connect an operations-verified WhatsApp business number. Reception must be enabled first. false disconnects. |

### Suggested setup sequence

- Read GET / and GET /settings.
- Select a voice from GET /voices, save the complete settings with PUT /settings.
- Choose an included number with GET /numbers and POST /number using its returned quote. For premium choices, complete the merchant dashboard checkout first.
- Publish with POST /publish and activate with PATCH /availability.
- Test an inbound call, then inspect GET /calls and GET /usage.

NeroWeb orders and NeroBooking appointments use their existing validation, payment and fulfilment flows. NeroGym, NeroTrade and NeroConnect integrations currently handle information and callback enquiries. They do not create memberships or wholesale/partner orders. Caller card details must go through secure hosted checkout.

WhatsApp ownership and service configuration must be verified before connection. API activation does not register a new WhatsApp business account. Dashboard or API access alone does not allocate a phone number or make an unconfigured receptionist answer calls.

Pause ordering while keeping reception available

```
curl -X PATCH 'https://eu.neropay.app/v2/ai-reception/availability' \
  -H "Authorization: Bearer $NEROPAY_SECRET_KEY" \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: pause-orders-20261019-001' \
  --data '{"accepting":{"neroweb":false,"nerobooking":true}}'
```

Publish saved settings

```
curl -X POST 'https://eu.neropay.app/v2/ai-reception/publish' \
  -H "Authorization: Bearer $NEROPAY_SECRET_KEY" \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: publish-reception-20261019-001' \
  --data '{}'
```

## Calls, callbacks & usage

List endpoints accept `page` (1–10,000, default 1) and `per_page` (1–100, default 25). The response `data` contains `items`, `page`, `per_page` and `has_more`. Records are newest first. All IDs are scoped to the authenticated merchant.

| Method & path | Returned fields / operation |
| --- | --- |
| GET /calls | id, channel, caller, state, duration_seconds, minutes, summary and created_at. |
| GET /calls/{id} | One call by UUID. Returns 404 for another merchant's call. No audio, raw transcript, provider identifier or call-context credential is exposed. |
| GET /callbacks | id, state, created_at and details. Details contain the consented callback enquiry; they are personal data. |
| PATCH /callbacks/{id} | {"state":"completed"} — mark one callback UUID complete. This does not place a telephone call. |
| GET /usage | id, call_id, minutes, amount_pence, state, payment_method and created_at. Amounts are integer GBP pence. |

Phone duration is verified before final usage is recorded; in-progress calls can show zero minutes. Started minutes are rounded up. Included usage has zero amount and payment_method included. Additional paid usage follows the existing saved-card and configured balance-fallback collection flow. This endpoint cannot submit or alter billable usage.

Use the workspace overview for current-period minutes and subscription status. Data is subject to the workspace retention policy, so old summaries and enquiry details may be empty. No public receptionist tool-execution, raw payment, receipt-send or outgoing-call endpoint is exposed by this API.

Read recent calls

```
curl 'https://eu.neropay.app/v2/ai-reception/calls?page=1&per_page=25' \
  -H "Authorization: Bearer $NEROPAY_SECRET_KEY" \
  -H 'Accept: application/json'
```

Empty list response

```
{
  "success": true,
  "data": {
    "items": [],
    "page": 1,
    "per_page": 25,
    "has_more": false
  }
}
```

On this page

Build with confidence[Authentication →](https://docs.neropay.app/api/#auth)[Test in the sandbox →](https://docs.neropay.app/api/#sandbox-intro)

NeroPay Docs

## Documentation library

[Explore all documentation ↗](https://docs.neropay.app/) On this API page

### Developer resources

[Live API reference](https://docs.neropay.app/api/)[Sandbox API reference](https://docs.neropay.app/api/?environment=sandbox) 

### Product documentation

[Browse the documentation library](https://docs.neropay.app/#browse-docs)

### Help & updates

[Support](https://docs.neropay.app/support)[Changelog](https://docs.neropay.app/changelog)[System status ↗](https://status.neropay.app/)
