> ## Documentation Index
> Fetch the complete documentation index at: https://docs.daya.co/llms.txt
> Use this file to discover all available pages before exploring further.

# Legacy: Create an onramp

> Legacy route for creating an NGN receive flow

## Overview

Create an onramp through the legacy compatibility route. New integrations should use [`POST /v1/funding-accounts`](/api-reference/funding-accounts/create-funding-account) with `rail: NGN_VIRTUAL_ACCOUNT`.

## Authentication

<ParamField header="X-Api-Key" type="string" required>
  Your merchant API key
</ParamField>

<ParamField header="X-Idempotency-Key" type="string" required>
  Unique idempotency key to prevent duplicate onramp creation
</ParamField>

## Request Body

<ParamField body="type" type="string" required>
  Onramp type

  **Allowed values:** `TEMPORARY`, `PERMANENT`

  * `TEMPORARY`: Short-lived VA (25 minutes), locked to `rate_id`
  * `PERMANENT`: Long-lived VA, uses current rate at settlement
</ParamField>

<ParamField body="customer" type="object" required>
  Customer information. Either `customer_id` or `email` must be provided.

  <Expandable title="customer properties">
    <ParamField body="customer.customer_id" type="string">
      UUID of an existing customer. Either this or `customer.email` is required.

      **Example:** `650e8400-e29b-41d4-a716-446655440000`
    </ParamField>

    <ParamField body="customer.email" type="string">
      Email for auto-creating a customer. Either this or `customer.customer_id` is required.

      **Example:** `user@example.com`
    </ParamField>

    <ParamField body="customer.first_name" type="string">
      Customer first name
    </ParamField>

    <ParamField body="customer.last_name" type="string">
      Customer last name
    </ParamField>

    <ParamField body="customer.verification" type="object">
      Verification data. Required for permanent onramps when creating a new customer (no `customer_id`). For existing customers, required only if the customer is not yet verified.

      <Expandable title="verification properties">
        <ParamField body="customer.verification.bvn" type="string" required>
          11-digit Bank Verification Number

          **Example:** `22345678901`
        </ParamField>

        <ParamField body="customer.verification.image_url" type="string" required>
          Face image for identity matching. Accepts an HTTPS URL to a jpg/png image, or base64-encoded image data up to 1 MiB decoded.

          **Example:** `https://example.com/selfie.jpg`
        </ParamField>
      </Expandable>
    </ParamField>
  </Expandable>
</ParamField>

<ParamField body="rate_id" type="string">
  Rate identifier from `GET /v1/rates`

  **Example:** `rate_8x7k2mq9p`

  <Note>
    **Required** for temporary onramps. **Not allowed** for permanent onramps.
  </Note>
</ParamField>

<ParamField body="amount" type="integer">
  Expected deposit amount in NGN

  **Example:** `50000`

  <Note>
    **Required** for temporary onramps. Ignored for permanent onramps.
  </Note>
</ParamField>

<ParamField body="developer_fee" type="object">
  Optional fee that your merchant account keeps from each onramp deposit. Omit to use `0%`.

  <Expandable title="developer_fee properties">
    <ParamField body="developer_fee.percentage" type="string" required>
      Percentage of each received deposit that your merchant account keeps. Use a decimal string from `0` to `50`. This is a percentage value, not basis points: `0.5` means `0.5%`, `2` means `2%`, and `50` means `50%`.

      **Example:** `"2.5"`
    </ParamField>
  </Expandable>
</ParamField>

<ParamField body="settlement" type="object" required>
  Settlement configuration

  <Expandable title="settlement properties">
    <ParamField body="settlement.mode" type="string" required>
      Settlement mode

      **Allowed values:**

      * `ONCHAIN` - Settle directly on-chain to the destination address
      * `INTERNAL_BALANCE` - Credit merchant USD balance
    </ParamField>

    <ParamField body="settlement.asset" type="string" required>
      Asset type

      **Allowed values:** `USDC`, `USDT`

      <Note>
        Permanent onramps only support `USDC` in v1.
      </Note>
    </ParamField>

    <ParamField body="settlement.chain" type="string">
      Blockchain network. Required for `ONCHAIN` mode.

      **Allowed values:** `APTOS`, `BASE`, `CELO`, `ETHEREUM`, `POLYGON`, `SOLANA`, `TRON`

      See [Supported Chains](/concepts/supported-chains) for details on which assets are available on each chain.
    </ParamField>

    <ParamField body="settlement.destination_address" type="string">
      On-chain destination address. Required for `ONCHAIN` mode.

      **Example:** `0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb`
    </ParamField>
  </Expandable>
