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:
Environment | Base address |
---|---|
Live | https://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:
Host | The host name of the API endpoint, which is always api.npay.eu . |
Authorization | The API key used for authentication, see Authentication. |
User-Agent | The user agent of the client, see User agent. |
Content-Length | The length of the request body in bytes. |
Recommended headers are:
Content-Type | The content type of the request body, which is always application/json . |
Accept | The 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-Type | The 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-Length | The 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.