Sessions and orders
Your backend mints a single-order collection session with pulse.collectionSessions.create().
The session is the bridge to the browser: it returns a session_token (cs_…), and the
order it minted carries everything the payment surface needs — deposit_address,
qr_payload (Solana Pay / EIP-681), quote (rate, asset_amount, locked_until),
status, amount_status, deposit, and disbursement. Whether you render with a
drop-in component or build your own UI against the API, this order is what you’re
driving to completion.
Fiat gates
Every session picks one of two fiat gates:
naira — carries per-user identity. Add user_ref plus an account block:
{ provision: true, bvn, nin } for a first-time customer, so Pulse provisions their
named virtual account, or { provision: false, virtual_account_ref } for a returning
one. BVN/NIN are sensitive — they travel server-to-server only and never reach the
browser.
usd — no user_ref, no account block. Funds land in your omnibus rather
than a per-user account.
Each order also has a direction: offramp (the default — your user pays crypto and
you collect fiat) or onramp (your user pays fiat and receives crypto). The
engine-direct onramp flow is live today — see Onramp.
Assets and networks
Sessions accept stablecoin assets (USDC, USDT) on a network. Two network surfaces
exist, and the casing tells you which one you’re on:
Network (product surface — collection sessions) is lowercase: base,
polygon, arbitrum, and others. Solana and TON are recognised but
coming soon (see Supported stablecoins).
EngineNetwork (engine surface — quotes, rates, offramp, onramp) is
UPPERCASE: BASE, POLYGON, ARBITRUM, OPTIMISM, ETHEREUM, BSC.
Pass the casing that matches the call you’re making. The full asset and network lists
live in the SDK reference.
Quotes and locking
A quote is a locked price; creating one moves no money and creates no order. The
order’s quote carries the locked rate, the asset_amount to send, and a
locked_until expiry. A separate rates.preview() call reshapes a quote for
display with no order created, returning { rate, youSend, youReceive, expiresAt, quoteId }. When an order’s quote expires, re-lock it with
collectionOrders.refreshQuote(orderId) rather than creating a new order.
The money-moment lifecycle
A payment moves through the states reflected in the webhook event names:
collection.order.created → collection.deposit.detected →
collection.deposit.confirmed → disbursement.completed. Alongside the happy path,
edge states can occur at any point: collection.amount.underpaid / .overpaid,
collection.deposit.wrong_chain (funds arrived on the wrong network; the order is
still payable on the right one), collection.order.expired, and
collection.order.failed. The sandbox can simulate every one of these directly
against a real order.
Webhooks are the source of truth
Client-side callbacks like onSuccess are UX signals only — they can be spoofed.
Credit a customer only after a verified disbursement.completed webhook.
Client-side callbacks such as onSuccess are UX signals — they can be spoofed.
The signed webhook is the only source of truth for crediting a user.