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

# Create an onramp order

> Opens an onramp order for the API key’s business and returns the virtual bank account the end user pays into. Provide either amount (token) or fiatAmount, not both. When a bvn is given the order is credited to that end user — traced through their KYC record, or verified and registered if the BVN is new; without one the order is not attributed to any user. Once the deposit is confirmed the token is delivered to recipient on the given chain, and every status change is posted to webhookURL.

Opens an order to convert fiat into a token. Paj hands you a virtual bank
account, your user pays naira into it, and the token is delivered to the wallet
you named. It is the mirror image of [Create an offramp
order](/api-reference/create-offramp-order), and the two share a shape: state
the amount up front, get back somewhere to send funds, and let the deposit do
the rest.

## Amount: token or fiat, never both

Send **exactly one** of:

* **`fiatAmount`** — what your user will pay, in `currency`. Paj works out how
  much token that buys.
* **`amount`** — how much of `mint` your user should end up with. Paj works
  backwards to the fiat figure they must pay.

Sending both is a `400`; so is sending neither. `recipient`, `mint` and `chain`
are required either way.

## Choosing the chain

`chain` is where the token is delivered, and can be any [supported
chain](/concepts/chains): `SOLANA`, `TON`, `ETHEREUM`, `BASE`, `BSC`, `MONAD` or
`ARC`. `recipient` is a wallet on that chain and `mint` is the token's address on
it.

Buying the chain's settlement stablecoin (USDC, or USDT on TON) is a direct
transfer. Any other token is swapped on-chain from that stablecoin, with the
output delivered straight to `recipient` in the same step, so what arrives can
differ slightly from the quote by the swap's slippage.

<CodeGroup>
  ```json Solana theme={null}
  {
    "fiatAmount": 15000,
    "currency": "NGN",
    "chain": "SOLANA",
    "mint": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
    "recipient": "4C8XFXBPG1Dm2eBs68oixjsVEzjxz5pwTDrLCR17ZRGz"
  }
  ```

  ```json Arc theme={null}
  {
    "fiatAmount": 15000,
    "currency": "NGN",
    "chain": "ARC",
    "mint": "0x3600000000000000000000000000000000000000",
    "recipient": "0x2B5AD5c4795c026514f8317c7a215E218DcCD6cF"
  }
  ```

  ```json TON (Toncoin) theme={null}
  {
    "fiatAmount": 15000,
    "currency": "NGN",
    "chain": "TON",
    "mint": "TON",
    "recipient": "UQBvW8Z5huBkMJYdnfAEM5JqTNkuWX3diqYENkWsIL0XgHwB"
  }
  ```
</CodeGroup>

On TON, `mint` is `"TON"` for Toncoin or a jetton **master** address for
anything else. Any spelling of a TON address — `EQ…`, `UQ…` or raw `0:…` — is
accepted for both `mint` and `recipient`.

`recipient` and `mint` must be addresses in the format of `chain` — a Solana
address on an EVM order, or the reverse, is a `400`.

<Warning>
  An EVM address is valid on all five EVM chains, so the format alone cannot
  catch a user who gave you their wallet for the wrong EVM network. The token is
  sent on `chain`; confirm the wallet can receive there.
</Warning>

## The account to pay

Four fields on the response describe where the money goes:

| Field | What it is |
| - | - |
| `accountNumber` | A virtual account opened for this order alone |
| `bank` | The bank that account sits with |
| `fiatAmount` | The exact amount to transfer, in major units |
| `accountName` | The account holder, with the amount embedded |

`accountName` is worth a note: it comes back looking like
`Paj Inc.(Pay NGN 1,500.00)`. The amount is baked into the name deliberately, so
the figure is visible inside the user's own banking app while they are typing
the transfer. Render it as-is, but drive your own UI from `fiatAmount` — parsing
the name is fragile.

<Warning>
  The virtual account is per-order and the amount is expected to match. Do not
  cache an account number and reuse it for a later order, and do not let a user
  send a different figure than the one quoted.
</Warning>

## Charging your own fee

`businessUSDCFee` is your markup, in USDC, and it is added **on top** rather
than taken out of the middle. If your user is buying $10 of a token and you set
a fee of `0.5`, they are billed the naira equivalent of $10.50 and still receive
the full \$10 of token. The fee comes back on the response as `fee`. Despite the
name, it is counted in the chain's settlement stablecoin, which on TON is USDT.

Leave it off and you charge nothing beyond the rate, which already includes the
spread configured for your business — see [Rates](/concepts/rates).

## Attributing the order to a user

`bvn` is optional and identifies the end user the order belongs to. When you
send one, Paj traces it to that user through their KYC record; if the BVN is new
to Paj, it is verified with the identity provider and the user is registered from
the verified bio before the order is created. A BVN that fails verification is a
`400`.

Omit it and the order stays a business-level order under your API key. If you
only need your own identifier carried through, `userExternalId` is a free-text
field Paj stores and echoes back without interpreting.

## First-time recipients on Solana

If `recipient` has never held `mint` on Solana, a token account has to be created
for it before the token can land, and that costs rent. Paj charges it to the
order. Nothing is required from you — but a first purchase can cost fractionally
more than a repeat one for the same user, which is worth knowing before a
support ticket asks why.

## Following the order

`status` starts at `INIT` and moves to `PROCESSING` when the deposit is
confirmed, then `COMPLETED` once the token has been delivered. `ERROR` is
terminal. Paj `POST`s the whole order back to you at every one of those
transitions, in the same shape as this endpoint's response. It goes to the
order's `webhookURL` if you set one, and otherwise to the `rampWebhookURL` on
your API key; with neither, you are polling.