</ParamField>

## Request Examples

<CodeGroup>
  ```json Temporary - On-chain theme={null}
  {
    "type": "TEMPORARY",
    "customer": {
      "email": "user@example.com"
    },
    "rate_id": "rate_8x7k2mq9p",
    "amount": 5000000,
    "developer_fee": {
      "percentage": "2.5"
    },
    "settlement": {
      "mode": "ONCHAIN",
      "asset": "USDC",
      "chain": "BASE",
      "destination_address": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb"
    }
  }
  ```

  ```json Temporary - Internal Balance theme={null}
  {
    "type": "TEMPORARY",
    "customer": {
      "email": "user@example.com"
    },
    "rate_id": "rate_8x7k2mq9p",
    "amount": 5000000,
    "settlement": {
      "mode": "INTERNAL_BALANCE",
      "asset": "USDC"
    }
  }
  ```

  ```json Permanent - New Customer theme={null}
  {
    "type": "PERMANENT",
    "customer": {
      "email": "user@example.com",
      "first_name": "John",
      "last_name": "Doe",
      "verification": {
        "bvn": "22345678901",
        "image_url": "https://example.com/selfie.jpg"
      }
    },
    "settlement": {
      "mode": "ONCHAIN",
      "asset": "USDC",
      "chain": "BASE",
      "destination_address": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb"
    }
  }
  ```

  ```json Permanent - Existing Verified Customer theme={null}
  {
    "type": "PERMANENT",
    "customer": {
      "customer_id": "650e8400-e29b-41d4-a716-446655440000"
    },
    "settlement": {
      "mode": "ONCHAIN",
      "asset": "USDC",
      "chain": "SOLANA",
      "destination_address": "7EcDhSYGxXyscszYEp35KHN8vvw3svAuLKTzXwCFLtV"
    }
  }
  ```

  ```json Permanent - Existing Unverified Customer theme={null}
  {
    "type": "PERMANENT",
    "customer": {
      "customer_id": "650e8400-e29b-41d4-a716-446655440000",
      "verification": {
        "bvn": "22345678901",
        "image_url": "data:image/jpeg;base64,/9j/4AAQSkZJRg..."
      }
    },
    "settlement": {
      "mode": "ONCHAIN",
      "asset": "USDC",
      "chain": "BASE",
      "destination_address": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb"
    }
  }
  ```

  ```bash cURL - Temporary theme={null}
  curl --request POST \
    --url https://api.daya.co/v1/onramps \
    --header 'X-Api-Key: YOUR_API_KEY' \
    --header 'X-Idempotency-Key: unique-key-123' \
    --header 'Content-Type: application/json' \
    --data '{
      "type": "TEMPORARY",
      "customer": {
        "email": "user@example.com"
      },
      "rate_id": "rate_8x7k2mq9p",
      "amount": 5000000,
      "developer_fee": {
        "percentage": "2.5"
      },
      "settlement": {
        "mode": "ONCHAIN",
        "asset": "USDC",
        "chain": "BASE",
        "destination_address": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb"
      }
    }'
  ```

  ```bash cURL - Permanent theme={null}
  curl --request POST \
    --url https://api.daya.co/v1/onramps \
    --header 'X-Api-Key: YOUR_API_KEY' \
    --header 'X-Idempotency-Key: unique-key-456' \
    --header 'Content-Type: application/json' \
    --data '{
      "type": "PERMANENT",
      "customer": {
        "email": "user@example.com",
        "first_name": "John",
        "last_name": "Doe",
        "verification": {
          "bvn": "22345678901",
          "image_url": "https://example.com/selfie.jpg"
        }
      },
      "settlement": {
        "mode": "ONCHAIN",
        "asset": "USDC",
        "chain": "BASE",
        "destination_address": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb"
      }
    }'
  ```
</CodeGroup>

## Response

### Temporary Onramp Response

<ResponseField name="onramp_id" type="string" required>
  Unique identifier for this onramp
</ResponseField>

<ResponseField name="type" type="string" required>
  `TEMPORARY`
</ResponseField>

<ResponseField name="status" type="string" required>
  Current onramp status. New onramps start as `ACTIVE`.
</ResponseField>

<ResponseField name="amount" type="string" required>
  Expected deposit amount in NGN. Required and always returned for temporary onramps. Decimal string.

  **Example:** `"50000.50"`
