API

This page is currently only available in English.

The proxy API is intentionally smaller and easier to consume than raw OPI. You send JSON requests to the proxy, and the proxy handles the terminal-side OPI conversation.

API Basics

  • terminal-scoped operations use terminalAlias
  • stored follow-up actions use transactionId
  • almost all routes require a bearer token
  • GET /health is intentionally public
  • mutating routes support Idempotency-Key for safe retries

Authentication

Use:

Authorization: Bearer <token>

Health

GET /health

Example response:

{
  "ok": true,
  "service": "nexi-opi-proxy",
  "version": "1.0.5"
}

Terminal Routes

GET /terminals/:terminalAlias/info

Returns terminal information, parsed info-receipt data, and discovered payment methods when available.

GET /terminals/:terminalAlias/status

Returns terminal status and cached reachability information.

GET /terminals/:terminalAlias/ping

Sends four ICMP ping packets to the configured terminal IP. A reachable terminal returns 204 No Content; an unreachable terminal returns 503 Service Unavailable with no body.

Use this route as a simple network presence check. It does not open an OPI session or prove that the terminal application is active; use /status when you need the live OPI status payload.

GET /terminals/:terminalAlias/receipts/latest

Returns the latest stored receipt bundle for the terminal. That may come from a transaction or from a terminal service operation.

GET /terminals/:terminalAlias/receipts?limit=5

Returns the most recent stored receipt bundles for the terminal across transaction and terminal service flows.

POST /terminals/:terminalAlias/payment

Starts a sale or a preauthorisation.

Default sale:

{
  "reference": "ORDER-100045",
  "amount": 24.90,
  "currency": "CHF"
}

Preauthorisation uses the same route with autoCapture=false:

{
  "reference": "ORDER-100046",
  "amount": 120.00,
  "currency": "CHF",
  "autoCapture": false
}

Cashback also uses the same route. Send the total EFT amount as amount and the cash withdrawal as cashback. The proxy forwards OPI TotalAmount = amount and CashBackAmount = cashback.

{
  "reference": "ORDER-100047",
  "amount": 50.00,
  "cashback": 20.00,
  "currency": "CHF"
}

cashback must be less than or equal to amount.

For unattended IM15 and IM30 proxy-mode flows, the proxy also stores receipts by default and returns receipt metadata through the receipt endpoints.

For merchant setups approved for Forced Acceptance, the payment request can include forceAcceptance=true. The proxy forwards this to OPI as ForceAcceptance in POSdata.

{
  "reference": "ORDER-100048",
  "amount": 24.90,
  "currency": "CHF",
  "forceAcceptance": true
}

Use this only when the merchant and terminal setup supports Forced Acceptance and normal online authorisation cannot be reached. The integrator is responsible for stopping forceAcceptance once normal authorisation is available again. The proxy returns forceAcceptance on payment transaction responses when it was requested, and returns systemAvailable: true when a Forced Acceptance payment reports terminalDeclineReason: 0; at that point, send forceAcceptance: false or omit the parameter on later payments. See Offline Payments for the Own Risk model.

POST /terminals/:terminalAlias/reversal

Starts a direct terminal-side PaymentReversal.

The proxy does not send the original approval code. If the proxy has a previous successful reversible transaction for the terminal, it reuses that amount. If no stored proxy transaction exists, the proxy can still forward the reversal request to the terminal.

POST /terminals/:terminalAlias/refund

Starts a direct terminal-side refund.

Example:

{
  "reference": "REFUND-100045",
  "amount": 24.90,
  "currency": "CHF"
}

POST /terminals/:terminalAlias/reprint

Requests a reprint of the last receipt through the terminal.

POST /terminals/:terminalAlias/transmit

Triggers OPI TransmitTrx.

Use this route as part of recovery when a merchant setup has pending offline or Forced Acceptance transactions that must be submitted or transmitted after connectivity returns. See Offline Payments for the business and risk model.

POST /terminals/:terminalAlias/config

Triggers OPI ContactTMS and returns the parsed terminal configuration payload from the accept host ep2.

This route can be called with an empty body, or with responseMode when the client should not wait for the terminal operation.

POST /terminals/:terminalAlias/init

Triggers OPI ContactAcq and returns the parsed merchant-contract initialisation and supported brand payload from the accept host ep2.

This route can be called with an empty body, or with responseMode when the client should not wait for the terminal operation.

POST /terminals/:terminalAlias/activate

Activates or logs in the terminal when the target setup requires it.

By default, the proxy returns early if its cached state already says the terminal is active. Send { "force": true } to bypass the cached state and send the OPI Login request anyway. AlreadyActivated is treated as a successful state confirmation.

POST /terminals/:terminalAlias/deactivate

Deactivates or logs out the terminal when the target setup requires it.

