Skip to main content
POST
/
v1
/
onramp
Create an onramp
curl --request POST \
  --url https://api.daya.co/v1/onramp \
  --header 'Content-Type: application/json' \
  --header 'X-Api-Key: <x-api-key>' \
  --header 'X-Idempotency-Key: <x-idempotency-key>' \
  --data '
{
  "type": "<string>",
  "customer": {
    "customer.customer_id": "<string>",
    "customer.email": "<string>",
    "customer.first_name": "<string>",
    "customer.last_name": "<string>",
    "customer.verification": {
      "customer.verification.bvn": "<string>",
      "customer.verification.image_url": "<string>"
    }
  },
  "rate_id": "<string>",
  "amount": 123,
  "settlement": {
    "settlement.mode": "<string>",
    "settlement.asset": "<string>",
    "settlement.chain": "<string>",
    "settlement.destination_address": "<string>"
  }
}
'
{
  "onramp_id": "onramp_3j5k8n2q",
  "type": "TEMPORARY",
  "status": "ACTIVE",
  "rate_id": "rate_8x7k2mq9p",
  "payment_reference": "DAYA-3J5K8N2Q",
  "virtual_account": {
    "account_number": "9876543210",
    "account_name": "Daya - user@example.com",
    "bank_name": "Wema Bank",
    "bank_code": "035"
  },
  "expires_at": "2026-01-14T15:30:00Z",
  "settlement": {
    "mode": "ONCHAIN",
    "asset": "USDC",
    "chain": "BASE",
    "destination_address": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb"
  },
  "created_at": "2026-01-14T15:05:12Z"
}

Overview

Create an onramp (deposit configuration) that generates a virtual NGN bank account for receiving deposits. Onramps can be temporary (25-minute TTL, locked rate) or permanent (long-lived, floating rate, auto-withdraw only).

Authentication

X-Api-Key
string
required
Your merchant API key
X-Idempotency-Key
string
required
Unique idempotency key to prevent duplicate onramp creation

Request Body

type
string
required
Onramp typeAllowed values: TEMPORARY, PERMANENT
  • TEMPORARY: Short-lived VA (25 minutes), locked to rate_id
  • PERMANENT: Long-lived VA, uses current rate at settlement
customer
object
required
Customer information. Either customer_id or email must be provided.
rate_id
string
Rate identifier from GET /v1/ratesExample: rate_8x7k2mq9p
Required for temporary onramps. Not allowed for permanent onramps.
amount
integer
Expected deposit amount in NGNExample: 50000
Optional for temporary onramps. Ignored for permanent onramps.
settlement
object
required
Settlement configuration

Request Examples

{
  "type": "TEMPORARY",
  "customer": {
    "email": "user@example.com"
  },
  "rate_id": "rate_8x7k2mq9p",
  "amount": 5000000,
  "settlement": {
    "mode": "ONCHAIN",
    "asset": "USDC",
    "chain": "BASE",
    "destination_address": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb"
  }
}

Response

Temporary Onramp Response

onramp_id
string
required
Unique identifier for this onramp
type
string
required
TEMPORARY
status
string
required
Current onramp status. New onramps start as ACTIVE.
rate_id
string
required
Associated rate identifier
payment_reference
string
required
Unique payment reference for the transfer
virtual_account
object
required
NGN bank account details for receiving deposits
expires_at
string
required
When onramp expires (~25 minutes from creation)
settlement
object
required
Settlement configuration (same as request)
created_at
string
required
When onramp was created (ISO 8601 timestamp)

Permanent Onramp Response

type
string
required
PERMANENT
permanent_onramp_id
string
required
Unique identifier for the permanent onramp configuration
active_settlement_id
string
required
Identifier of the currently active settlement configuration
customer_id
string
required
The customer this permanent onramp belongs to
settlement
object
required
Active settlement configuration
virtual_account
object
required
Permanent NGN bank account details

Success Responses

{
  "onramp_id": "onramp_3j5k8n2q",
  "type": "TEMPORARY",
  "status": "ACTIVE",
  "rate_id": "rate_8x7k2mq9p",
  "payment_reference": "DAYA-3J5K8N2Q",
  "virtual_account": {
    "account_number": "9876543210",
    "account_name": "Daya - user@example.com",
    "bank_name": "Wema Bank",
    "bank_code": "035"
  },
  "expires_at": "2026-01-14T15:30:00Z",
  "settlement": {
    "mode": "ONCHAIN",
    "asset": "USDC",
    "chain": "BASE",
    "destination_address": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb"
  },
  "created_at": "2026-01-14T15:05:12Z"
}

Error Responses

{
  "error": {
    "code": "validation_error",
    "message": "either customer.customer_id or customer.email is required"
  }
}

Validation Rules

Either customer.customer_id or customer.email must be provided (not both optional, at least one required).
  • rate_id is required and must be a valid, non-expired rate snapshot
  • amount is optional
  • Settlement modes: ONCHAIN or INTERNAL_BALANCE
  • customer.verification is not required
  • For ONCHAIN: chain and destination_address are required
  • For INTERNAL_BALANCE: chain and destination_address must NOT be set
  • rate_id must not be provided
  • amount is silently ignored
  • Settlement modes: ONCHAIN or INTERNAL_BALANCE
  • For ONCHAIN: chain and destination_address are required
  • For INTERNAL_BALANCE: chain and destination_address must NOT be set
  • If customer.customer_id is not provided, customer.verification with both bvn and image_url is required
  • If customer.customer_id is provided, the customer must either be already verified or customer.verification must be included
Verification uses BVN + face matching via an identity provider.Two paths:New customer (customer.email provided, no customer_id):
  • customer.verification is required with both bvn and image_url
  • The system creates or finds the customer by email, runs verification, then provisions the virtual account
Existing customer (customer.customer_id provided):
  • If already verified: proceeds directly
  • If not verified + customer.verification provided: runs verification first
  • If not verified + no verification data: returns error

Permanent Onramp Behavior

Settlement updates: If a permanent onramp already exists for a customer, calling this endpoint again updates the settlement configuration (chain, address) without creating a new virtual account. The previous settlement is deactivated and the new one becomes active.
Virtual accounts for permanent onramps do not expire. The same account number is reused across settlement updates.

Best Practices

1

Get fresh rate before creation (temporary)

Always call GET /v1/rates immediately before creating a temporary onramp to ensure maximum validity window.
2

Validate destination address

Use a blockchain library to validate addresses before submitting:
import { isAddress } from 'ethers';

if (!isAddress(destinationAddress)) {
  throw new Error('Invalid Ethereum address');
}
3

Use customer_id for returning customers

Create customers once via POST /v1/customers, then reference them by customer_id in subsequent onramp requests.
4

Handle verification errors gracefully

For permanent onramps, verification may fail due to invalid BVN, face mismatch, or provider issues. Handle 400 VALIDATION_FAILED and 502 INTEGRATION_FAILED separately.
5

Use idempotency keys

Always include a unique X-Idempotency-Key header to prevent duplicate onramp creation on retries.

Rate Limits

  • 1,000 onramp creations per day per merchant
  • 100 API requests per minute per key

Next Steps

List Deposits

Query deposits for an onramp

Customer API

Pre-create customers before onramp requests