Nexi POS API Reference

The Nexi POS API is a simple REST API for making payments on payment terminals provided by Nexi. If this is your first time here, we recommend starting from the guide-level documentation to get an overview of the API and how to use it.

Currently Nexi POS API is in preview. This means that only the simulated terminals are publicly available, and v0 of the API is subject to change without notice. As production terminals become available, the API will be updated to v1 and the API will be considered stable.

Please leave feedback at https://forms.office.com/e/t4q1Sdxs2g

Environments and base addresses

All of the endpoints are available under the following base addresses:

EnvironmentBase address
Livehttps://api.npay.eu/pos/v0/

These endpoints are globally available and will automatically route the client to the closest region. DNS should be used to resolve the domain on each request, respecting the standard TTL behaviour for DNS. The endpoints are only available over TLS 1.2 or higher with strong cipher suites. See Security and compliance for more details.

All terminals are transparently available in all regions. This means that even if a client connects from Germany, and a terminal is deployed in Finland, it will be fully available through the same API endpoint.

Testing and development of clients should be done in the Live environment, with simulated terminals, see Development and testing for more details.

If you would desire to have fixed static IP addresses for the API, please let us know through the feedback form: https://forms.office.com/e/t4q1Sdxs2g

Requests and responses

HTTP method

All the requests in this API are made using the HTTP POST method, even for safe (read-only) operations.

If a method other than POST is used the API will respond with a 405 Method Not Allowed error with an Allow header listing POST.

Path

The root path of the API is /pos/v0/. All operations are available under this path, for example /pos/v0/transaction/purchase. The major version of the API is embedded directly in the path and is incremented every time there is a breaking change in the API. See Breaking changes for more details on what is considered a breaking change.

Request headers

The API uses standard HTTP headers for all requests and responses. The headers that are mandatory are:

HostThe host name of the API endpoint, which is always api.npay.eu.
AuthorizationThe API key used for authentication, see Authentication.
User-AgentThe user agent of the client, see User agent.
Content-LengthThe length of the request body in bytes.

Recommended headers are:

Content-TypeThe content type of the request body, which is always application/json.
AcceptThe content type of the response body, which is always application/json.

Conditional operations, such as If-None-Match or If-Modified-Since, are not used in this API. Content negotiation is used and validated, but the only supported content type is application/json. Content-Encoding is not supported and responses will always be uncompressed.

The API internally supports W3C Trace Context for distributed tracing. The traceparent and tracestate headers are not required, but if they are present, they will be used to propagate the trace context. Communicating the trace-id may be helpful in problem solving.

Request body

The body of the request is always a JSON object. The request object contains all the parameters for the operation. Query parameters or bodies with other content-types are not used. See JSON encoding for details.

Requests without a body, or with a body that is not a JSON object, or with Content-Type different from application/json will be rejected with 400 Bad Request.

Response status codes

Successful responses will always return a 200 OK status code with a response body. 201 Created and 202 Accepted are not used even for responses where they might be more appropriate. If the client requests a 100-continue response the server will send a 100 Continue response before procesing the request. However, it is not guaranteed that the request headers, such as authentication, would be processed before the full request is received.

Error responses will return 4xx status code for client errors or 5xx status code for server errors. Certain errors that may be meaningful on the HTTP level are mapped to specific status codes, but the specific error code is always delivered in the response body. The HTTP status code never provides any additional information compared to the response body, so may be safely ignored if processing the error code in the body.

Client errors (4xx) are used for errors where retrying the request is not likely to succeed without modifying the request. This includes also configuration errors where the operation cannot be completed because the terminal is not configured to support the requested operation. It is the responsiblity of the client to resolve the error somehow.

Server errors (5xx) are used for errors where retrying the request may succeed even if the request is not modified. This includes transient errors in the backend infrastructure. However, since this also includes internal errors, certain errors may be persistent and retrying the request may not succeed. It is the responsibility of the server to resolve the error.

The response being an error does not always mean that the request was not processed. If the error response is being generated by a service in the processing path, the request may still have been processed successfully. Transaction flow should be followed to ensure reliable operation.

Response headers

Response headers will always include:

