Thestral API
- Version
1.0.0- Base URL
https://api.nerivia.io/v1/thestral/neripay
Guide
Overview
Thestral is Nerivia's REST API for external integrations: one HTTPS host, JSON bodies, and one signing scheme for every product. Nerivia is non-custodial and never holds your funds.
Authentication
Thestral signs every request instead of using bearer tokens, so a captured request can't be replayed or altered. Each API key has two values:
api_key_uuid: the key's public ID, sent with every request.api_key_signing_secret: the secret you sign with. It's shown once, when you create the key, and never sent to Nerivia again.
Send these three headers with every request:
| Header | Value |
|---|---|
X-Nerivia-Key-Id |
Your api_key_uuid. |
X-Nerivia-Timestamp |
Current Unix time, in seconds. |
X-Nerivia-Signature |
Lowercase hex HMAC-SHA256 of the signed message, keyed with your api_key_signing_secret. |
The signed message is the timestamp, a period, and the raw request body, in UTF-8:
<timestamp>.<raw_body>
- Sign the exact bytes you send. Any difference, even whitespace or key order, fails the check.
- A request with no body signs
<timestamp>.alone. - The timestamp must be within five minutes of server time, so keep your clock in sync.
A bad signature, a stale timestamp, a revoked or expired key, or a suspended workspace
returns 401 with a specific error code. Revoke a key or set its expiry in the
Nerivia dashboard.
Workspaces
A workspace owns your settings, API keys, and orders, and nothing crosses between workspaces. Every account has two:
- Live: real payments.
- Test: the same flows on test networks, to rehearse your integration.
Each API key belongs to one workspace, and Nerivia resolves the workspace from that key: no URL or body names it. To switch between test and live, sign with the other workspace's key.
Errors
Every error returns the same shape:
{
"errors": [
{
"code": "neripay_order_amount_out_of_range",
"field": null,
"message": "The order amount is outside the accepted range."
}
]
}code: a stable, machine-readable ID. Branch on it, never on the message.field: the request field that caused the error, ornull.message: a readable explanation in theAccept-Languagelanguage, English or Spanish.
The errors list holds one item per problem. The status code gives the class of
failure: 400 for an invalid request, 401 for an authentication failure, 403 for a
permission failure, and 404 for an object the request references that doesn't exist
in your workspace.
Webhooks
Instead of polling, register an endpoint URL in the dashboard and choose the events it receives. Each subscription gets its own signing secret.
Nerivia POSTs a JSON body to your endpoint, signed the same way as a request:
X-Nerivia-Timestamp and X-Nerivia-Signature, computed over the exact body
delivered, with that subscription's secret. Verify the signature before trusting a
delivery, and reply with any 2xx status to acknowledge it. A non-2xx reply or a
timeout is retried a limited number of times.
Support
Need help? Message us on WhatsApp at +593 963 187 317.
Resource
Asset ledger configs
An asset ledger config is one asset on one ledger, or blockchain network, that your workspace accepts, such as USDT on Polygon, with its receiving address, commission rate, and amount limits. You set configs up in the dashboard, and the API reads them.
GETList asset ledger configs
/workspace-config/asset-ledger-configs/Returns your workspace's active asset ledger configs, newest first: every asset and ledger pair you can create orders in right now. Inactive configs never appear, and no filter can include them.
Filter by asset or ledger with the UUIDs inside an item's asset_ledger object,
never with the config's own uuid. When you combine filters, an item must match
all of them, and a value the API can't parse, such as a malformed UUID, returns an
empty list, never the unfiltered one.
A workspace with no active config gets a 200 with an empty results list: set
one up in the dashboard before creating orders.
Request
Parameters
assetstring (uuid)Only configs for this asset
uuid, on every ledger it's set up on.ledgerstring (uuid)Only configs on this ledger
uuid, for every asset set up on it.limitintegerItems per page. Defaults to
10, which also applies when the value is invalid.offsetintegerItems to skip before the page starts. Defaults to
0, which also applies when the value is invalid.
Responses
200
Your active asset ledger configs.
countintegerItems matching your filters, across every page.
nextstring | null (uri)URL of the next page, or
nullon the last page.previousstring | null (uri)URL of the previous page, or
nullon the first page.resultsarrayThis page's items, newest first.
uuidstringThe config's ID. Not the value you send when creating an order.
is_activebooleanWhether the pair accepts orders. Always
truehere.commission_ratestring (decimal)Nerivia's commission as a percentage:
"2.50"means 2.50%. Each new order keeps the rate in force when it's created.min_amountstring (decimal)The smallest order amount accepted, in the asset's units.
max_amountstring (decimal)The largest order amount accepted, in the asset's units.
receiving_addressstringWhere your share of each settled order is sent. Check it after any dashboard change.
asset_ledgerobjectThe asset and ledger this config is for.
uuidstringID of the asset and ledger pair. Send it as
asset_ledgerwhen you create an order.contract_addressstring | nullThe token's contract address on this ledger, or
nullfor the ledger's native asset. It is not where a customer pays.assetobjectThe asset, such as Tether USD.
uuidstringThe asset's ID.
namestringThe asset's name, such as Tether USD.
codestringThe asset's code, such as
USDT.decimalsintegerDecimal places the asset supports, which caps an order amount's precision.
ledgerobjectThe ledger, or blockchain network, such as Polygon.
uuidstringThe ledger's ID.
namestringThe ledger's name, such as Polygon Amoy.
codestringThe ledger's code, such as
polygon-amoy.address_regexstring | nullThe pattern an address on this ledger must match, or
nullwhen unknown.
{
"count": 1,
"next": null,
"previous": null,
"results": [
{
"uuid": "59a13b02-a4b5-4d42-9d20-19d42be990e9",
"is_active": true,
"commission_rate": "2.50",
"min_amount": "1.000000000000000000",
"max_amount": "10000.000000000000000000",
"receiving_address": "0xf59fedB5D38CD00995AC1D8256f4CfeF46FD58a3",
"asset_ledger": {
"uuid": "37caa3f8-79f3-48a3-bde2-840636f50df2",
"contract_address": "0x0502986e4Be5d3e0A98D68A0fD0f7683F9968843",
"asset": {
"uuid": "46588bb4-600b-4e41-a5c0-95071166f41d",
"name": "Tether USD",
"code": "USDT",
"decimals": 2
},
"ledger": {
"uuid": "85a198fd-4ee6-4f17-bbeb-040ff46896dc",
"name": "Polygon Amoy",
"code": "polygon-amoy",
"address_regex": "^0x[0-9a-fA-F]{40}$"
}
}
}
]
}401
Authentication failed: a bad signature, a stale timestamp, a revoked or expired key, or a suspended workspace.
errorsarrayOne item per problem.
codestringA stable, machine-readable error ID. Branch on it, never on
message.fieldstring | nullThe request field that caused the error, or
null.messagestringA readable explanation in the
Accept-Languagelanguage.
403
The key isn't allowed to perform this action.
errorsarrayOne item per problem.
codestringA stable, machine-readable error ID. Branch on it, never on
message.fieldstring | nullThe request field that caused the error, or
null.messagestringA readable explanation in the
Accept-Languagelanguage.
Resource
Orders
An order asks a customer to pay an amount in one asset on one ledger, to an address dedicated to that order. It moves through these statuses on its own:
| Status | Meaning |
|---|---|
awaiting_payment |
Created, and waiting for the customer's payment until expires_at. |
paid |
Payment confirmed on-chain. Fulfill the order at this point. |
settling |
Nerivia is sending the funds to your receiving address. |
completed |
The funds reached your receiving address. |
expired |
No payment arrived before expires_at. |
settlement_failed |
Sending the funds to your receiving address failed. |
Subscribe to the neripay.order.status.change webhook event to follow these
changes, or poll List orders with the order's uuid as a filter.
GETList orders
/orders/Returns your workspace's orders, newest first.
Filter on created_at with the two date parameters, whose bounds are inclusive;
the field itself isn't returned. When you combine filters, an order must match all
of them, and a value the API can't parse, such as a malformed UUID, returns an
empty list, never the unfiltered one.
List items carry no checkout_url: read it from the creation response.
Request
Parameters
statusstringOnly orders in this status.
uuidstring (uuid)Only the order with this ID.
created_at__gtestring (date-time)Only orders created at or after this moment. A value with no offset is read as UTC.
created_at__ltestring (date-time)Only orders created at or before this moment. A value with no offset is read as UTC.
limitintegerItems per page. Defaults to
10, which also applies when the value is invalid.offsetintegerItems to skip before the page starts. Defaults to
0, which also applies when the value is invalid.
Responses
200
Your orders.
countintegerItems matching your filters, across every page.
nextstring | null (uri)URL of the next page, or
nullon the last page.previousstring | null (uri)URL of the previous page, or
nullon the first page.resultsarrayThis page's items, newest first.
uuidstringThe order's ID. Store it to find the order later with the
uuidfilter.amountstring (decimal)What the customer pays, in the asset's units. Nerivia's commission comes out of it, not on top of it.
commission_ratestring (decimal)Nerivia's commission as a percentage, fixed when the order was created.
statusstringThe order's current status, such as
awaiting_paymentorpaid.expires_atstring (date-time)When the order stops accepting payment. Drive your countdown from it rather than assuming a fixed window.
contract_addressstringThe order's own settlement address, where the customer pays. Use it for this order only.
asset_ledgerobjectThe asset and ledger the order is priced and paid in.
uuidstringID of the asset and ledger pair.
assetobjectThe asset, such as Tether USD.
uuidstringThe asset's ID.
namestringThe asset's name, such as Tether USD.
codestringThe asset's code, such as
USDT.decimalsintegerDecimal places the asset supports, which caps an order amount's precision.
ledgerobjectThe ledger, or blockchain network, such as Polygon.
uuidstringThe ledger's ID.
namestringThe ledger's name, such as Polygon Amoy.
codestringThe ledger's code, such as
polygon-amoy.address_regexstring | nullThe pattern an address on this ledger must match, or
nullwhen unknown.
contract_addressstring | nullThe token's contract address on this ledger, or
nullfor the ledger's native asset. It is not where a customer pays.
external_referencestring | nullYour own ID for the order, or
nullfor an order created in the dashboard.
{
"count": 1,
"next": null,
"previous": null,
"results": [
{
"uuid": "e75f7105-d903-4d69-b1ea-17bbfbd7f777",
"amount": "10.000000000000000000",
"commission_rate": "2.50",
"status": "settlement_failed",
"expires_at": "2026-09-11T05:44:18.988829Z",
"contract_address": "0x0146a0AA378044be8a59e2aee97B00Cac073539C",
"asset_ledger": {
"uuid": "37caa3f8-79f3-48a3-bde2-840636f50df2",
"asset": {
"uuid": "46588bb4-600b-4e41-a5c0-95071166f41d",
"name": "Tether USD",
"code": "USDT",
"decimals": 2
},
"ledger": {
"uuid": "85a198fd-4ee6-4f17-bbeb-040ff46896dc",
"name": "Polygon Amoy",
"code": "polygon-amoy",
"address_regex": "^0x[0-9a-fA-F]{40}$"
},
"contract_address": "0x0502986e4Be5d3e0A98D68A0fD0f7683F9968843"
},
"external_reference": null
}
]
}401
Authentication failed: a bad signature, a stale timestamp, a revoked or expired key, or a suspended workspace.
errorsarrayOne item per problem.
codestringA stable, machine-readable error ID. Branch on it, never on
message.fieldstring | nullThe request field that caused the error, or
null.messagestringA readable explanation in the
Accept-Languagelanguage.
403
The key isn't allowed to perform this action.
errorsarrayOne item per problem.
codestringA stable, machine-readable error ID. Branch on it, never on
message.fieldstring | nullThe request field that caused the error, or
null.messagestringA readable explanation in the
Accept-Languagelanguage.
POSTCreate order
/orders/Creates a payment order for an amount in one asset on one ledger. Nerivia gives
the order its own settlement address, starts watching it, and returns the order
with a checkout_url: a hosted payment page you can send your customer to
instead of building your own.
A new order starts in awaiting_payment, and its payment window opens as soon as
this call succeeds.
After creating
Show your customer contract_address and amount, or send them to
checkout_url. Nerivia confirms the payment, marks the order paid, sends the
funds to your receiving address, and marks it completed, with no further call
from you.
Fulfill on paid, not completed: paid means your customer has paid, while
completed only means the funds have reached you.
Request
Body
The order to create. All three fields are required, and any other field is ignored.
asset_ledgerstringRequiredThe
asset_ledger.uuidof an active asset ledger config in your key's workspace.amountstring (decimal)RequiredWhat the customer pays, as a decimal string in the asset's units, such as
"25.50". It must fall within the config'smin_amountandmax_amount, carry no more decimals than the asset supports, and hold at most 36 digits and 18 decimals.external_referencestringRequiredYour own ID for the order, such as an invoice number. Up to 36 characters, and unique in your workspace, so a retried creation never duplicates a charge.
{
"asset_ledger": "37caa3f8-79f3-48a3-bde2-840636f50df2",
"amount": "100.00",
"external_reference": "AAA-000-000-003"
}Responses
201
The created order.
uuidstringThe order's ID. Store it to find the order later with the
uuidfilter.amountstring (decimal)What the customer pays, in the asset's units. Nerivia's commission comes out of it, not on top of it.
commission_ratestring (decimal)Nerivia's commission as a percentage, fixed when the order was created.
statusstringThe order's current status, such as
awaiting_paymentorpaid.expires_atstring (date-time)When the order stops accepting payment. Drive your countdown from it rather than assuming a fixed window.
contract_addressstringThe order's own settlement address, where the customer pays. Use it for this order only.
asset_ledgerobjectThe asset and ledger the order is priced and paid in.
uuidstringID of the asset and ledger pair.
assetobjectThe asset, such as Tether USD.
uuidstringThe asset's ID.
namestringThe asset's name, such as Tether USD.
codestringThe asset's code, such as
USDT.decimalsintegerDecimal places the asset supports, which caps an order amount's precision.
ledgerobjectThe ledger, or blockchain network, such as Polygon.
uuidstringThe ledger's ID.
namestringThe ledger's name, such as Polygon Amoy.
codestringThe ledger's code, such as
polygon-amoy.address_regexstring | nullThe pattern an address on this ledger must match, or
nullwhen unknown.
contract_addressstring | nullThe token's contract address on this ledger, or
nullfor the ledger's native asset. It is not where a customer pays.
external_referencestringYour own ID for the order, as you sent it.
checkout_urlstring (uri)A Nerivia-hosted payment page for this order alone, safe to share with your customer.
{
"uuid": "9ba6ade8-416a-495b-8cf6-6eeac215ab08",
"amount": "100.000000000000000000",
"commission_rate": "2.50",
"status": "awaiting_payment",
"expires_at": "2026-09-11T21:45:03.204571Z",
"contract_address": "0x23BDBDbAD94Bb5CD2D8AD44645523D4A5Bd1aeD9",
"asset_ledger": {
"uuid": "37caa3f8-79f3-48a3-bde2-840636f50df2",
"asset": {
"uuid": "46588bb4-600b-4e41-a5c0-95071166f41d",
"name": "Tether USD",
"code": "USDT",
"decimals": 2
},
"ledger": {
"uuid": "85a198fd-4ee6-4f17-bbeb-040ff46896dc",
"name": "Polygon Amoy",
"code": "polygon-amoy",
"address_regex": "^0x[0-9a-fA-F]{40}$"
},
"contract_address": "0x0502986e4Be5d3e0A98D68A0fD0f7683F9968843"
},
"external_reference": "AAA-000-000-001",
"checkout_url": "https://app.nerivia.io/neripay/checkout/9ba6ade8-416a-495b-8cf6-6eeac215ab08"
}400
The request is invalid. errors lists every problem.
errorsarrayOne item per problem.
codestringA stable, machine-readable error ID. Branch on it, never on
message.fieldstring | nullThe request field that caused the error, or
null.messagestringA readable explanation in the
Accept-Languagelanguage.
{
"errors": [
{
"code": "neripay_order_amount_out_of_range",
"field": null,
"message": "The order amount is outside the accepted range."
}
]
}401
Authentication failed: a bad signature, a stale timestamp, a revoked or expired key, or a suspended workspace.
errorsarrayOne item per problem.
codestringA stable, machine-readable error ID. Branch on it, never on
message.fieldstring | nullThe request field that caused the error, or
null.messagestringA readable explanation in the
Accept-Languagelanguage.
403
The key isn't allowed to perform this action.
errorsarrayOne item per problem.
codestringA stable, machine-readable error ID. Branch on it, never on
message.fieldstring | nullThe request field that caused the error, or
null.messagestringA readable explanation in the
Accept-Languagelanguage.
404
An object the request references doesn't exist in your workspace.
errorsarrayOne item per problem.
codestringA stable, machine-readable error ID. Branch on it, never on
message.fieldstring | nullThe request field that caused the error, or
null.messagestringA readable explanation in the
Accept-Languagelanguage.