Orders
Create an order from a quote, then track any order to settlement.
An order moves one payment from the customer to its destination. An onramp collects naira and delivers a stablecoin. An offramp collects a stablecoin and delivers naira. Orders come from two sources.
| Source | How the order starts | First event |
|---|---|---|
| You create it from a quote | POST /v1/onramp/orders or POST /v1/offramp/orders | order.awaiting_payment |
| A payment to a collection | Pulse creates the order when the payment arrives: an offramp for a stablecoin deposit, an onramp for a naira transfer | order.payment_received, or order.held |
This page covers orders that you create. You read and track both kinds the same way. See Order status and Track an order.
Create an order
Create each order from an unused quote in the same direction. The quote
sets the rate, the stablecoin, and the network. The network is the blockchain that
the stablecoin moves on, such as POLYGON.
Every order takes these fields:
reference: your ID for the order, up to 128 characters. It must be unique across onramp and offramp orders, and it makes the request safe to retry. See Idempotency.quote_id: the quote.payer, on an onramp only: the customer who sends the naira.- The payout:
destinationon an onramp,beneficiaryon an offramp, orrecipient_idfor a payout account.
payer and beneficiary carry the customer's nin and bvn, 11 digits each, for
regulatory purposes. Pulse encrypts both at rest and omits them from API responses.
Create an onramp order
An onramp order gives the customer a bank account for one naira transfer. Pulse then sends the stablecoin to a wallet address: the customer's, or a payout account.
curl https://engine-api.pilot.pulseshiga.io/v1/onramp/orders \
-H "X-API-Key: <your key>" \
-H "Content-Type: application/json" \
-d '{
"reference": "partner-order-88213",
"quote_id": "0198e2c1-…",
"payer": {
"name": "Ada Obi",
"email": "ada@example.com",
"nin": "12345678901",
"bvn": "22345678901"
},
"destination": { "address": "0xAbC…" }
}'The response has status awaiting_payment. Show the customer funding_account. See
Fund the order.
{
"status": true,
"data": {
"id": "0198e2d0-…",
"reference": "partner-order-88213",
"type": "onramp",
"status": "awaiting_payment",
"rate": "1538.46",
"source": { "currency": "NGN", "amount": "150000" },
"destination": { "currency": "USDT", "network": "POLYGON", "amount": "97.500097" },
"party": { "name": "Ada Obi", "email": "ada@example.com" },
"funding_account": {
"account_name": "Ada Obi",
"account_number": "9901234567",
"bank_code": "090175",
"bank_name": "Rubies MFB",
"amount": "150000"
},
"expires_at": "2026-07-30T12:12:00Z",
"created_at": "…",
"updated_at": "…"
}
}Pulse sends the stablecoin to destination.address on the quoted network. An
address that is not valid for that network returns invalid_destination_address.
See address formats.
Create an offramp order
An offramp order gives the customer a deposit address: a wallet address that Pulse issues for this order. The customer sends the stablecoin to it, and Pulse pays naira to a bank account: the customer's, or a payout account.
Resolve the account first to get its registered name. Ask the customer to confirm the name before you create the order.
curl https://engine-api.pilot.pulseshiga.io/v1/offramp/orders \
-H "X-API-Key: <your key>" \
-H "Content-Type: application/json" \
-d '{
"reference": "partner-order-88214",
"quote_id": "0198e3f2-…",
"beneficiary": {
"name": "Ada Obi",
"email": "ada@example.com",
"nin": "12345678901",
"bvn": "22345678901",
"bank_account": {
"bank_code": "058",
"account_number": "0123456789"
}
}
}'The response has status awaiting_payment. Show the customer funding_account. See
Fund the order.
{
"status": true,
"data": {
"id": "0198e400-…",
"reference": "partner-order-88214",
"type": "offramp",
"status": "awaiting_payment",
"rate": "1366.75",
"source": { "currency": "USDT", "network": "POLYGON", "amount": "102.432779" },
"destination": { "currency": "NGN", "amount": "140000" },
"party": { "name": "Ada Obi", "email": "ada@example.com" },
"funding_account": {
"deposit_address": "0xDeF…",
"network": "POLYGON"
},
"expires_at": "2026-07-30T12:14:00Z",
"created_at": "…",
"updated_at": "…"
}
}Order creation verifies the beneficiary's 10-digit account number. An unconfirmed
account returns account_verification_failed without creating an order.
Name a payout account on an order
If Pulse set up your business to
pay out only to your own accounts,
name a payout account on each order with recipient_id:
- An offramp order sends
recipient_idinstead ofbeneficiary. The account must be abank_account. Pulse still verifies it with the bank. - An onramp order sends
recipient_idinstead ofdestination, and still sendspayer. The account must be acrypto_addresswith the quote's network and stablecoin.
Send recipient_id or the payout details, never both. A request with both, or with
neither, returns 400 bad_request.
The order keeps the account details from the moment you create it. If an admin
replaces the account, the order still pays those details. The old recipient_id
then returns recipient_not_found, so use the new id for new orders.
See Errors for the codes.
Fund the order
Every order that you create carries its payment instructions in funding_account:
on create, on read, and on the
order.awaiting_payment event. The shape
depends on the order type, so switch on type before you read it.
type | funding_account fields | The customer |
|---|---|---|
onramp | account_name, account_number, bank_code, bank_name, amount | Transfers exactly amount in naira. |
offramp | deposit_address, network | Sends the quoted stablecoin to deposit_address on network. |
A short naira transfer does not fund an onramp order. For an offramp, Pulse applies
the order rate to the stablecoin received, so the naira payout changes with the amount
received. The list endpoint is a summary and omits funding_account.
Send on the quoted network
Send the stablecoin on the quoted network. A deposit on any other network cannot be recovered.
Funding window
Every order carries an expires_at. An offramp order counts 30 minutes from creation.
An onramp order counts 30 minutes from the moment its virtual account is issued, so a
delayed account still gives the customer the full window.
Pulse uses the quote rate when funding arrives within 15 minutes. For funding that arrives later in the window, it can use the current rate. The order shows the rate and amount that Pulse used.
Funding that arrives after expires_at does not complete the order. Create a new
quote and order, and contact support about the late payment.
Order status
An order has one of six statuses. The path depends on the source.
completed can end in failed instead.| Status | Meaning | Terminal |
|---|---|---|
awaiting_payment | Waiting for the customer's naira transfer or stablecoin deposit. | no |
processing | Funds received. Pulse converts and settles the funds. | no |
completed | Settled to the destination. | yes |
expired | Never funded in time. | yes |
failed | Could not be settled. | yes |
held | Pulse could not process a collection payment automatically, and holds the funds for review. See Held payments. | no |
An order from a collection payment never has awaiting_payment or expired,
because the payment arrives before the order exists. Only an order from a collection
payment can be held.
Each status change also sends a webhook.
Track an order
Track an order in one of two ways:
- Wait for a webhook for each status change.
- Read
GET /v1/orders/{id}until the status is terminal. Poll with backoff.
curl https://engine-api.pilot.pulseshiga.io/v1/orders/0198e2d0-… \
-H "X-API-Key: <your key>"{
"status": true,
"data": {
"id": "0198e2d0-…",
"reference": "partner-order-88213",
"type": "onramp",
"status": "processing",
"rate": "1538.46",
"source": { "currency": "NGN", "amount": "150000" },
"destination": { "currency": "USDT", "network": "POLYGON", "amount": "97.500097" },
"party": { "name": "Ada Obi", "email": "ada@example.com" },
"funding_account": {
"account_name": "Ada Obi",
"account_number": "9901234567",
"bank_code": "090175",
"bank_name": "Rubies MFB",
"amount": "150000"
},
"expires_at": "2026-07-30T12:12:00Z",
"created_at": "…",
"updated_at": "…"
}
}The API key scopes the request to your business. An unknown order and another
business's order both return 404 order_not_found.
An order from a quote with a partner fee also returns
partner_fee. An order from a collection payment
also returns collection_id.
Order terms can differ from the quote
Reconcile with the order's rate and destination.amount, not the quote's. Late
funding can change both. Confirm the delivered amount from order.completed.
In the test environment, trigger each outcome on purpose. See Testing.
List orders
List orders to reconcile a period or find an order by reference. Results are
ordered from newest to oldest.
curl "https://engine-api.pilot.pulseshiga.io/v1/orders?type=onramp&per_page=20" \
-H "X-API-Key: <your key>"{
"status": true,
"data": [
{
"id": "0198e2d0-…",
"reference": "partner-order-88213",
"type": "onramp",
"status": "completed",
"rate": "1538.46",
"source": { "currency": "NGN", "amount": "150000" },
"destination": { "currency": "USDT", "network": "POLYGON", "amount": "97.500097" },
"expires_at": "2026-07-30T12:12:00Z"
}
],
"meta": { "next_cursor": "0198e2c1-…" }
}Query parameters
| Parameter | Meaning |
|---|---|
reference | Your reference, exact match. Returns at most one order. |
type | onramp or offramp. |
status | One order status. |
collection_id | Orders created from payments to one collection. |
from | Timestamp, inclusive lower bound (RFC 3339). |
to | Timestamp, inclusive upper bound (RFC 3339). |
per_page | Page size. The default is 20 and the maximum is 100. |
cursor | The next_cursor from the last page. |
See Pagination for cursor handling.
Errors
| Code | Cause |
|---|---|
quote_expired | The quote expired, or it already created an order. Get a new one. |
quote_flow_mismatch | An onramp quote was used on an offramp order, or the reverse. |
invalid_destination_address | The address is not valid for the quoted network. |
account_verification_failed | The bank could not confirm the beneficiary account. |
recipient_id_required | Your business pays out only to its own accounts. Send recipient_id, not beneficiary or destination. |
recipient_id_not_supported | Your business pays its customers. Send beneficiary or destination, not recipient_id. |
recipient_not_found | recipient_id does not match one of your payout accounts. |
recipient_type_mismatch | An offramp order named a wallet, or an onramp order named a bank account. |
recipient_quote_mismatch | The wallet's network or stablecoin differs from the onramp quote's. |
duplicate_reference | The reference belongs to a different order. |
quote_changed | A refresh changed the quote during order creation. |
order_not_found | The order does not exist for your business. |
See Errors for every code.
See the Orders reference for the full schema of every field.