curl --request POST \
--url https://api.paj.cash/pub/v2/onramp \
--header 'Content-Type: application/json' \
--header 'x-api-key: <api-key>' \
--data '
{
"currency": "NGN",
"recipient": "4C8XFXBPG1Dm2eBs68oixjsVEzjxz5pwTDrLCR17ZRGz",
"mint": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
"chain": "SOLANA",
"fiatAmount": 100,
"amount": 123,
"webhookURL": "https://example.com/webhook",
"userExternalId": "user-123",
"businessUSDCFee": 0.5,
"bvn": "22222222222"
}
'import requests
url = "https://api.paj.cash/pub/v2/onramp"
payload = {
"currency": "NGN",
"recipient": "4C8XFXBPG1Dm2eBs68oixjsVEzjxz5pwTDrLCR17ZRGz",
"mint": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
"chain": "SOLANA",
"fiatAmount": 100,
"amount": 123,
"webhookURL": "https://example.com/webhook",
"userExternalId": "user-123",
"businessUSDCFee": 0.5,
"bvn": "22222222222"
}
headers = {
"x-api-key": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'x-api-key': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({
currency: 'NGN',
recipient: '4C8XFXBPG1Dm2eBs68oixjsVEzjxz5pwTDrLCR17ZRGz',
mint: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v',
chain: 'SOLANA',
fiatAmount: 100,
amount: 123,
webhookURL: 'https://example.com/webhook',
userExternalId: 'user-123',
businessUSDCFee: 0.5,
bvn: '22222222222'
})
};
fetch('https://api.paj.cash/pub/v2/onramp', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.paj.cash/pub/v2/onramp",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'currency' => 'NGN',
'recipient' => '4C8XFXBPG1Dm2eBs68oixjsVEzjxz5pwTDrLCR17ZRGz',
'mint' => 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v',
'chain' => 'SOLANA',
'fiatAmount' => 100,
'amount' => 123,
'webhookURL' => 'https://example.com/webhook',
'userExternalId' => 'user-123',
'businessUSDCFee' => 0.5,
'bvn' => '22222222222'
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"x-api-key: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.paj.cash/pub/v2/onramp"
payload := strings.NewReader("{\n \"currency\": \"NGN\",\n \"recipient\": \"4C8XFXBPG1Dm2eBs68oixjsVEzjxz5pwTDrLCR17ZRGz\",\n \"mint\": \"EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v\",\n \"chain\": \"SOLANA\",\n \"fiatAmount\": 100,\n \"amount\": 123,\n \"webhookURL\": \"https://example.com/webhook\",\n \"userExternalId\": \"user-123\",\n \"businessUSDCFee\": 0.5,\n \"bvn\": \"22222222222\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("x-api-key", "<api-key>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.paj.cash/pub/v2/onramp")
.header("x-api-key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"currency\": \"NGN\",\n \"recipient\": \"4C8XFXBPG1Dm2eBs68oixjsVEzjxz5pwTDrLCR17ZRGz\",\n \"mint\": \"EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v\",\n \"chain\": \"SOLANA\",\n \"fiatAmount\": 100,\n \"amount\": 123,\n \"webhookURL\": \"https://example.com/webhook\",\n \"userExternalId\": \"user-123\",\n \"businessUSDCFee\": 0.5,\n \"bvn\": \"22222222222\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.paj.cash/pub/v2/onramp")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["x-api-key"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"currency\": \"NGN\",\n \"recipient\": \"4C8XFXBPG1Dm2eBs68oixjsVEzjxz5pwTDrLCR17ZRGz\",\n \"mint\": \"EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v\",\n \"chain\": \"SOLANA\",\n \"fiatAmount\": 100,\n \"amount\": 123,\n \"webhookURL\": \"https://example.com/webhook\",\n \"userExternalId\": \"user-123\",\n \"businessUSDCFee\": 0.5,\n \"bvn\": \"22222222222\"\n}"
response = http.request(request)
puts response.read_body{
"id": "679be2527f99f556d77b8cc2",
"address": "4KSLE7EU1P7PQ8Rc4hdb2ZKq2JmWHD8UXJp7guEdyT9j",
"signature": "2oLx7XrXP7Df9dZca1difJ91ZFmA44EEadNqrZTecta6ugcX8BAETbpvKQs54r58fmJTQVLzUSuzvRBFwS1GfJLo",
"mint": "So11111111111111111111111111111111111111112",
"currency": "NGN",
"amount": 100000,
"usdcAmount": 100,
"fiatAmount": 100,
"sender": "4KSLE7EU1P7PQ8Rc4hdb2ZKq2JmWHD8UXJp7guEdyT9j",
"recipient": "4KSLE7EU1P7PQ8Rc4hdb2ZKq2JmWHD8UXJp7guEdyT9j",
"rate": 9,
"status": "PENDDING",
"transactionType": "OFF_RAMP",
"createdAt": "2026-09-28T14:55:27.097Z",
"accountNumber": "0025635480",
"accountName": "Paj Inc.",
"bank": "First Bank",
"fee": 0.2,
"chain": "SOLANA"
}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.
curl --request POST \
--url https://api.paj.cash/pub/v2/onramp \
--header 'Content-Type: application/json' \
--header 'x-api-key: <api-key>' \
--data '
{
"currency": "NGN",
"recipient": "4C8XFXBPG1Dm2eBs68oixjsVEzjxz5pwTDrLCR17ZRGz",
"mint": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
"chain": "SOLANA",
"fiatAmount": 100,
"amount": 123,
"webhookURL": "https://example.com/webhook",
"userExternalId": "user-123",
"businessUSDCFee": 0.5,
"bvn": "22222222222"
}
'import requests
url = "https://api.paj.cash/pub/v2/onramp"
payload = {
"currency": "NGN",
"recipient": "4C8XFXBPG1Dm2eBs68oixjsVEzjxz5pwTDrLCR17ZRGz",
"mint": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
"chain": "SOLANA",
"fiatAmount": 100,
"amount": 123,
"webhookURL": "https://example.com/webhook",
"userExternalId": "user-123",
"businessUSDCFee": 0.5,
"bvn": "22222222222"
}
headers = {
"x-api-key": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'x-api-key': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({
currency: 'NGN',
recipient: '4C8XFXBPG1Dm2eBs68oixjsVEzjxz5pwTDrLCR17ZRGz',
mint: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v',
chain: 'SOLANA',
fiatAmount: 100,
amount: 123,
webhookURL: 'https://example.com/webhook',
userExternalId: 'user-123',
businessUSDCFee: 0.5,
bvn: '22222222222'
})
};
fetch('https://api.paj.cash/pub/v2/onramp', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.paj.cash/pub/v2/onramp",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'currency' => 'NGN',
'recipient' => '4C8XFXBPG1Dm2eBs68oixjsVEzjxz5pwTDrLCR17ZRGz',
'mint' => 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v',
'chain' => 'SOLANA',
'fiatAmount' => 100,
'amount' => 123,
'webhookURL' => 'https://example.com/webhook',
'userExternalId' => 'user-123',
'businessUSDCFee' => 0.5,
'bvn' => '22222222222'
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"x-api-key: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.paj.cash/pub/v2/onramp"
payload := strings.NewReader("{\n \"currency\": \"NGN\",\n \"recipient\": \"4C8XFXBPG1Dm2eBs68oixjsVEzjxz5pwTDrLCR17ZRGz\",\n \"mint\": \"EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v\",\n \"chain\": \"SOLANA\",\n \"fiatAmount\": 100,\n \"amount\": 123,\n \"webhookURL\": \"https://example.com/webhook\",\n \"userExternalId\": \"user-123\",\n \"businessUSDCFee\": 0.5,\n \"bvn\": \"22222222222\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("x-api-key", "<api-key>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.paj.cash/pub/v2/onramp")
.header("x-api-key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"currency\": \"NGN\",\n \"recipient\": \"4C8XFXBPG1Dm2eBs68oixjsVEzjxz5pwTDrLCR17ZRGz\",\n \"mint\": \"EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v\",\n \"chain\": \"SOLANA\",\n \"fiatAmount\": 100,\n \"amount\": 123,\n \"webhookURL\": \"https://example.com/webhook\",\n \"userExternalId\": \"user-123\",\n \"businessUSDCFee\": 0.5,\n \"bvn\": \"22222222222\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.paj.cash/pub/v2/onramp")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["x-api-key"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"currency\": \"NGN\",\n \"recipient\": \"4C8XFXBPG1Dm2eBs68oixjsVEzjxz5pwTDrLCR17ZRGz\",\n \"mint\": \"EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v\",\n \"chain\": \"SOLANA\",\n \"fiatAmount\": 100,\n \"amount\": 123,\n \"webhookURL\": \"https://example.com/webhook\",\n \"userExternalId\": \"user-123\",\n \"businessUSDCFee\": 0.5,\n \"bvn\": \"22222222222\"\n}"
response = http.request(request)
puts response.read_body{
"id": "679be2527f99f556d77b8cc2",
"address": "4KSLE7EU1P7PQ8Rc4hdb2ZKq2JmWHD8UXJp7guEdyT9j",
"signature": "2oLx7XrXP7Df9dZca1difJ91ZFmA44EEadNqrZTecta6ugcX8BAETbpvKQs54r58fmJTQVLzUSuzvRBFwS1GfJLo",
"mint": "So11111111111111111111111111111111111111112",
"currency": "NGN",
"amount": 100000,
"usdcAmount": 100,
"fiatAmount": 100,
"sender": "4KSLE7EU1P7PQ8Rc4hdb2ZKq2JmWHD8UXJp7guEdyT9j",
"recipient": "4KSLE7EU1P7PQ8Rc4hdb2ZKq2JmWHD8UXJp7guEdyT9j",
"rate": 9,
"status": "PENDDING",
"transactionType": "OFF_RAMP",
"createdAt": "2026-09-28T14:55:27.097Z",
"accountNumber": "0025635480",
"accountName": "Paj Inc.",
"bank": "First Bank",
"fee": 0.2,
"chain": "SOLANA"
}Amount: token or fiat, never both
Send exactly one of:fiatAmount— what your user will pay, incurrency. Paj works out how much token that buys.amount— how much ofmintyour user should end up with. Paj works backwards to the fiat figure they must pay.
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: 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.
{
"fiatAmount": 15000,
"currency": "NGN",
"chain": "SOLANA",
"mint": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
"recipient": "4C8XFXBPG1Dm2eBs68oixjsVEzjxz5pwTDrLCR17ZRGz"
}
{
"fiatAmount": 15000,
"currency": "NGN",
"chain": "ARC",
"mint": "0x3600000000000000000000000000000000000000",
"recipient": "0x2B5AD5c4795c026514f8317c7a215E218DcCD6cF"
}
{
"fiatAmount": 15000,
"currency": "NGN",
"chain": "TON",
"mint": "TON",
"recipient": "UQBvW8Z5huBkMJYdnfAEM5JqTNkuWX3diqYENkWsIL0XgHwB"
}
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.
chain; confirm the wallet can receive there.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.
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 10ofatokenandyousetafeeof‘0.5‘,theyarebilledthenairaequivalentof10.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.
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
Ifrecipient 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 POSTs 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.Authorizations
Body
Local currency user wish to use
"NGN"
User wallet address
"4C8XFXBPG1Dm2eBs68oixjsVEzjxz5pwTDrLCR17ZRGz"
Expected token mint address. On TON, a jetton master address or "TON" for Toncoin.
"EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"
Wallet Chain
"SOLANA"
Amount user should doposite; in local currency. Mutually exclusive with amount.
100
Amount of token user expected to receive. Mutually exclusive with fiatAmount.
Webhook URL to receive order status updates
"https://example.com/webhook"
User external id
"user-123"
Business fee in USDC deducted from the token amount sent to the user
0.5
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.
"22222222222"
Response
The created onramp order. accountNumber, accountName and bank are the account to pay; the order stays in INIT until the deposit lands.
Transaction Id
"679be2527f99f556d77b8cc2"
Address
"4KSLE7EU1P7PQ8Rc4hdb2ZKq2JmWHD8UXJp7guEdyT9j"
Transaction Signature
"2oLx7XrXP7Df9dZca1difJ91ZFmA44EEadNqrZTecta6ugcX8BAETbpvKQs54r58fmJTQVLzUSuzvRBFwS1GfJLo"
Mint Address
"So11111111111111111111111111111111111111112"
Currency
"NGN"
Mint Amount
100000
Usdc Amount
100
Fiat Amount
100
Wallet Public Key
"4KSLE7EU1P7PQ8Rc4hdb2ZKq2JmWHD8UXJp7guEdyT9j"
Recipient Public Key
"4KSLE7EU1P7PQ8Rc4hdb2ZKq2JmWHD8UXJp7guEdyT9j"
Rate of currency to usdc
9
Transaction Status
"PENDDING"
Transaction Type
"OFF_RAMP"
Transaction creation date
"2026-09-28T14:55:27.097Z"
Account Number (for onramp transactions)
"0025635480"
Account Name (for onramp transactions)
"Paj Inc."
Bank Name (for onramp transactions)
"First Bank"
Transaction fee in usdc
0.2
Chain
"SOLANA"