Unpaid orders are not kept forever: an order is deleted 72 hours after it was
created. Treat the quote as good for that window and open a fresh order rather
than pointing a user at a stale account.


## OpenAPI

````yaml POST /pub/v2/onramp
openapi: 3.0.0
info:
  title: Paj Public API
  description: >-
    Programmatic access to Paj's on/off ramp. Register a Nigerian bank account,
    get

    the on-chain address that pays it, create offramp orders for a specific
    amount,

    and read the live conversion rates.


    Every request must carry an `x-api-key` header. Keys are scoped to a
    business,

    and the rates you receive already have that business's fee applied.
  version: 2.0.0
  contact: {}
servers:
  - url: https://api.paj.cash
    description: Production
security: []
tags: []
paths:
  /pub/v2/onramp:
    post:
      tags:
        - Pub V2
      summary: Create an onramp order
      description: >-
        Opens an onramp order for the API key’s business and returns the virtual
        bank account the end user pays into. Provide either amount (token) or
        fiatAmount, not both. When a bvn is given the order is credited to that
        end user — traced through their KYC record, or verified and registered
        if the BVN is new; without one the order is not attributed to any user.
        Once the deposit is confirmed the token is delivered to recipient on the
        given chain, and every status change is posted to webhookURL.
      operationId: PubV2Controller_createOnrampOrder
      parameters: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateOnrampOrderV2Dto'
      responses:
        '201':
          description: >-
            The created onramp order. accountNumber, accountName and bank are
            the account to pay; the order stays in INIT until the deposit lands.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TransactionInterfaceDto'
        '400':
          description: >-
            The bvn could not be verified, both or neither of amount and
            fiatAmount were given, or the order failed validation
        '401':
          description: Missing or invalid API key (x-api-key header)
      security:
        - x-api-key: []
components:
  schemas:
    CreateOnrampOrderV2Dto:
      type: object
      properties:
        fiatAmount:
          type: number
          description: >-
            Amount user should doposite; in local currency. Mutually exclusive
            with amount.
          example: 100
        amount:
          type: number
          description: >-
            Amount of token user expected to receive. Mutually exclusive with
            fiatAmount.
        currency:
          type: object
          description: Local currency user wish to use
          example: NGN
        recipient:
          type: string
          description: User wallet address
          example: 4C8XFXBPG1Dm2eBs68oixjsVEzjxz5pwTDrLCR17ZRGz
        mint:
          type: string
          description: >-
            Expected token mint address. On TON, a jetton master address or
            "TON" for Toncoin.
          example: EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v
        chain:
          type: object
          description: Wallet Chain
          example: SOLANA
        webhookURL:
          type: string
          description: Webhook URL to receive order status updates
          example: https://example.com/webhook
        userExternalId:
          type: string
          description: User external id
          example: user-123
        businessUSDCFee:
          type: number
          description: Business fee in USDC deducted from the token amount sent to the user
          example: 0.5
          default: 0
        bvn:
          type: string
          description: >-
            BVN of the end user the order is created for. It is traced to that
            user's KYC record; if the BVN is not on file it is verified and the
            user is registered before the order is opened.
          example: '22222222222'
      required:
        - currency
        - recipient
        - mint
        - chain
    TransactionInterfaceDto:
      type: object
      properties:
        id:
          type: string
          description: Transaction Id
          example: 679be2527f99f556d77b8cc2
        address:
          type: string
          description: Address
          example: 4KSLE7EU1P7PQ8Rc4hdb2ZKq2JmWHD8UXJp7guEdyT9j
        signature:
          type: string
          description: Transaction Signature
          example: >-
            2oLx7XrXP7Df9dZca1difJ91ZFmA44EEadNqrZTecta6ugcX8BAETbpvKQs54r58fmJTQVLzUSuzvRBFwS1GfJLo
        mint:
          type: string
          description: Mint Address
          example: So11111111111111111111111111111111111111112
        currency:
          type: object
          description: Currency
          example: NGN
        amount:
          type: number
          description: Mint Amount
          example: 100000
        usdcAmount:
          type: number
          description: Usdc Amount
          example: 100
        fiatAmount:
          type: number
          description: Fiat Amount
          example: 100
        sender:
          type: string
          description: Wallet Public Key
          example: 4KSLE7EU1P7PQ8Rc4hdb2ZKq2JmWHD8UXJp7guEdyT9j
        recipient:
          type: string
          description: Recipient Public Key
          example: 4KSLE7EU1P7PQ8Rc4hdb2ZKq2JmWHD8UXJp7guEdyT9j
        rate:
          type: number
          description: Rate of currency to usdc
          example: 9
        status:
          type: object
          description: Transaction Status
          example: PENDDING
        transactionType:
          type: object
          description: Transaction Type
          example: OFF_RAMP
        createdAt:
          format: date-time
          type: string
          description: Transaction creation date
          example: '2026-09-28T14:55:27.097Z'
        accountNumber:
          type: string
          description: Account Number (for onramp transactions)
          example: '0025635480'
        accountName:
          type: string
          description: Account Name (for onramp transactions)
          example: Paj Inc.
        bank:
          type: string
          description: Bank Name (for onramp transactions)
          example: First Bank
        fee:
          type: number
          description: Transaction fee in usdc
          example: 0.2
        chain:
          type: object
          description: Chain
          example: SOLANA
      required:
        - id
        - address
        - signature
        - mint
        - currency
        - amount
        - usdcAmount
        - fiatAmount
        - sender
        - recipient
        - rate
        - status
        - transactionType
        - createdAt
        - accountNumber
        - accountName
        - bank
        - fee
        - chain
  securitySchemes:
    x-api-key:
      type: apiKey
      in: header
      name: x-api-key

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.