Content-TypeThe content type of the response body, which is always application/json, unless a network component (e.g. load balancer) produces an error response. In that case, the content type may be text/html or text/plain.
Content-LengthThe length of the response body in bytes, omitted if Transfer-Encoding: chunked is used.

The response may also include any number of additional headers. Especially important is the Retry-After header, which indicates how long the client should wait before retrying the request if the request processing failed. See Rate limits for more details.

Responses should not be cached, as is the default for all POST requests, even when the server doesn’t send a Cache-Control header.

Response body

Successful responses will always contain a body generated by the server. The response body is always a JSON object in accordance with our JSON encoding rules. The body of a successful response will never contain a top-level error key.

A successful response means that the request was successfully processed by the server, but not necessarily that the requested operation was successfully completed. For example, a purchase may be successfully processed, but the authorization was declined, resulting in 200 OK containing a transaction with a INSUFFICIENT_FUNDS as the result_code. Clients must always check the result_code of transactions to determine the true outcome of the operation.

Error responses will be a JSON object in accordance with our JSON encoding rules. The JSON object will always contain a top-level error key. The error object will always contain a code key with the error code and a description key with the error message. The error codes are discussed in more detail in Results and errors.

Network infrastructure that is in the processing path of the request may sometimes also inject their own error responses. These responses may have any content-type for the response body. No specific guarantees are made about the content of these responses and no specific meaning should be assigned to them.

JSON encoding

All requests and responses are encoded in JSON. The API uses I-JSON message format as described in RFC 7493 with the “Must-Ignore” policy. This is standard JSON with the following additional restrictions:

  • Encoding is always UTF-8
  • Unpaired surrogates and non-characters are not allowed
  • Numbers can always be represented as IEEE754 binary64 floating point numbers
  • Objects cannot have duplicate keys
  • Order of keys in objects does not matter
  • Object members whose names are not recognized must be ignored

There are no special rules on white space, so the client can send messages with any amount of white space and must ignore any amount of white space in the response. The server may change the encoding of the JSON in response without notice, as long as the meaning of the message is preserved.

Additionally, floating point numbers are not used in the API. Instead fractional numbers are delivered as integers with the number of decimal places specified by context.

Authentication

The API uses API keys for authentication. The API key is passed via basic authentication (username/password) in the Authorization header as described by RFC 7617.

The username identifies the API key id, and the password is the API key secret. Incorrect authentication headers or expired API key will result in a 401 Unauthorized response. There is no WWW-Authenticate header sent, as the API is not meant to be used by browsers.

Code sample

A standard way to add the authentication in JavaScript is to add the Authorization header to the Headers object in the fetch API:

new Headers({
  'Authorization': 'Basic ' + btoa(`${username}:${password}`),
})

Policy

Each API key has an associated policy that defines what the key can do. API keys can be limited to apply to specific subset of terminals that the partner has access to.

If the API key is not allowed to perform a specific action, the server will return a 403 Forbidden response. In case the API key is allowed to perform the operation, but not on the specific resource in question, the response may also be 404 Not Found.

API keys must be kept confidential. Please see Security and compliance for more details.

API keys will be provided for you during the partner onboarding process. Please see Onboarding for more details.

Rate limits

The API rate limits clients to prevent abuse and to protect the API from overloading. Rate limits are applied per API key and per terminal. If the rate limits for a certain operation differ from the common rate limits, these are documented in the API reference for that operation.

The server will return a 429 Too Many Requests response if the rate limit is exceeded. The response should also have a Retry-After header which indicates how long the client should wait before making a follow-up request.

In case the server is overloaded, the server may return a 503 Service Unavailable response. This indicates that the server is temporarily unable to process the request. This response should also have a Retry-After header which indicates how long the client should wait before making a follow-up request.

In case there is no Retry-After header in the response, the client should have a sensible delay of a few seconds before retrying the request. The client is also recommended to do exponential back-off in case of repeated 429 Too Many Requests or 503 Service Unavailable responses without Retry-After headers.

Breaking changes

The API is versioned by a version number in the URL path. The version number is incremented every time there is a breaking change in the API. A breaking change is defined as a change that would cause existing clients that adhere to the API specification to fail to work correctly.

