Orders API
This document covers the Orders endpoints used to create and manage crypto invoice orders (deposit address + payment URI + QR + lifecycle status), including refunds for cancelled orders. It includes:- List orders (
GET /api/orders) - Create an invoice order (
POST /api/orders) - Delete an order by reference (
DELETE /api/orders?reference=...) - Fetch a single order + payment notifications (
GET /api/orders/:orderId) - Update an order status (
PATCH /api/orders/:orderId) — including webhook dispatch + forward funds - Refund a cancelled order (
POST /api/orders/:orderId/refund)
These endpoints are protected, you must be authenticated.
Base URL
All examples use:https://www.niftipay.com
Authentication
These endpoints support two authentication methods (depending on your integration setup):1) API Key (recommended for server-to-server integrations)
Send your API key in thex-api-key header.
2) Session cookie (browser / dashboard usage)
If you are authenticated via the dashboard..For browser calls, you usually don’t need to add headers manually — the cookie is sent automatically.
Order statuses
Crypto invoice orders use these statuses:pending— waiting for payment (default on creation)paid— fully paid (or manually marked paid)cancelled— cancelled (manual or automated)refunded— refund executed and wallet freed
Chain + asset rules
network and asset work like this:
networkis the chain where the deposit address lives:BTC | LTC | ETH | SOL | XRPassetis what the customer sends:- Native coin (e.g.
BTC,ETH,SOL) - Or an ERC‑20 token on Ethereum for supported tokens (e.g.
USDT,USDC) only whennetworkisETH
- Native coin (e.g.
amount— base invoice amountnetworkFees— any network fee the customer must add (nullable)totalToSend—amount + networkFees(or justamountif fees are null)
Payment URI + QR URL
For convenience, orders return:paymentUri— a standard chain-specific URI (bitcoin:, ethereum:, solana:, ripple:, etc.)qrUrl— a QR image URL generated frompaymentUri
- BTC:
bitcoin:<address>?amount=<totalToSend> - LTC:
litecoin:<address>?amount=<totalToSend> - SOL:
solana:<address>?amount=<totalToSend> - XRP:
ripple:<address>?amount=<totalToSend>&dt=<destinationTag?> - ETH native:
ethereum:<address>?amount=<totalToSend> - ETH token:
ethereum:<address>?contract=<tokenContract>&amount=<totalToSend>
List Orders
Endpoint
GET /api/orders
Returns a list of orders for the authenticated user, newest first.
Query parameters
| Name | Type | Description |
|---|---|---|
status | string | Filter by order status (e.g. pending, paid, cancelled, refunded). |
reference | string | Filters orders where reference starts with this value. |
search | string | Searches in firstName, lastName, and email. |
Example request: list all orders
Example request: filter by status
Example request: filter by reference prefix
Example request: search by customer name/email
Example response
Create Order (Crypto Invoice)
Endpoint
POST /api/orders
Creates a crypto invoice order:
- reserves a deposit address
- computes the amount in crypto/token based on your fiat amount
- returns
paymentUriandqrUrlfor checkout UX - starts in
pending
Important behavior
- Payment method gating: if crypto invoices are disabled for your user, this endpoint returns
403. - Reference handling:
- If you provide a
reference, it must be unique for your user. - If you don’t provide a
reference, one is generated (UUID). - If you provide a
referencethat conflicts with a fiat ordermerchantReference, it returns409.
- If you provide a
- Idempotency (optional):
- If you pass
Idempotency-Key(orx-idempotency-key) and you retry with the samereference, the API can return the existing order instead of failing.
- If you pass
- Replace cancelled (optional):
- Add
?replaceCancelled=1to allow re-using a reference only when the prior order iscancelled(and the old order is safely deletable).
- Add
Request body
| Field | Type | Required | Notes |
|---|---|---|---|
network | string | ✅ | One of BTC, LTC, ETH, SOL, XRP. |
asset | string | ✅ | Asset symbol. For USDT/USDC you must use network=ETH. |
amount | number | ✅ | Fiat invoice amount. |
currency | string | ❌ | Defaults to EUR. |
firstName | string | ✅ | Customer name. |
lastName | string | ✅ | Customer name. |
email | string | ✅ | Used for receipts and internal processing. |
reference | string | ❌ | If omitted, a UUID is generated. Must be unique for this user. |
merchantId | string | ❌ | Your internal merchant identifier (if you use multi-merchant). |
pct | string/number | ❌ | Passed through to invoice creation logic. |
Example: create an ETH USDT invoice
Example response (201)
Idempotency example
If you’re creating orders from a backend and might retry on network failure, do:- Include a stable
reference - Include an idempotency key
reference already exists and idempotency headers are present, the API returns the existing order (200) instead of a 409 conflict.
Replace cancelled example
If you want to re-use a reference that exists only because the prior invoice was cancelled:Delete Order (by reference)
Endpoint
DELETE /api/orders?reference=<reference>
Deletes a crypto invoice order by reference, if it is safe to delete.
When deletion is blocked
The API checks wallet balances and blocks deletion if the deposit wallet has any funds. It also deletes:- per-order subscriptions (best effort)
paymentNotificationrows- frees the deposit wallet (
isAssigned=false) - deletes the order row
Example request
Success response
204 No Content
Error responses
Missing referenceFetch Order (by ID) + Payments
Endpoint
GET /api/orders/:orderId
Returns:
- the order
- payment notifications (
payments) - a computed
runningTotal(sum of payment notifications) - a computed
remainingon-chain balance (best effort) - a normalized
feesobject (snapshot)
Example request
Example response
Notes:
paymentsare returned newest first.runningTotalis the sum of positivepaymentNotification.amount.remainingis a best-effort on-chain balance read for the deposit wallet.
Update Order Status (manual)
Endpoint
PATCH /api/orders/:orderId
Allows manual transitions between statuses.
Valid statuses:
pendingpaidcancelled
Request body
| Field | Type | Required | Notes |
|---|---|---|---|
status | string | ✅ | Must be one of pending, paid, cancelled. |
Important behavior
Idempotency for status changes
If you PATCH the same status the order already has, the API returns success and a warning, and performs no side effects:- no webhook dispatch
- no forward funds
- no subscription changes
Side effects by status
If status = paid
- sets
paidAt = now - sends a merchant webhook event
"paid"(best effort) - attempts to forward funds for non-XRP invoices (best effort)
- attempts to delete the per-order subscription (best effort)
paid and you get a warning:
If status = pending
- clears
paidAtandrefundedAt - sets a new
expiresAt = now + 12 hours - ensures deposit wallet is assigned (
isAssigned=true) - attempts to recreate a subscription (best effort, non-XRP)
- sends a merchant webhook event
"pending"(best effort)
If status = cancelled
- attempts to delete subscription (non-XRP)
- checks balance, and if balance is
"0"it frees the wallet (isAssigned=false) - sends a merchant webhook event
"cancelled"(best effort)
Example: mark an order as paid
Example response
Example: revert an order to pending
Example: cancel an order
Refunds
Refunds are performed from the deposit wallet back to a user-providedrefundAddress.
Refunds are only allowed when:
- the order exists and belongs to you
- the order is cancelled
- there is a non-zero balance to refund
- order status becomes
refunded refundedAtis set- the deposit wallet is freed (
isAssigned=false) - a merchant webhook event
"refunded"is sent (best effort)
Refund an Order
Endpoint
POST /api/orders/:orderId/refund
Request body
| Field | Type | Required | Notes |
|---|---|---|---|
refundAddress | string | ✅ | Destination address/account to receive the refund. |
Example: refund a cancelled ETH order
Example success response
The returnedorderis the order row after it was updated torefunded.
Refund rules by chain/asset (high level)
The refund implementation attempts to send a net amount (after accounting for chain rules/fees) from the deposit wallet:- ERC‑20 on ETH (USDT/USDC):
- token amount is refunded in full
- ETH is required in the deposit wallet to pay gas
- the system may top-up ETH gas automatically (internal) to enable the refund
- Native ETH:
- refunds
balance - gasFee(fails if too small after fees)
- refunds
- BTC / LTC:
- refunds
balance - estimatedFee(and may broadcast via txData if needed) - very small amounts may not refund if below dust/fee thresholds
- refunds
- SOL:
- keeps a small reserve (rent + fee) and refunds the remainder
- XRP:
- keeps the account reserve (e.g. 10 XRP) and refunds the remainder
Error responses
Missing refund address (400)
Not found (404)
Order is not cancelled (400)
Nothing to refund (400)
Refund failed (502)
Webhooks (merchant notifications)
When you manually transition status viaPATCH /api/orders/:orderId, the API attempts to notify your webhook endpoint (best effort) with:
"pending""paid""cancelled"
POST /api/orders/:orderId/refund, the API also sends:
"refunded"
id,reference,merchantIdrefundedAtamount(the actual net amount sent on-chain)chain,asset
If webhook dispatch fails, the API still completes the operation and returns success.
Error codes summary
These endpoints commonly return:400— invalid payload, invalid status, missing required query param, nothing to refund403— payment method disabled (crypto invoices disabled)404— order not found409— reference conflict, wallet has balance, no available addresses502— refund broadcast failed (upstream/provider errors)