By default, the proxy returns early if its cached state already says the terminal is inactive. Send { "force": true } to bypass the cached state and send the OPI LogOff request anyway. AlreadyDeactivated is treated as a successful state confirmation.

POST /terminals/:terminalAlias/abort

Sends an abort request for an in-progress card flow.

POST /terminals/:terminalAlias/close

Triggers OPI CloseDay and performs the final balance with logoff at the end.

This route can be called with an empty body. In async mode, the proxy returns an empty HTTP 202 with Preference-Applied: respond-async.

POST /terminals/:terminalAlias/reset

Requests a terminal restart.

Payment Example

POST /terminals/front-counter/payment
Authorization: Bearer <token>
Content-Type: application/json
{
  "reference": "ORDER-100045",
  "amount": 24.90,
  "currency": "CHF",
  "paymentMethods": ["visa", "mastercard", "twint"],
  "receiptHandling": {
    "customer": "external",
    "merchant": "external"
  }
}

Important notes:

  • workstationId is the OPI WorkstationID. Configure it as a stable, unique POS/ECR identity per checkout or terminal context. If omitted, the proxy uses the terminal alias.
  • autoCapture=true means sale
  • autoCapture=false means preauthorisation
  • ClerkId and ShiftNumber are optional and are omitted from OPI if not supplied
  • paymentMethods limits which explicitly filterable brands can be processed for a request. It does not change terminal UI branding.
  • the example paymentMethods values reflect brands officially tested through the OPI Proxy as of today
  • other brands relevant to the Swiss market can still work through OPI even if they are not yet available for request-level filtering through paymentMethods
  • receiptHandling.customer=external and receiptHandling.merchant=external send receipt output to the proxy for storage under data/receipts; in this mode, the ECR is responsible for asking whether the customer receipt should be printed, shown, sent, or skipped
  • receiptHandling.customer=local and receiptHandling.merchant=local let terminals with local printers handle those copies on the terminal
  • on validated IM30 unattended proxy-mode payment flows, the terminal sends DeliveryBox, the proxy acknowledges it with Success, and the terminal continues with its own delivery-success messaging
  • /config and /init return parsed terminal service details, including status data and any supported brand data returned by the terminal
  • service routes that print slips return receipt metadata and file paths, not the full receipt body
  • tips and DCC are terminal-side configuration topics, not public JSON request flags

Async Responses

By default we recommend RESPONSE_MODE=async. With RESPONSE_MODE=async, request body "responseMode": "async", or Prefer: respond-async, long-running routes return immediately after the operation is accepted.

Payment-style async response:

{
  "transactionId": "{transactionId}",
  "status": "pending",
  "terminalAlias": "front-counter"
}

The response includes Location: /transactions/{transactionId}. Poll GET /transactions/:transactionId for the source-of-truth result.

Service-style operations such as /config, /init, /transmit, /close, /reprint, /abort, and /reset return an empty HTTP 202 with Preference-Applied: respond-async.

pending means the terminal operation has not fully ended. unknown means the proxy could not safely determine the final payment or terminal result.

Mutating routes also support Idempotency-Key. Reusing the same key with the same request method, route, async preference, and JSON body returns the same proxy response again. Reusing the same key with different request content returns HTTP 409.

If the terminal is already locked by another long-running operation, the proxy returns 409 with the active operation:

{
  "error": {
    "code": "BUSY",
    "message": "Terminal already has an active operation."
  },
  "terminalAlias": "front-counter",
  "activeOperation": "payment"
}

Receipt Routes

The canonical receipt lookup is transaction-based:

GET /transactions/:transactionId/receipts

That is the stable route to use once you have a stored transactionId.

The terminal receipt routes are convenience lookups across the latest stored terminal receipts, including service-operation slips:

  • GET /terminals/:terminalAlias/receipts/latest
  • GET /terminals/:terminalAlias/receipts?limit=5

Transaction Routes

GET /transactions/:transactionId

Reads a stored transaction.

POST /transactions/:transactionId/capture

Captures a stored preauthorisation.

Example:

{
  "amount": 120.00
}

The resulting capture transaction includes parentTransactionId, pointing back to the stored preauthorisation.

POST /transactions/:transactionId/cancel

Cancels a stored preauthorisation.

POST /transactions/:transactionId/refund

Refunds a stored sale or captured transaction.

Example:

{
  "amount": 25.00
}

Follow-up transactions use parentTransactionId to point to the transaction they were created from. The public API does not expose a separate rootTransactionId; if no parent exists, the transaction stands on its own.

PATCH /transactions/:transactionId

Admin-only recovery route for manual transaction correction.

Response Style

Successful responses return JSON data plus requestId, except for empty async 202 responses on service-style operations.

Error responses use this shape:

{
  "error": {
    "code": "SOME_CODE",
    "message": "Human-readable message"
  },
  "requestId": "..."
}