API Endpoints
All requests require authentication headers. See Authentication for details.
Base URL: https://papi.cryptumpay.com
Response Format
All responses use the same envelope structure:
// Success
{ "data": T, "errorObject": null }
// Error
{ "data": null, "errorObject": { "httpCode": number, "appCode": number, "message"?: string } }POST /v1/orders
Creates a new merchant order. Depending on your integration approach, the returned order ID can be:
- Passed to the widget so the customer completes payment inline on your site
- Used to form a payment gateway link —
https://pay.cryptumpay.com/m/{orderId}— and sent to the customer via email, messenger, SMS, or any other channel (see Without Widget) - Used entirely without the widget, from your own app's UI — see White-label / Native App
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
title | string | Yes | Order title shown to the customer |
description | string | Yes | Order description shown to the customer |
fiatAmount | string | One of two price bases | Payment amount as a decimal string, e.g. "9.99". Mutually exclusive with cryptoAmount — see below |
fiatCurrency | string | With fiatAmount | Fiat currency code, e.g. "usd" or "eur" |
cryptoCurrency | string | One of two price bases | Cryptocurrency ticker, e.g. "usdt". Use together with cryptoBlockchain and cryptoAmount instead of fiatAmount/fiatCurrency |
cryptoBlockchain | string | With cryptoCurrency | Blockchain name, e.g. "tron", "bsc" (same values as in POST /v1/withdraw) |
cryptoAmount | string | With cryptoCurrency | Order price denominated in crypto, as a decimal string. The equivalent fiatAmount is calculated for you from the current rate. When this basis is used, the request also creates and attaches the customer order (deposit address, exact amount) in the same call — see Price basis below |
clientEmail | string | No | Your end customer's email, attached to the resulting deposit/customer order (distinct from projectDataUserEmail, which is just metadata on the merchant order) |
url | string | No | Return URL for the payment gate (overrides project-level successPath/failPath) |
projectDataUserId | string | No | Your internal user ID to attach to the order |
projectDataUserEmail | string | No | Customer email to attach to the order (metadata only) |
projectDataOrderId | string | No | Your internal order ID to attach to the order |
projectDataMeta | string | No | Arbitrary metadata string (e.g. JSON) to attach to the order |
Price basis
You can price an order in fiat or in crypto — pick exactly one:
fiatAmount+fiatCurrency— the classic flow. Creates only the merchant order; the customer (via widget or your own app, calling the endpoints below) still has to pick a network to pay with.cryptoCurrency+cryptoBlockchain+cryptoAmount— for apps with their own network picker (use GET /v1/currencies to build it). Creates the merchant order and the customer order (deposit address) in one call, since the network is already known.fiatCurrencyis optional here — it defaults to your project's preferred fiat currency, and is used only for bookkeeping/reporting, not for pricing.
Who pays the fees
If your project is configured so the merchant absorbs a given fee (service fee and/or network transfer fee), the customer pays exactly cryptoAmount for that fee. If the customer absorbs it, it's added on top automatically. GET /v1/currencies already returns a cryptoMinAmount that accounts for this, so you don't need to do this math yourself — just stay above it.
Response
{
"data": {
"id": "1c555afe-77bc-ab57-ffa0-27cdacc94000",
"project": {
"id": "string",
"feePercent": 1.5,
"logoUrl": "https://...",
"title": "My Store",
"url": "https://mystore.com"
},
"expiresAt": 1745975440234,
"customerOrderRequest": {
"fiatTicker": "usd",
"fiatAmount": "9.99",
"title": "Order #1",
"description": "My product",
"url": null,
"projectData": {}
}
},
"errorObject": null
}When you create an order using the cryptoAmount price basis, the response has the same shape (id is always the merchant order ID), plus a customerOrder field with the deposit details — identical to what GET /v1/orders/:orderId returns right after creation:
{
"data": {
"id": "1c555afe-77bc-ab57-ffa0-27cdacc94000",
"project": { ... },
"expiresAt": 1745975440234,
"customerOrderRequest": { ... },
"customerOrderId": "4fc1ae7b-c7f9-4dc7-2222-8d37f993e28f",
"customerOrder": {
"status": "created",
"cryptoTicker": "trx",
"cryptoBlockchainId": 12,
"cryptoAmount": "150.5",
"depositAddress": "TQn9Y2khEsLJW1ChVWFMSMeRDow5KcbLSE",
"expiresAt": 1745977240234,
"depositsList": [],
"summary": { ... }
}
},
"errorObject": null
}id is always the merchant order ID
Regardless of which price basis you use, id in the response is always the merchant order ID — the same ID you'd use for GET /v1/orders/:orderId or the pay.cryptumpay.com/m/{orderId} link. The deposit-specific details (address, exact crypto amount, status) live under customerOrder, present only once a customer order exists.
Errors
| appCode | Description |
|---|---|
6000 | Order creation failed (service unavailable or invalid parameters) |
Node.js SDK
const response = await client.createOrder({
title: 'Order #12345',
description: 'Shopping cart checkout',
fiatAmount: '49.99',
fiatCurrency: 'usd',
projectDataUserId: 'user_42',
projectDataOrderId: 'internal-order-123',
});
if (response.errorObject) {
console.error('Failed to create order:', response.errorObject);
} else {
const orderId = response.data.id;
// Pass orderId to the widget via onCreateOrder callback
}GET /v1/currencies
Returns the cryptocurrencies and networks currently available for payment, with the limits and fees relevant to your project. Use this to build your own currency/network picker for the cryptoAmount price basis of POST /v1/orders (see White-label / Native App) — no rate lookups or fee math required on your side.
This endpoint only covers the crypto side. It does not return fiat rates or fiat-denominated limits — if you need to quote a price in fiat, convert it yourself; the orders themselves are always priced in either fiat or crypto, your choice, independently of this endpoint.
Response
{
"data": [
{
"cryptoCurrency": "trx",
"cryptoBlockchain": "tron",
"cryptoMinAmount": "7.5",
"cryptoMaxAmount": "99998.9",
"transferFee": "1.1"
},
{
"cryptoCurrency": "usdt",
"cryptoBlockchain": "bsc",
"cryptoMinAmount": "5.000000",
"cryptoMaxAmount": "49999.5"
}
],
"errorObject": null
}cryptoMinAmountis already the safe minimum forcryptoAmountinPOST /v1/orders— if your project absorbs a fee, this number already accounts for it, so any amount at or above it is guaranteed valid.cryptoMaxAmountalready leaves room for the network transfer fee.transferFee(network fee) is present only if your customer pays it (your project's fee-side setting). If your project absorbs it, the field is omitted — it doesn't affect what you charge the customer.serviceFeePercent(CryptumPay's service fee, as a percentage) is present only if your customer pays it. If your project absorbs it, the field is omitted.- Networks currently under maintenance or with deposits disabled are left out of the list entirely.
Node.js SDK
const response = await client.getCurrencies();
if (!response.errorObject) {
for (const entry of response.data) {
console.log(`${entry.cryptoCurrency} on ${entry.cryptoBlockchain}: min ${entry.cryptoMinAmount}`);
}
}GET /v1/orders/:orderId
Returns the current state of a merchant order, including whether a customer has started payment (customerOrderId).
Path Parameters
| Parameter | Description |
|---|---|
orderId | The merchant order ID returned by POST /v1/orders |
Response
{
"data": {
"id": "1c555afe-77bc-ab57-ffa0-27cdacc94000",
"project": { ... },
"expiresAt": 1745975440234,
"customerOrderRequest": { ... },
"customerOrderId": "4fc1ae7b-c7f9-4dc7-2222-8d37f993e28f",
"customerOrder": {
"status": "created",
"cryptoTicker": "trx",
"cryptoBlockchainId": 12,
"cryptoAmount": "150.5",
"depositAddress": "TQn9Y2khEsLJW1ChVWFMSMeRDow5KcbLSE",
"expiresAt": 1745977240234,
"depositsList": [],
"summary": { ... }
},
"financeSummary": {
"status": 3,
"accrued": true,
"income": {
"amount": "49.10",
"currency": "usd"
}
}
},
"errorObject": null
}customerOrder is null until the customer has picked a network and the deposit address has been assigned — either via the widget, or via your own app calling POST /v1/orders with the cryptoAmount price basis. Use it to re-fetch the deposit address/amount at any point (e.g. after your app was closed and reopened), without needing a separate endpoint.
financeSummary is present only after the customer order reaches a final state. income reflects the amount actually credited after fees.
financeSummary Statuses
| status | Description |
|---|---|
0 | In progress — customer order is being processed |
1 | Under review — order is pending manual review |
2 | Canceled — order was canceled |
3 | Paid — payment received and funds credited |
4 | Crediting — funds are being credited to your balance |
The accrued field is true once funds have been credited to your project balance.
Confirming a successful payment
To confirm that funds were received, check that financeSummary.status is 3 or 4:
- Status 3 (Paid) — funds have already been credited to your project balance.
- Status 4 (Crediting) — crediting is still in progress, but the system has definitely received the payment and will complete the transfer shortly.
Do not fulfill orders based on status 0 (In progress) or 1 (Under review), as those states are not yet final.
Errors
| appCode | Description |
|---|---|
6001 | Order not found or does not belong to your project |
Node.js SDK
const response = await client.getOrder('1c555afe-77bc-ab57-ffa0-27cdacc94000');
if (response.errorObject) {
console.error('Order not found:', response.errorObject);
} else {
console.log('Customer order ID:', response.data.customerOrderId);
console.log('Finance summary:', response.data.financeSummary);
}POST /v1/withdraw
IP Whitelist Required
This endpoint checks the IP address of the request. You must configure an IP whitelist for your API key in the console. Requests from unlisted IPs will be rejected with error 8002. See Authentication → IP Whitelist.
Initiates a cryptocurrency withdrawal to an external address.
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
currency | string | Yes | Cryptocurrency ticker, e.g. "usdt" |
blockchain | string | Yes | Blockchain identifier, e.g. "bsc", "tron" |
address | string | Yes | Destination wallet address |
amount | string | Yes | Amount to withdraw as a decimal string, e.g. "100.00" |
extra | string | null | No | Memo/tag for blockchains that require it (e.g. XRP, TON) |
Response
{
"data": {
"id": "b44ac754-b67a-4d79-8c78-61e01b9c1196"
},
"errorObject": null
}Errors
| appCode | Description |
|---|---|
8000 | Withdrawal creation failed (insufficient balance, invalid address, or service unavailable) |
8002 | IP whitelist not configured for this API key |
7003 | Request IP is not in the whitelist |
Node.js SDK
const response = await client.withdraw({
currency: 'usdt',
blockchain: 'bsc',
address: '0xYOUR_WALLET_ADDRESS',
amount: '100.00',
});
if (response.errorObject) {
console.error('Withdrawal failed:', response.errorObject);
} else {
console.log('Withdrawal ID:', response.data.id);
}GET /v1/withdraw/:id
IP Whitelist Required
Same requirement as POST /v1/withdraw — IP whitelist must be configured. See Authentication → IP Whitelist.
Returns the current status of a withdrawal.
Path Parameters
| Parameter | Description |
|---|---|
id | The withdrawal ID returned by POST /v1/withdraw |
Response
{
"data": {
"status": "finished",
"hash": "0xabc123..."
},
"errorObject": null
}hash is the on-chain transaction hash, available once the transaction is broadcast.
Withdrawal Statuses
| Status | Description |
|---|---|
created | Withdrawal queued, not yet processed |
pending | Transaction submitted to the blockchain |
finished | Transaction confirmed on-chain |
failed | Withdrawal could not be completed |
Errors
| appCode | Description |
|---|---|
8001 | Withdrawal not found or does not belong to your project |
See Also
- Authentication — Signing requests and error reference
- Webhooks & Redirects — Receive payment status updates via webhooks