- Offramp — user sends stablecoins, recipient receives local fiat (bank or mobile wallet)
- Onramp — user deposits fiat into a virtual account, recipient receives stablecoins
POST /v2/sender/orders. Direction comes from the source and destination types you pass.
Fastest path to production: one authenticated create call. The API validates bank or mobile account details and resolves an acceptable rate inside that request—you are not required to call verify-account or the public rates URL first. The sections after Create a payment order cover what happens next (response shape, webhooks). Optional: prefetch quotes and account names explains how to polish checkout when you want prefetching—not because it is required.
Flow Overview
- Offramp (Stablecoin → Fiat)
- Onramp (Fiat → Stablecoin)
Getting Started
1. Obtain API Credentials
Sign up as a Sender at app.paycrest.io and complete the KYB process. Once verified, you’ll have:- API Key — included in every request as the
API-Keyheader - API Secret — used to verify webhook signatures; keep this secret
2. Configure Tokens (Optional)
In the dashboard settings, you can set defaults per token/network. Offramp: defaultrefundAddress (crypto) and feeAddress; you can also pass source.refundAddress on the request. Onramp: you supply source.refundAccount (fiat bank or mobile details for refunds) in the order payload—configure related defaults in the dashboard where available.
Create a payment order
POST /v2/sender/orders
Pass a source and a destination object. The type field on each determines the direction. Offramp and onramp use the same endpoint; examples below include curl for both directions. The extra JavaScript and Python snippets are offramp-shaped—mirror the onramp curl payload (source.type: "fiat", destination.type: "crypto", refundAccount, etc.) in your language.
- Offramp (stablecoin → fiat)
- Onramp (fiat → stablecoin)
- JavaScript (offramp)
- Python (offramp)
- JavaScript (onramp)
- Python (onramp)
User sends stablecoins; recipient receives fiat. Set
source.type = "crypto" and destination.type = "fiat".Request Fields
| Field | Type | Required | Description |
|---|---|---|---|
amount | string | ✅ | Payment amount. Denomination set by amountIn (defaults to crypto units). |
amountIn | string | — | "crypto" (default) or "fiat". Set to "fiat" when amount is in the fiat currency. |
rate | string | — | Exchange rate (fiat per crypto). Omit to let the API choose a valid rate. Pass rate only when you lock a quote from the user; it must stay within market tolerance. See Prefetch a quote for the UI if you display rates before create. |
senderFeePercent | string | — | Optional fee percentage you collect, settled atomically onchain. |
reference | string | — | Your internal order ID. |
source | object | ✅ | { type: "crypto", ... } for offramp; { type: "fiat", ... } for onramp. |
destination | object | ✅ | { type: "fiat", ... } for offramp; { type: "crypto", ... } for onramp. |
Handle the create response
The create response includes aproviderAccount whose shape depends on direction: offramp gives a crypto receive address; onramp gives virtual account / fiat transfer instructions.
Offramp — send tokens to receiveAddress
amount + senderFee + transactionFee (all returned in the response) in the specified token to providerAccount.receiveAddress before validUntil.
Onramp — present virtual account to user
providerAccount details to your user. They must transfer exactly amountToTransfer in currency to accountIdentifier before validUntil. (There is no onchain token transfer from your app for this path—the user moves fiat through their bank or mobile wallet.)
Monitor completion
Listen for webhooks or pollGET /v2/sender/orders/:id.
Webhook Events
Configure your webhook URL in the Sender Dashboard. Webhooks use thepayment_order.<status> format and include a direction field ("offramp" or "onramp"):
| Event | When it fires |
|---|---|
payment_order.deposited | Stablecoin deposit detected (offramp) |
payment_order.pending | Assigned to provider / fiat deposit confirmed (onramp) |
payment_order.validated | Fiat payout confirmed by provider |
payment_order.settling | Onchain release in progress |
payment_order.settled | Order complete |
payment_order.refunding | Refund in progress |
payment_order.refunded | Funds returned to sender |
payment_order.expired | No deposit received before validUntil |
You can notify the user of a successful offramp at
payment_order.validated — that’s when the provider has confirmed fiat delivery. For onramp, use payment_order.settled — that’s when stablecoins are confirmed onchain.Verify Webhook Signatures
Verify theX-Paycrest-Signature header using HMAC-SHA256 with your API Secret:
- JavaScript
- Python
- Go
Polling
Optional: Prefetch quotes and account names
Use this when you want clearer UX before the user confirms—not because the API requires extra calls.Resolve the account display name early
POST /v2/verify-account runs the same checks again on create, but calling it first cuts down “invalid account” surprises and returns a canonical accountName for your form.
- Offramp — verify the fiat recipient you will send in
destination.recipient(sameinstitution,accountIdentifier, andcurrencyas the order). - Onramp — verify
source.refundAccount(where fiat refunds go), not the crypto wallet indestination.recipient.
200 OK response returns the resolved accountName in data. If data is a real name (e.g. "JOHN DOE"), use that exact string as accountName on the order. If data is "OK", name lookup is not available for that corridor—supply your own accountName.
Prefetch a quote for the UI
GET /v2/rates/{network}/{token}/{amount}/{fiat} is public (no API key). Use it when you want to show a rate before submit, or when you will pass rate on create (it must stay within market tolerance). If you omit rate on create, the API still picks an acceptable rate.
The JSON
data object includes buy and sell (unless you pass ?side=buy or ?side=sell). Use data.sell.rate for offramp and data.buy.rate for onramp when displaying a quote. provider_id is optional and must be exactly 8 letters (A–Z or a–z) if you pin a provider.- cURL
- JavaScript
- Python
Sender Fees
You can optionally charge your users a fee on each order. It’s settled atomically onchain — no offchain billing required. Configure your fee in Sender Dashboard settings (required). You can override it per-request by passingsenderFeePercent in the order payload.
providerAccount.receiveAddress is amount + senderFee + transactionFee, all returned in the create response. Onramp: the user pays fiat using providerAccount.amountToTransfer (and currency); fee line items in the response still describe what applies on settlement—follow the fields returned for your order.
Testing
Paycrest runs on mainnet only. Test with the minimum order size: $0.50 on any supported chain. Use small amounts until you’ve verified your integration end-to-end. Smoke test: credentials +POST /v2/sender/orders + handle providerAccount + one webhook or poll. Add verify-account and GET /v2/rates when you are polishing the checkout experience—they are not required to go live.
v1 Legacy API
The v1 API is fully supported for existing integrations. It supports offramp only and uses a flat request schema.v1 Offramp — Create Order
v1 Offramp — Create Order
POST /v1/sender/ordersreceiveAddress (string) and validUntil. Send tokens to receiveAddress.v1 vs v2 Differences
v1 vs v2 Differences
| Feature | v1 | v2 |
|---|---|---|
| Offramp | ✅ | ✅ |
| Onramp | ❌ | ✅ |
| Schema | Flat: token, network, recipient | Polymorphic: source, destination with type |
refundAddress | returnAddress (top-level) | source.refundAddress (offramp); onramp uses source.refundAccount |
| Amount direction | Always crypto | amountIn: "fiat" or "crypto" |
| Sender fee | Fixed via dashboard | Dashboard (fixed) or senderFeePercent on request |
| Numeric types | Number | String |
| Webhook version | "1" | "2" |
Related
- Transaction Lifecycle — order statuses, webhook events, expired vs refunded
- Supported Stablecoins — tokens, networks, contract addresses
- Supported Currencies — active fiat corridors
- API Reference — Sender — full endpoint spec