Welcome to the Nexo Payment Gateway API documentation. This API is designed specifically for merchants who have been successfully onboarded onto the Nexo platform. To begin the onboarding process or to access more information, please visit Nexo Onboarding.
The Nexo Payment Gateway API adheres to RESTful principles, ensuring a seamless integration experience. Our API provides predictable, resource-oriented URLs, supports form-encoded request bodies, and returns JSON-encoded responses. It employs standard HTTP response codes, authentication methods, and verbs to facilitate clear and reliable communication between your services and our platform.
Secure authentication is crucial for protecting your data. In the Nexo Payment Gateway API, authentication is handled using an API key. This API key must be included in the header of every request to ensure that the interaction is authorized.
To authenticate API requests, include your API key in the x-api-key header.
This header is required for all API calls to verify that the requestor has proper authorization.
Failure to include the API key, or using an incorrect or revoked key, will result in a denial of access.
Currently, the Nexo Payment Gateway API does not provide support for a sandbox or test environment. This means that all API calls made will be executed in the live production environment.
We recognize the importance of testing functionalities in a controlled setting without affecting real transactions or data. Plans to introduce a sandbox environment are under consideration, and we will inform all integrated partners once it becomes available.
We understand the critical role a sandbox environment plays in effective API integration and are committed to enhancing the experience of our merchant clients. Thank you for your patience, updates will be provided as soon as they are available.
To ensure the security and integrity of data, all requests to the Nexo Payment Gateway API must be sent over HTTPS. This protocol encrypts the data exchanged between the client and the server, providing a secure channel to prevent interception and unauthorized access during transmission.
Currently, the Nexo API does not support Virtual Private Cloud (VPC) tunnelling. If VPC tunnelling is an essential feature for your integration, we encourage you to reach out to our technical support team. We are always looking to evolve our services in ways that best meet the needs of our merchants, and your feedback is invaluable in guiding our feature updates.
Currently Nexo supports the following list of networks and associated assets for the Payment Gateway, with more planned to be added in the future.
| Network | Description | Assets |
|---|---|---|
| bitcoin | This is the network of Bitcoin. | BTC |
| ethereum | This is the mainnet network of Ethereum. | ETH, PAXG, USDT, USDC, AAVE |
| polygon | This is the mainnet network of Polygon. | POL, ETH, USDT, USDC, AAVE |
| bsc | This is the mainnet network of Binance Smart Chain. | BNB |
The Nexo Payment Gateway provides notifications via WebHooks. This feature allows merchants to receive updates about deposits and other significant events directly through HTTPS callbacks, facilitating an automated and efficient response system.
The callback URL for the WebHooks must be configured through the Payment Gateway dashboard, available at Nexo Onboarding. This URL, which is specified during the onboarding process, can be updated at any time as needed:
Below is the structure of the data that Nexo sends to the configured WebHook URL, encapsulated in two primary components - the event type and the data payload.
| Property | Type | Description |
|---|---|---|
| eventType | string | Specifies the type of WebHook event. For more information, see WebHook Types. |
| data | object | Contains the payload of the WebHook, detailed in the WebHookPayload structure. |
| Property | Type | Description |
|---|---|---|
| transactionId | string | Unique identifier of the transaction within Nexo's system. |
| transactionHash | string | Blockchain-specific hash associated with the transaction, if applicable. |
| referenceId | string | Unique identifier associated with the address involved in the transaction. |
| depositAddress | string | Blockchain address targeted by the deposit operation. |
| asset | string | Identifier representing the type of asset transferred, e.g "ETH". |
| amount | string | Amount of the asset transferred, presented with a comma separated value - e.g. “0.2365” ETH. |
| timestamp | string | Timestamp of when the deposit was detected by Nexo represented in ISO format (UTC). |
| fiat | object | The fiat information of the transaction. Optional: omitted when no fiat value is available. |
| usd | object | The fiat information for the said currency. |
| value | string | The calculated value of the transaction in said currency. - “27.76630856” USD |
| course | string | The course at the time of the transaction. - “13.88315428” ETH/USD |
| status | string | Confirmation state of the deposit: "confirmed" for PG_DEPOSIT, "unconfirmed" for PG_UNCONFIRMED_DEPOSIT. Always present, and always the last field of data (after fiat). |
{
"eventType": "PG_DEPOSIT",
"data": {
"transactionId": "bfd5f586-a706-4d26-8ca1-ea91dbecea2b",
"transactionHash": "0x02fc56662f353ebc4300d21690464e0e8afb77edc2329124b9f8b7e341f43896",
"referenceId": "6bf84342-606b-42c4-a3af-d738002e7f18",
"depositAddress": "0xdac17f958d2ee523a2206206994597c13d831ec7",
"asset": "ETH",
"amount": "0.00042142",
"timestamp": "2020-07-28T13:53:24.674Z",
"fiat": {
"usd": {
"value": "27.76630856",
"course": "13.88315428"
}
},
"status": "confirmed"
}
}
| Type | Description |
|---|---|
| PG_DEPOSIT | A deposit of assets has been done to an address which belongs to the merchant and is confirmed. data.status is "confirmed". This is the only deposit event you should credit. |
| PG_UNCONFIRMED_DEPOSIT | A deposit of assets to an address which belongs to the merchant has been detected, but is not yet confirmed. data.status is "unconfirmed". Informational only, never credit on this event. See Unconfirmed Deposits. |
A PG_UNCONFIRMED_DEPOSIT event is sent to the same webhook URL as PG_DEPOSIT, as soon as an incoming deposit is detected and before it is confirmed. It has the same structure as PG_DEPOSIT, with data.status set to "unconfirmed". It lets you show a pending deposit to your customer early.
{
"eventType": "PG_UNCONFIRMED_DEPOSIT",
"data": {
"transactionId": "bfd5f586-a706-4d26-8ca1-ea91dbecea2b",
"transactionHash": "0x02fc56662f353ebc4300d21690464e0e8afb77edc2329124b9f8b7e341f43896",
"referenceId": "6bf84342-606b-42c4-a3af-d738002e7f18",
"depositAddress": "0xdac17f958d2ee523a2206206994597c13d831ec7",
"asset": "ETH",
"amount": "0.00042142",
"timestamp": "2020-07-28T13:53:24.674Z",
"fiat": {
"usd": {
"value": "27.76630856",
"course": "13.88315428"
}
},
"status": "unconfirmed"
}
}
Rules for handling unconfirmed deposits:
PG_UNCONFIRMED_DEPOSIT. The event is informational only. Credit your customer only when you receive PG_DEPOSIT (data.status is "confirmed").PG_UNCONFIRMED_DEPOSIT may arrive after the PG_DEPOSIT of the same deposit. It may also never be followed by a PG_DEPOSIT. Ignore an unconfirmed event for a deposit you have already received as confirmed.transactionId than the unconfirmed one. Correlate the two by transactionHash and depositAddress, not by transactionId alone.eventType, transactionId). Do not deduplicate on the signature, because the signature is calculated per event (see WebHook Integrity).PG_UNCONFIRMED_DEPOSIT before PG_DEPOSIT.PG_UNCONFIRMED_DEPOSIT webhooks are enabled by default for newly onboarded merchants. They are controlled by the sendUnconfirmedDepositWebhooks setting of your merchant account.data.status is a signed field of every deposit event. It is always the last field of data, after fiat. Make sure your signature verification includes it (see WebHook Integrity).To ensure the integrity and security of WebHook events, Nexo incorporates an X-SIGNATURE header with every event.
This cryptographic signature allows you, as the merchant, to verify that the event has not been altered or tampered with during transmission.
The signature is calculated over the whole event, so it is specific to a single event: a redelivery of the same event carries the same signature, while a different event has a different signature. A single deposit can produce more than one event, for example PG_UNCONFIRMED_DEPOSIT and later PG_DEPOSIT, and these have different signatures. For that reason do not use the signature as the only mechanism to deduplicate deposits. Deduplicate on the pair (eventType, transactionId) instead.
When you re-serialise the event to verify the signature, keep the field order exactly as documented, and omit fields that are absent from the received event (for example fiat). Do not add null values for them.
The integrity verification is performed by recalculating the SHA256 hash of the event using a Shared Secret, which is a unique key generated for each merchant upon onboarding. This process ensures that the event you receive is exactly what was sent by Nexo without any alterations.
class Event {
constructor({ eventType, data }) {
this.eventType = eventType;
this.data = new Transaction(data);
}
}
class Transaction {
constructor({
transactionId,
transactionHash,
referenceId,
depositAddress,
asset,
amount,
timestamp,
fiat,
status
}) {
this.transactionId = transactionId;
this.transactionHash = transactionHash;
this.referenceId = referenceId;
this.depositAddress = depositAddress;
this.asset = asset;
this.amount = amount;
this.timestamp = timestamp;
// fiat is optional and may be absent. Do not set the property when it is missing,
// so that JSON.stringify omits it, exactly like the original event.
if (fiat) {
this.fiat = new Fiat(fiat);
}
// status must stay after fiat, the order of the fields is part of the signature.
this.status = status;
}
}
class Fiat {
constructor({ usd }) {
this.usd = new FiatValue(usd);
}
}
class FiatValue {
constructor({ value, course }) {
this.value = value;
this.course = course;
}
}
const raw = {
eventType: "PG_DEPOSIT",
data: {
transactionId: "NXT7Z2qAw4SFZpQaOyjZjak9B",
transactionHash:
"0x02fc56662f353ebc4300d21690464e0e8afb77edc2329124b9f8b7e341f43896",
depositAddress: "0xdac17f958d2ee523a2206206994597c13d831ec7",
referenceId: "6bf84342-606b-42c4-a3af-d738002e7f18",
amount: "0.00042142",
asset: "ETH",
timestamp: "2020-07-28T13:53:24.674Z",
fiat: {
usd: {
value: "27.76630856",
course: "13.88315428"
}
},
status: "confirmed"
},
};
const event = new Event(raw);
const secret = "YourSharedSecretFromNexo";
const data = `${JSON.stringify(event)}/${secret}`;
const computedSignature = `0x${crypto
.createHash("sha256")
.update(data)
.digest("hex")}`;
let receivedSignature = receivedHeaders["X-SIGNATURE"];
if (receivedSignature === computedSignature) {
console.log("Signature verified successfully.");
} else {
console.log("Warning: Signature mismatch detected!");
}
Define an Event class that holds the structure of the event.
package org.example;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class Event {
private String eventType;
private Transaction data;
public Event(String eventType, Transaction data) {
this.eventType = eventType;
this.data = data;
}
}
Define a CurrencyData class that holds the fiat currency data of the event.
package org.example;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class CurrencyData {
private String value;
private String course;
public CurrencyData(String value, String course) {
this.value = value;
this.course = course;
}
}
Define a FiatData class that holds the fiat currency.
package org.example;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class FiatData {
private CurrencyData usd;
public FiatData(CurrencyData usd) {
this.usd = usd;
}
}
Define a Transaction class that holds the payload of the event.
package org.example;
import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.Getter;
import lombok.Setter;
import java.util.Map;
@Getter
@Setter
// fiat is optional: a null value must be omitted from the serialized event, like in the original event
@JsonInclude(JsonInclude.Include.NON_NULL)
public class Transaction {
private String transactionId;
private String transactionHash;
private String referenceId;
private String depositAddress;
private String asset;
private String amount;
private String timestamp;
private FiatData fiat;
// status must stay after fiat, the order of the fields is part of the signature
private String status;
public Transaction(String transactionId, String transactionHash, String referenceId,
String depositAddress, String asset, String amount, String timestamp, FiatData fiat, String status) {
this.transactionId = transactionId;
this.transactionHash = transactionHash;
this.referenceId = referenceId;
this.depositAddress = depositAddress;
this.asset = asset;
this.amount = amount;
this.timestamp = timestamp;
this.fiat = fiat;
this.status = status;
}
}
Signature creation.
package org.example;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.HexFormat;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
public class Main {
public static void main(String[] args) {
CurrencyData usdCurrencyData = new CurrencyData("27.76630856", "13.88315428");
FiatData fiatData = new FiatData(usdCurrencyData);
Transaction transaction = new Transaction(
"NXT7Z2qAw4SFZpQaOyjZjak9B",
"0x02fc56662f353ebc4300d21690464e0e8afb77edc2329124b9f8b7e341f43896",
"6bf84342-606b-42c4-a3af-d738002e7f18",
"0xdac17f958d2ee523a2206206994597c13d831ec7",
"ETH",
"0.00042142",
"2020-07-28T13:53:24.674Z",
fiatData,
"confirmed"
);
Event event = new Event("PG_DEPOSIT", transaction);
String secret = "9a994da63491f3ed5abcb948f9d1976dac831742";
try {
String serializedEvent = new ObjectMapper().writeValueAsString(event);
String data = serializedEvent + "/" + secret;
String signature = "0x" + sha256Hex(data);
System.out.println(signature); // 0x60303773ccbd594d70225c84e9e7d9844fdbcf57f3b0781fb52cc86ec2bbb6d7
} catch (JsonProcessingException e) {
System.out.println("Error serializing the event.");
} catch (NoSuchAlgorithmException e) {
System.out.println("SHA-256 algorithm not available.");
}
}
public static String sha256Hex(String data) throws NoSuchAlgorithmException {
MessageDigest digest = MessageDigest.getInstance("SHA-256");
byte[] hash = digest.digest(data.getBytes(StandardCharsets.UTF_8));
return HexFormat.of().formatHex(hash);
}
}
Nexo utilizes standard HTTP response codes to signify the success or failure of an API request. Here is a general guideline:
| Code | Status | Description |
|---|---|---|
| 200 | OK | The request has been processed successfully. |
| 400 | Bad Request | The request could not be processed due to incorrect input. |
| 401 | Unauthorized | Authentication failed, likely due to an invalid API key. |
| 500, 502, 503, 504 | Server Errors | An issue occurred on Nexo's side. (These instances are uncommon.) |
This section explains how to handle 4xx errors based on the type field. To handle 5xx errors, please use the status field, as this is a Server Error.
Errors returned by our API are accompanied by a unique identifier in the type field of the error response.
This identifier is a URL linking to a specific section of our documentation that describes potential reasons for the error.
While the full URL in the type field can be used directly, we recommend utilizing the last segment of this URL as a concise error identifier for easier handling.
When the API encounters a BadRequest, it means that the request could not be processed due to incorrect input. This typically occurs when the passed parameters violate the constraints described for a specific endpoint.
| Identifier | Code | Title | Description |
|---|---|---|---|
bad_request_error |
400 | Bad Request | The request could not be processed due to incorrect input. |
Recommendation: Review and assess the parameters you`ve passed, keeping in mind the specific endpoint restrictions outlined in the Nexo Payment API documentation. Ensure that all inputs meet the required conditions for successful API calls.
When the API returns a MerchantNotOnboarded error, it means that the merchant account is either not fully set up or the requested asset is not supported. This error often indicates that the merchant has not completed all necessary onboarding steps or that the asset involved is not available for deposit address creation through the API.
| Identifier | Code | Title | Description |
|---|---|---|---|
merchant_not_onboarded |
400 | Bad Request | Merchant not onboarded, or asset not supported. |
Recommendation: Verify that the merchant account is fully onboarded and check the list of supported assets. Ensure that all required setup processes are complete and that the assets you are attempting to create address with are supported by the API.
When the API encounters an InternalServerError, it indicates a problem with the API's servers that prevents it from fulfilling the request. This error is a general catch-all response for when the server encounters an unexpected condition that prevents it from completing the request.
| Identifier | Code | Title | Description |
|---|---|---|---|
internal_server_error |
500 | Internal Server Error | Internal backend server error. |
Recommendation: This is usually a temporary issue. Retry your request after a few minutes. If the problem persists, contact our support team for assistance and provide them with the relevant details of your request.
Welcome to the Release Notes section of our API documentation! This space is dedicated to providing you with detailed and up-to-date information about the latest versions of our API. Each set of release notes is designed to help you understand what has changed, how these changes may impact your applications, and how you can take advantage of new features and improvements.
We recommend reviewing the release notes with each new version of our API to stay informed about changes that could impact your projects.
Thank you for choosing our API, and happy coding!
This initial release establishes the foundation of our API with a focus on reliability, security, and ease of use. We provide you with robust tools and flexible endpoints to manage data, integrate complex functionalities, and enhance user experiences. As this is the first version, we are especially eager to hear from you. Your feedback is invaluable and will be directly used to shape future versions of this API.
What You Will Find in This Documentation
Your input is crucial to us. If you have suggestions, experience issues, or just want to share how you're using the API, please reach out to us. We are committed to continuously improving our services to meet your needs and expectations.
Operations related to the assets supported by the API, including listing available assets and providing detailed information about each asset's characteristics.
Returns all assets available for the merchant through the Nexo Payment Gateway. This includes metadata such as symbol, blockchain, and other relevant properties.
Array of objects (Asset) A list of supported assets. |
{- "assets": [
- {
- "asset": "ETH",
- "blockchain": "ethereum",
- "address": "",
- "decimals": 18
}, - {
- "asset": "USDT_ETH",
- "blockchain": "ethereum",
- "address": "0xdac17f958d2ee523a2206206994597c13d831ec7",
- "decimals": 6
}, - {
- "asset": "BTC",
- "blockchain": "bitcoin",
- "address": "",
- "decimals": 8
}
]
} Operations concerning the creation and management of deposit addresses. A
deposit address provides a blockchain address, tailored to facilitate
distinct transactions per entity via a unique referenceId.
Workflow:
referenceId.referenceId across blockchains
of the same family. This means that repeating a referenceId in a request
within the same blockchain family will return the previously generated
address.Benefits:
Restrictions:
referenceId) is
versatile, but some basic constraints are placed: Creates a deposit address associated with the provided referenceId.
This address facilitates the reception of cryptocurrency deposits.
If a deposit address already exists for the same referenceId in the same
blockchain family, the existing address is returned to prevent duplicate
address generation. This ensures efficient utilization of addresses within
the same blockchain type, where the same deposit address may be used for
multiple assets.
Notifications for deposits to this address will be sent to the previously
configured WebHook URL, with details identifying the specific transaction
and referenceId.
Use this endpoint to streamline the management of deposit addresses, reducing the need for multiple address generations, and enhancing tracking capabilities.
| referenceId required | string [ 0 .. 50 ] characters ^[a-zA-Z0-9\-_]+$ A unique identifier provided by the merchant used to reference the deposit address. |
| asset required | string^[a-zA-Z0-9\-_]+$ The type of cryptocurrency asset for which the deposit address is being created. |
{- "referenceId": "merchant_reference_ID-1",
- "asset": "BTC"
}{- "referenceId": "merchant_reference_ID-1",
- "asset": "BTC",
- "blockchain": "bitcoin",
- "address": "bc1qct9wk67qup7wz8p772r700prd74rhngnml0jcu"
}