</ResponseField>

<ResponseField name="rate_id" type="string" required>
  Associated rate identifier
</ResponseField>

<ResponseField name="payment_reference" type="string" required>
  Unique payment reference for the transfer
</ResponseField>

<ResponseField name="developer_fee" type="object" required>
  Developer fee percentage used for deposits received through this onramp.

  <Expandable title="developer_fee properties">
    <ResponseField name="developer_fee.percentage" type="string">
      Configured percentage as a decimal string.
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="virtual_account" type="object" required>
  NGN bank account details for receiving deposits

  <Expandable title="virtual_account properties">
    <ResponseField name="virtual_account.account_number" type="string">
      Virtual account number
    </ResponseField>

    <ResponseField name="virtual_account.account_name" type="string">
      Account name
    </ResponseField>

    <ResponseField name="virtual_account.bank_name" type="string">
      Bank name
    </ResponseField>

    <ResponseField name="virtual_account.expires_at" type="string">
      When the virtual account expires (temporary onramps only)
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="expires_at" type="string" required>
  When onramp expires (\~25 minutes from creation)
</ResponseField>

<ResponseField name="settlement" type="object" required>
  Settlement configuration (same as request)
</ResponseField>

<ResponseField name="created_at" type="string" required>
  When onramp was created (ISO 8601 timestamp)
</ResponseField>

### Permanent Onramp Response

<ResponseField name="type" type="string" required>
  `PERMANENT`
</ResponseField>

<ResponseField name="permanent_onramp_id" type="string" required>
  Unique identifier for the permanent onramp configuration
</ResponseField>

<ResponseField name="active_settlement_id" type="string" required>
  Identifier of the currently active settlement configuration
</ResponseField>

<ResponseField name="customer_id" type="string" required>
  The customer this permanent onramp belongs to
</ResponseField>

<ResponseField name="settlement" type="object" required>
  Active settlement configuration
</ResponseField>

<ResponseField name="virtual_account" type="object" required>
  Permanent NGN bank account details

  <Expandable title="virtual_account properties">
    <ResponseField name="virtual_account.account_number" type="string">
      Virtual account number
    </ResponseField>

    <ResponseField name="virtual_account.bank_name" type="string">
      Bank name
    </ResponseField>

    <ResponseField name="virtual_account.account_name" type="string">
      Account name (typically "Daya-Customer Name")
    </ResponseField>
  </Expandable>
</ResponseField>

### Success Responses

<ResponseExample>
  ```json 201 Created - Temporary On-chain theme={null}
  {
    "onramp_id": "onramp_3j5k8n2q",
    "type": "TEMPORARY",
    "status": "ACTIVE",
    "amount": "50000.50",
    "rate_id": "rate_8x7k2mq9p",
    "payment_reference": "DAYA-3J5K8N2Q",
    "developer_fee": {
      "percentage": "2.5"
    },
    "virtual_account": {
      "account_number": "9876543210",
      "account_name": "Daya - user@example.com",
      "bank_name": "Wema Bank",
      "expires_at": "2026-01-14T15:30:00Z"
    },
    "expires_at": "2026-01-14T15:30:00Z",
    "settlement": {
      "mode": "ONCHAIN",
      "asset": "USDC",
      "chain": "BASE",
      "destination_address": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb"
    },
    "created_at": "2026-01-14T15:05:12Z"
  }
  ```

  ```json 201 Created - Temporary Internal Balance theme={null}
  {
    "onramp_id": "onramp_7k3m5n8q",
    "type": "TEMPORARY",
    "status": "ACTIVE",
    "amount": "50000.50",
    "rate_id": "rate_8x7k2mq9p",
    "payment_reference": "DAYA-7K3M5N8Q",
    "virtual_account": {
      "account_number": "1122334455",
      "account_name": "Daya - user@example.com",
      "bank_name": "Wema Bank",
      "expires_at": "2026-01-14T15:30:00Z"
    },
    "expires_at": "2026-01-14T15:30:00Z",
    "settlement": {
      "mode": "INTERNAL_BALANCE"
    },
    "created_at": "2026-01-14T15:05:12Z"
  }
  ```

  ```json 201 Created - Permanent theme={null}
  {
    "type": "PERMANENT",
    "permanent_onramp_id": "850e8400-e29b-41d4-a716-446655440000",
    "active_settlement_id": "950e8400-e29b-41d4-a716-446655440000",
    "customer_id": "650e8400-e29b-41d4-a716-446655440000",
    "settlement": {
      "mode": "ONCHAIN",
      "asset": "USDC",
      "chain": "BASE",
      "destination_address": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb"
    },
    "virtual_account": {
      "account_number": "1234567890",
      "bank_name": "Wema Bank",
      "account_name": "Daya-John Doe"
    }
  }
  ```