When a new version is published, the online documentation for the API will be updated to the new version, along with a migration guide from the previous version. Old versions of the API will remain kept available for requests and will continue to work as before, but their documentation will be no longer be accessible online. comment: <> (FIXME Make “migration guide” into a link once one exists)

Old versions will be kept available for existing integrations for a period of 12 months after the new version is published. Clients still using the old version 3 months before end of this period will be notified by email and reminded to migrate to the new version.

Backward compatible changes

Backward compatible changes are changes that do not break existing clients that adhere to the API specification. These changes are not considered breaking changes and the version number is not incremented.

Backward compatible changes are identified by a release date, such as 2025-04-29. These changes can be done at any time without advance notice. There is no way for the client to select which release they wish to use, nor is there any way to roll back to an older release.

Backward compatible changes are listed in the changelog, but the schema will only document the latest version.

The API uses I-JSON message format as described in RFC 7493 with the “Must-Ignore” policy. This means that the API is designed to be extensible and new fields can be added to the response without breaking existing clients.

Backward compatible changes include:

  • Adding an operation
  • Adding an optional field to the request
  • Adding a field to the response
  • Making a required field in the request optional
  • Changing the order of fields in the response
  • Changing whitespace in the response
  • Changing behaviour of the API when the request does not adhere to the specification
  • Changing response field contents that are documented to be informational only, such as error descriptions

Breaking changes include:

  • Removing an operation
  • Removing or renaming a field from the request
  • Removing or renaming a field from the response
  • Changing an optional field to be required in the request
  • Adding a new required field to the request
  • Changing the type of a field in the request or response
  • Changing enumeration values in the request or response
  • Adding a new enumeration value to the response
  • Changing the meaning of a field in the request or response
  • Adding new validation to an existing field in the request
  • Changing authentication or authorization requirements

Any compatibility issues resulting from backward compatible changes breaking client implementation are fully the responsibility of the client implementer, and will not be considered a breach of backwards compatibility.

Long polling

Long polling is a technique used to reduce the number of requests made to the server and to improve the responsiveness of the client. It is a variation of traditional polling, where the client sends a request to the server and waits for a response. In traditional polling, the client sends requests at regular intervals, without knowing if there is new data available. This can lead to unnecessary network traffic, as the client may be sending requests when there is no new data to retrieve as well as increased latency as the client has to wait for the next polling interval to receive updates.

In long polling, the server will not immediately respond to the client’s request, but instead waits for a certain amount of time before sending a response if there is no new data available. This allows the server to immediately respond to the client when new data is available, improving the responsiveness of the client. It also reduces the number of requests made to the server, as the client does not need to send requests at regular intervals. It will, however, increase the number of open connections to the server, as the client will keep the connection open until it receives a response.

This API uses long polling for all operations that require waiting by default. The default time to wait is 25 seconds, but this can be controlled by the client by sending the options.wait_seconds parameter in the request. This means that the server will not instantly respond to the client’s request, but instead will wait until the request is in a state where it requires interaction with the client or until the wait time is reached.

The options.wait_seconds parameter does not mean any kind of a timeout for the operation itself and will not cause an error to be returned if the wait time is reached. It merely means that the server will delay responding to the request until a suitable time. If the operation is already in a state where it requires interaction with the client, the server will respond immediately, regardless of the options.wait_seconds parameter.

If the long polling wait time is reached and the operation is not in a state where it requires interaction with the client, the server will respond with the current state of the operation. The client can then retry the request with the same parameters, which will not cause a duplicate operation to be created, as the APIs are designed to be idempotent. See Idempotency for more details.

The client can us a different options.wait_seconds parameter for each request and it will affect only that request. For example, the client can send a request with options.wait_seconds of zero on the first request, getting the transaction state immediately after creation, and then follow up with a request without options.wait_seconds, causing the default wait time of 25 seconds to be used. Or the client can send every request with options.wait_seconds of zero and waiting 2 seconds between request, which makes the API behave like a short polling API.

Idempotency

Ensuring that transactions are processed once and only once is a fundamental requirement for any payment system. There shouldn’t be any risk of creating duplicate payment transactions even when there are network errors or intermittent connectivity issues.

