Create Ramp Intent
Create a standalone ramp intent for on-ramp (buy crypto) or off-ramp (sell crypto). A ramp intent represents a merchant-initiated ramp session that tracks the full lifecycle of a transaction — from creation through to completion or expiry.
https://api.zenpayz.com/payment/api/v1/ramp-intentsRequest
Headers
| Header | Description |
|---|---|
Content-Type REQUIRED | application/json |
Authorization REQUIRED | Bearer {api_key} |
x-merchant-id REQUIRED | Your merchant ID |
x-request-id REQUIRED | Unique request ID for tracing |
This endpoint requires API key authentication. Include your API key in the Authorization header and your merchant ID in x-merchant-id.
Body Parameters
| Parameter | Type | Description |
|---|---|---|
userId REQUIRED | string | Unique user identifier from your system. Used as the KYC record key. |
kycVerified REQUIRED | boolean | Whether you have already verified this user's identity. When true, ZenPays KYC verification is skipped in the widget. |
type REQUIRED | string | buy (on-ramp) or sell (off-ramp) |
wallets REQUIRED | array | Array of { network, address } objects (min 1) |
fiatCurrency REQUIRED | string | Fiat currency code (e.g. USD, EUR) |
cryptoCurrency OPTIONAL | string | Crypto identifier (e.g. btc_bitcoin, usdt_tron) |
amount OPTIONAL | number | Default amount (must be positive) |
successUrl OPTIONAL | string | URL to redirect on success |
cancelUrl OPTIONAL | string | URL to redirect on cancel |
customerEmail OPTIONAL | string | Customer's email address |
metadata OPTIONAL | object | Custom key-value pairs for your reference |
When kycVerified: true, the checkout widget skips ZenPays KYC verification entirely — use this when your platform has already verified the user's identity.
When kycVerified: false (default), the widget checks the user's KYC status. If unverified, the customer is prompted to complete identity verification before proceeding.
Wallet Object
| Parameter | Type | Description |
|---|---|---|
network REQUIRED | string | Blockchain network (e.g. ethereum, tron, solana) |
address REQUIRED | string | Wallet address on the network |
Sample Request Body
{
"userId": "usr_abc123",
"kycVerified": false,
"type": "buy",
"wallets": [
{
"network": "ethereum",
"address": "0x742d35Cc6634C0532925a3b844Bc9e7595f2bD28"
}
],
"fiatCurrency": "USD",
"cryptoCurrency": "eth_ethereum",
"amount": 100,
"successUrl": "https://yourapp.com/success",
"cancelUrl": "https://yourapp.com/cancel",
"customerEmail": "customer@example.com",
"metadata": {
"orderId": "order_123"
}
}
Response
Success (201 Created)
{
"success": true,
"data": {
"intentId": "ri_1710345678000_a1b2c3d4e5f6g7h8",
"redirectUrl": "https://checkout.zenpayz.com/payments/ramping/widget/ri_1710345678000_a1b2c3d4e5f6g7h8",
"expiresAt": "2026-03-15T10:00:00.000Z"
},
"message": "Ramp intent created"
}
Response Fields
| Parameter | Type | Description |
|---|---|---|
intentId OPTIONAL | string | Unique intent identifier (format: ri_{timestamp}_{hex}) |
redirectUrl OPTIONAL | string | URL to redirect the customer to the ramp widget |
expiresAt OPTIONAL | string | ISO 8601 expiry timestamp (1 hour from creation) |
Ramp intents automatically expire 1 hour after creation. Once expired, the intent cannot be used and a new one must be created.
Error Responses
| Code | HTTP | Message |
|---|---|---|
| VALIDATION_ERROR | 400 | Invalid request body or missing required fields |
| RAMP_INTENT_CREATION_FAILED | 500 | Failed to create the ramp intent |
Examples
- cURL
- JavaScript
- Python
curl -X POST https://api.zenpayz.com/payment/api/v1/ramp-intents \
-H "Content-Type: application/json" \
-H "Authorization: Bearer zp_test_your_api_key" \
-H "x-merchant-id: merch_abc123" \
-H "x-request-id: req_$(date +%s)" \
-d '{
"userId": "usr_abc123",
"kycVerified": false,
"type": "buy",
"wallets": [
{ "network": "ethereum", "address": "0x742d35Cc6634C0532925a3b844Bc9e7595f2bD28" }
],
"fiatCurrency": "USD",
"cryptoCurrency": "eth_ethereum",
"amount": 100,
"customerEmail": "customer@example.com",
"successUrl": "https://yourapp.com/success",
"cancelUrl": "https://yourapp.com/cancel"
}'
const response = await fetch('https://api.zenpayz.com/payment/api/v1/ramp-intents', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer zp_test_your_api_key',
'x-merchant-id': 'merch_abc123',
'x-request-id': `req_${Date.now()}`,
},
body: JSON.stringify({
userId: 'usr_abc123',
kycVerified: false,
type: 'buy',
wallets: [
{ network: 'ethereum', address: '0x742d35Cc6634C0532925a3b844Bc9e7595f2bD28' },
],
fiatCurrency: 'USD',
cryptoCurrency: 'eth_ethereum',
amount: 100,
customerEmail: 'customer@example.com',
successUrl: 'https://yourapp.com/success',
cancelUrl: 'https://yourapp.com/cancel',
}),
});
const { data } = await response.json();
console.log(`Intent ID: ${data.intentId}`);
console.log(`Redirect to: ${data.redirectUrl}`);
console.log(`Expires at: ${data.expiresAt}`);
// Redirect the customer to the ramp widget
window.location.href = data.redirectUrl;
import requests
import time
response = requests.post(
"https://api.zenpayz.com/payment/api/v1/ramp-intents",
headers={
"Authorization": "Bearer zp_test_your_api_key",
"x-merchant-id": "merch_abc123",
"x-request-id": f"req_{int(time.time())}",
},
json={
"userId": "usr_abc123",
"kycVerified": False,
"type": "buy",
"wallets": [
{"network": "ethereum", "address": "0x742d35Cc6634C0532925a3b844Bc9e7595f2bD28"}
],
"fiatCurrency": "USD",
"cryptoCurrency": "eth_ethereum",
"amount": 100,
"customerEmail": "customer@example.com",
"successUrl": "https://yourapp.com/success",
"cancelUrl": "https://yourapp.com/cancel",
},
)
data = response.json()["data"]
print(f"Intent ID: {data['intentId']}")
print(f"Redirect to: {data['redirectUrl']}")
print(f"Expires at: {data['expiresAt']}")
Intent Status Lifecycle
┌──────────┐
│ created │ ← Initial state
└────┬─────┘
│
First GET request
│
┌────▼─────┐
┌────│ active │────┐
│ └────┬─────┘ │
│ │ │
┌────▼────┐ ┌──▼───────┐ ┌▼──────────┐
│cancelled│ │completed │ │ expired │
└─────────┘ └──────────┘ └────────────┘
(terminal states)
Webhook Events
When a ramp intent reaches a terminal state, ZenPay delivers a webhook to your configured endpoint. Configure webhooks in the merchant dashboard under Developer Tools → Webhooks.
Events
| Event | When | Description |
|---|---|---|
ramp.completed | Buy: crypto sent to wallet. Sell: fiat payout submitted. | The ramp flow finished successfully. |
ramp.failed | Crypto send or payout submission failed. | The ramp flow encountered an error. |
Buy Ramp Completed (ramp.completed)
Delivered when crypto has been sent to the customer's wallet address.
{
"event_type": "ramp.completed",
"payment_data": {
"merchant_id": "m_abc123",
"intentId": "ri_1710345678000_a1b2c3d4e5f6g7h8",
"type": "buy",
"status": "completed",
"fiatCurrency": "USD",
"cryptoCurrency": "eth_ethereum",
"amount": 100,
"chain": "ethereum",
"destinationAddress": "0x742d35Cc6634C0532925a3b844Bc9e7595f2bD28",
"fireblocksTxId": "fb_tx_9a8b7c6d5e4f",
"completedAt": "2026-03-16T14:30:00.000Z"
}
}
Sell Ramp Completed (ramp.completed)
Delivered when the fiat payout has been submitted to the customer's bank account.
{
"event_type": "ramp.completed",
"payment_data": {
"merchant_id": "m_abc123",
"intentId": "ri_1710345678000_a1b2c3d4e5f6g7h8",
"type": "sell",
"status": "completed",
"fiatCurrency": "USD",
"cryptoCurrency": "usdt_tron",
"amount": 20,
"payoutId": "po_xyz789",
"payoutAmount": 19.50,
"payoutCurrency": "USD",
"completedAt": "2026-03-16T14:30:00.000Z"
}
}
Ramp Failed (ramp.failed)
Delivered when the crypto send (buy) or payout submission (sell) fails.
{
"event_type": "ramp.failed",
"payment_data": {
"merchant_id": "m_abc123",
"intentId": "ri_1710345678000_a1b2c3d4e5f6g7h8",
"type": "buy",
"status": "failed",
"fiatCurrency": "USD",
"cryptoCurrency": "eth_ethereum",
"amount": 100,
"reason": "Crypto send failed",
"failedAt": "2026-03-16T14:30:00.000Z"
}
}
Webhook Payload Fields
| Parameter | Type | Description |
|---|---|---|
merchant_id OPTIONAL | string | Your merchant ID |
intentId OPTIONAL | string | Ramp intent ID (ri_...) |
type OPTIONAL | string | "buy" or "sell" |
status OPTIONAL | string | "completed" or "failed" |
fiatCurrency OPTIONAL | string | ISO currency code (e.g. USD, EUR) |
cryptoCurrency OPTIONAL | string | Crypto identifier (e.g. eth_ethereum, usdt_tron) |
amount OPTIONAL | number | Original intent amount |
completedAt OPTIONAL | string | ISO 8601 completion timestamp |
failedAt OPTIONAL | string | ISO 8601 failure timestamp |
reason OPTIONAL | string | Human-readable failure reason |
chain OPTIONAL | string | Blockchain network (e.g. ethereum, tron) |
destinationAddress OPTIONAL | string | Wallet address crypto was sent to |
cryptoTxId OPTIONAL | string | Crypto transaction reference |
payoutId OPTIONAL | string | Payout reference ID |
payoutAmount OPTIONAL | number | Fiat amount paid out (after fees) |
payoutCurrency OPTIONAL | string | Fiat currency of payout |
Webhook Headers
| Header | Description |
|---|---|
X-ZenPay-Signature | HMAC-SHA256 signature of the request body |
X-Zenpay-Event | Event type (e.g. ramp.completed) |
X-Zenpay-Event-Id | Unique delivery ID for deduplication |
X-Zenpay-Timestamp | ISO 8601 timestamp of delivery |
X-Zenpay-Attempt | Delivery attempt number (1, 2, or 3) |
See the Webhooks guide for signature verification, retry behavior, and best practices.
Next Steps
- Get Ramp Intent — Retrieve intent details and widget URL
- List Ramp Intents — Browse intent history
- On-Ramp Flow — Complete buy integration guide
- Off-Ramp Flow — Complete sell integration guide