</ResponseExample>

## Error Responses

<ResponseExample>
  ```json 400 Bad Request - Missing customer theme={null}
  {
    "error": {
      "code": "validation_error",
      "message": "either customer.customer_id or customer.email is required"
    }
  }
  ```

  ```json 400 Bad Request - Rate not allowed for permanent theme={null}
  {
    "error": {
      "code": "validation_error",
      "message": "rate_id is not allowed for permanent onramps"
    }
  }
  ```

  ```json 400 Bad Request - Invalid settlement mode theme={null}
  {
    "error": {
      "code": "validation_error",
      "message": "invalid settlement mode"
    }
  }
  ```

  ```json 400 Bad Request - Unsupported asset theme={null}
  {
    "error": {
      "code": "validation_error",
      "message": "permanent onramps only support USDC in v1"
    }
  }
  ```

  ```json 400 Bad Request - Customer not verified theme={null}
  {
    "error": {
      "code": "validation_error",
      "message": "customer is not verified and no verification data provided"
    }
  }
  ```

  ```json 400 Bad Request - Verification failed theme={null}
  {
    "error": {
      "code": "VALIDATION_FAILED",
      "message": "BVN verification failed"
    }
  }
  ```

  ```json 502 Bad Gateway - Verification provider error theme={null}
  {
    "error": {
      "code": "INTEGRATION_FAILED",
      "message": "Verification provider unavailable, please try again"
    }
  }
  ```

  ```json 400 Bad Request - Rate expired theme={null}
  {
    "error": {
      "code": "rate_expired",
      "message": "The specified rate_id has expired",
      "details": "Request a new rate via GET /v1/rates"
    }
  }
  ```

  ```json 400 Bad Request - Invalid address theme={null}
  {
    "error": {
      "code": "invalid_address",
      "message": "destination_address is not valid for chain BASE"
    }
  }
  ```

  ```json 429 Too Many Requests - Daily limit theme={null}
  {
    "error": {
      "code": "onramp_creation_limit_exceeded",
      "message": "Merchant has exceeded daily onramp creation limit (1,000/day)"
    }
  }
  ```
</ResponseExample>

## Validation Rules

<AccordionGroup>
  <Accordion title="Customer identification">
    Either `customer.customer_id` or `customer.email` must be provided (not both optional, at least one required).
  </Accordion>

  <Accordion title="Temporary onramp rules">
    * `rate_id` is **required** and must be a valid, non-expired rate snapshot
    * `amount` is **required**
    * 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
  </Accordion>

  <Accordion title="Permanent onramp rules">
    * `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
  </Accordion>

  <Accordion title="Verification (permanent onramps)">
    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
  </Accordion>
</AccordionGroup>

## Permanent Onramp Behavior

<Info>
  **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.
</Info>

<Info>
  **Virtual accounts for permanent onramps do not expire.** The same account number is reused across settlement updates.
</Info>

## Best Practices

<Steps>
  <Step title="Get fresh rate before creation (temporary)">
    Always call `GET /v1/rates` immediately before creating a temporary onramp to ensure maximum validity window.
  </Step>

  <Step title="Validate destination address">
    Use a blockchain library to validate addresses before submitting:

    ```javascript theme={null}
    import { isAddress } from 'ethers';

    if (!isAddress(destinationAddress)) {
      throw new Error('Invalid Ethereum address');
    }
    ```
  </Step>

  <Step title="Use customer_id for returning customers">
    Create customers once via `POST /v1/customers`, then reference them by `customer_id` in subsequent onramp requests.
  </Step>

  <Step title="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.
  </Step>

  <Step title="Use idempotency keys">
    Always include a unique `X-Idempotency-Key` header to prevent duplicate onramp creation on retries.
  </Step>
</Steps>

## Rate Limits

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

## Next Steps

<CardGroup cols={2}>
  <Card title="List Deposits" icon="list" href="/api-reference/deposits/list-deposits">
    Query deposits for an onramp
  </Card>

  <Card title="Customer API" icon="users" href="/api-reference/customers/create-customer">
    Pre-create customers before onramp requests
  </Card>
</CardGroup>