This is achieved by using idempotency in the API. Idempotency is a property of an operation that allows it to be performed multiple times without changing the result beyond the initial application. In other words, if an operation is idempotent, performing it multiple times will have the same effect as performing it once.

This API does not use explicit idempotency keys per request. Instead, every transaction has specific fields, external_id and terminal_id, that are used to identify the transaction. The server will use these fields to ensure that no duplicate transactions are created, even if the same request is sent multiple times.

  • If the client resends the same request, and the server started processing the previous request, the server will respond with the current state of the transaction (with long polling, when applicable). This is the standard way of continuing transaction processing.
  • If the client sends a different request with the same identifiers, and the server started processing the previous request, the server will respond with an error. Also if too much time has passed since the original request, the server may respond with an error.

The options field is omitted from the comparison check between the old request and the new request.

User agent

All requests to the API must include a valid User-Agent header. The User-Agent header identifies the client application that is making the request. Requests without a valid User-Agent header will be rejected.

The format of the User-Agent header is defined in RFC 9110. In this API it should follow the general format:

User-Agent: <product>/<version> (key:value; ...)

The <product> in the header should clearly identify the client application that is making the requests towards the API. It is important to uniquely identify different client applications as different <product> values. The <product> value must not contain whitespace. A good rule of thumb is that each different codebase that is making requests should have a separate <product> value.

The <version> in the header should be an accurate version number that identifies the version of the client application that is making the requests. The <version> value must not contain whitespace. Multiple different application versions should all use a different <version> value.

The key:value pairs in the header can be used for any additional information that is relevant to distinguish client applications, such as operating system, commit hash, build number, etc. The key:value pairs should be separated by semicolons and can contain whitespace. The key:value pairs can be omitted if not needed, in which case the parentheses should be omitted as well.

In case the application consists of multiple components, such as having a separate SDK to talk to the API, these can be combined into a single User-Agent header by simply concatenating the different components with a space. For example, if the application is called MyApp and the SDK is MyAppSDK, the User-Agent header could look like this:

User-Agent: MyApp/1.0 (os:Windows; git:abc1234) MyAppSDK/1.0 (lang:C++)

The User-Agent header is used for logging and debugging purposes and does not directly affect functionality. It is used to track different client implementations and versions, and to help identify issues that may arise in specific client implementations.

Security and compliance

TLS

The API endpoint is only available over HTTPS. All requests must be sent over TLS 1.2 or higher with strong cipher suites to ensure secure communication. The service enforces HSTS and rejects weak SSL/TLS versions. Clients must verify the server’s SSL/TLS certificate to prevent man-in-the-middle attacks; the certificate chain is signed by Amazon Root CAs.

API keys

API keys must be kept confidential and should not be hard-coded in client applications. Instead, use secure storage mechanisms such as environment variables or dedicated secrets-management tools (e.g. AWS Secrets Manager, HashiCorp Vault). API keys should not be shared between different clients or applications. Each client should have its own API key, and the API key should be rotated regularly. API keys should be treated as sensitive information and should not be exposed in public repositories or client-side code. If an API key is compromised, it should be revoked immediately and a new key should be generated.

API keys are not meant to be used by end users. The API is designed to be used by server-side applications, and the API key should not be exposed to end users or client-side code. The API key should be treated as a secret and should not be shared with anyone outside of the development team.

PCI DSS compliance

This API is not in PCI DSS scope. No cardholder data is processed, transmitted or stored by the API; cardholder data is handled exclusively by the payment terminal, which is fully PCI compliant. PCI DSS compliance is handled as is normal for the merchant and the terminals; the usage of this API does not change the scope of the merchant’s PCI DSS environment.

GDPR compliance

The transaction information in the API may contain information that is defined as personal data under the EU General Data Protection Regulation (GDPR).

For example, many of the data fields (such as card_number_merchant) may become personally identifiable information if combined with other available data. If customer identification via an external provider is specifically requested in by the client, the customer_identity field may contain personally identifiable information coming from the external provider. Additionally, the metadata field contains arbitrary key-value pairs provided by the client. It is not permitted to store any personally identifiable information in the metadata field, but if the client does so, the field may also contain personally identifiable information.