# Create beneficiary
Source: https://docs.onswitch.xyz/api-reference/beneficiary/create-beneficiary
/openapi.json post /beneficiary/create
Create and save a beneficiary details for future payments.
# Get beneficiaries
Source: https://docs.onswitch.xyz/api-reference/beneficiary/get-beneficiaries
/openapi.json get /beneficiary/fetch
Returns paginated beneficiaries previously used as payment destination.
# Get requirements
Source: https://docs.onswitch.xyz/api-reference/beneficiary/get-requirements
/openapi.json get /beneficiary/requirement
Retrieve the required fields and validation rules for creating a beneficiary.
# AML histories
Source: https://docs.onswitch.xyz/api-reference/compliance/aml-histories
/openapi.json get /compliance/aml/history
Returns paginated previous AML lookup history.
# AML lookup
Source: https://docs.onswitch.xyz/api-reference/compliance/aml-lookup
/openapi.json post /compliance/aml/lookup
Screen an individual, business or crypto wallet against global compliance data.
# Get payments
Source: https://docs.onswitch.xyz/api-reference/history/get-payments
/openapi.json get /payment/history
Returns paginated payments history.
# Get summary
Source: https://docs.onswitch.xyz/api-reference/history/get-summary
/openapi.json get /payment/summary
Returns aggregated payment statistics.
# Get institution
Source: https://docs.onswitch.xyz/api-reference/institution/get-institution
/openapi.json get /institution
Retrieve institution-related data for the authenticated service. When `country` is provided, returns institutions (e.g. banks).
# Lookup institution
Source: https://docs.onswitch.xyz/api-reference/institution/lookup-institution
/openapi.json post /institution/lookup
Resolve bank or payment institution details from country-specific identifiers.
# Get assets
Source: https://docs.onswitch.xyz/api-reference/miscellaneous/get-assets
/openapi.json get /asset
Retrieve a list of all supported stablecoin assets.
# Get coverage
Source: https://docs.onswitch.xyz/api-reference/miscellaneous/get-coverage
/openapi.json get /coverage
Retrieve information about supported countries, currencies, and payment rails.
# Get rates
Source: https://docs.onswitch.xyz/api-reference/miscellaneous/get-rates
/openapi.json get /rates
Retrieve exchange rates for all supported currencies.
# Get quote
Source: https://docs.onswitch.xyz/api-reference/offramp/get-quote
/openapi.json post /offramp/quote
Get a real-time quote for an offramp transaction including exchange rate, settlement time, and payment rail information.
# Get rate
Source: https://docs.onswitch.xyz/api-reference/offramp/get-rate
/openapi.json post /offramp/rate
Get the current exchange rate for an offramp transaction.
# Initiate
Source: https://docs.onswitch.xyz/api-reference/offramp/initiate
/openapi.json post /offramp/initiate
Initiate a stablecoin to local currency transaction.
# Get quote
Source: https://docs.onswitch.xyz/api-reference/onramp/get-quote
/openapi.json post /onramp/quote
Get a real-time quote for an onramp transaction including exchange rate, settlement time, and payment rail information.
# Get rate
Source: https://docs.onswitch.xyz/api-reference/onramp/get-rate
/openapi.json post /onramp/rate
Get the current exchange rate for an onramp transaction.
# Initiate
Source: https://docs.onswitch.xyz/api-reference/onramp/initiate
/openapi.json post /onramp/initiate
Initiate a local currency to stablecoin transaction.
# Confirm deposit
Source: https://docs.onswitch.xyz/api-reference/payment/confirm-deposit
/openapi.json post /payment/confirm
Manually confirm a payment deposit.
# Get status
Source: https://docs.onswitch.xyz/api-reference/payment/get-status
/openapi.json get /payment/status
Retrieve the current status and details of a payment.
# Get quote
Source: https://docs.onswitch.xyz/api-reference/swap/get-quote
/openapi.json post /swap/quote
Get a real-time quote for swapping between two stablecoin assets including exchange rate and fees.
# Initiate
Source: https://docs.onswitch.xyz/api-reference/swap/initiate
/openapi.json post /swap/initiate
Initiate a swap transaction between two stablecoin assets.
# Create wallet
Source: https://docs.onswitch.xyz/api-reference/wallet/create-wallet
/openapi.json post /wallet/create
Create a new wallet with unified balance across multiple chains. One account, one balance, any chain.
# Estimate fee
Source: https://docs.onswitch.xyz/api-reference/wallet/estimate-fee
/openapi.json post /wallet/fee
Estimate the network fee for transferring a specific asset from a wallet.
# Export wallet
Source: https://docs.onswitch.xyz/api-reference/wallet/export-wallet
/openapi.json post /wallet/export
Export wallet private key. Use with caution; the private key grants full control of the wallet.
# Get balance
Source: https://docs.onswitch.xyz/api-reference/wallet/get-balance
/openapi.json get /wallet/{wallet_id}/balance
Get unified spendable wallet balance and breakdown by chain.
# Get transactions
Source: https://docs.onswitch.xyz/api-reference/wallet/get-transactions
/openapi.json get /wallet/{wallet_id}/transactions
List transactions for a wallet with optional pagination.
# Get wallet
Source: https://docs.onswitch.xyz/api-reference/wallet/get-wallet
/openapi.json get /wallet/{wallet_id}
Retrieve the details of an existing wallet
# Get wallets
Source: https://docs.onswitch.xyz/api-reference/wallet/get-wallets
/openapi.json get /wallets
Returns paginated stablecoin wallets
# Lookup transaction
Source: https://docs.onswitch.xyz/api-reference/wallet/lookup-transaction
/openapi.json get /wallet/{wallet_id}/transaction/{reference}
Get details of a single transaction by its reference (UUID).
# Rescue token
Source: https://docs.onswitch.xyz/api-reference/wallet/rescue-token
/openapi.json post /wallet/rescue
Transfer a specific token (by contract address) from the wallet to a recipient address. Use to recover tokens that are not part of the unified balance (e.g. unsupported or airdropped tokens). Supports EVM and Solana token addresses.
# Transfer funds
Source: https://docs.onswitch.xyz/api-reference/wallet/transfer-funds
/openapi.json post /wallet/transfer
Send assets to any address across supported chains. No bridging, no swapping or gas tokens required.
# Update wallet
Source: https://docs.onswitch.xyz/api-reference/wallet/update-wallet
/openapi.json patch /wallet/{wallet_id}
Update wallet details (e.g. callback URL for wallet notifications).
# Get webhooks
Source: https://docs.onswitch.xyz/api-reference/webhook/get-webhooks
/openapi.json get /webhook/history
Returns paginated webhooks history.
# Resend webhook
Source: https://docs.onswitch.xyz/api-reference/webhook/resend-webhook
/openapi.json post /webhook/resend
Resend any webhook notification for a previous transactions.
# Authentication
Source: https://docs.onswitch.xyz/authentication
Authenticate the Switch REST API with your service key (x-service-key) for sandbox and production crypto payment and settlement requests.
You can view both keys anytime in the Switch dashboard. Both keys works with the same base URL `https://api.onswitch.xyz`.
## Example request
```bash curl theme={"dark"}
curl https://api.onswitch.xyz/asset \
-H "x-service-key: $YOUR_SERVICE_KEY" \
-H "Accept: application/json"
```
```javascript Node.js theme={"dark"}
const res = await fetch('https://api.onswitch.xyz/asset', {
headers: {
'x-service-key': process.env.YOUR_SERVICE_KEY,
'Accept': 'application/json',
},
});
```
```python Python theme={"dark"}
import os, urllib.request
req = urllib.request.Request(
"https://api.onswitch.xyz/asset",
headers={
"x-service-key": os.environ["YOUR_SERVICE_KEY"],
"Accept": "application/json",
},
)
urllib.request.urlopen(req, timeout=30)
```
Use your *live key* only when it's production environment, as this might involve live onchain transactions and actual money movement.
If your key may is exposed or compromised, quickly rotate it from the dashboard or contact support at [contact@onswitch.xyz](mailto:contact@onswitch.xyz) for assistance.
## Need Help?
Send us a message
Book a call with us
# Changelog
Source: https://docs.onswitch.xyz/changelog
Release notes and updates for Switch.
**Collect multiple local currencies** to stablecoin in a single transaction. You can collect **NGN**, **GHS**, **CDF**, **GMD**, **KES**, **LRD**, **MWK**, **RWF**, **SLE**, **TZS**, **UGX**, **XAF**, **XOF**, and **ZMW**.
See [Initiate onramp](/api-reference/onramp/initiate) for guides.
You can run **AML lookup** against individuals, businesses, and crypto wallet addresses, and retrieve PEP, sanctions, and other compliance and regulatory details.
See [Lookup AML](/api-reference/compliance/aml-lookup) for guides.
The Switch **partners** dashboard is live, seamlessly built to enable businesses to easily manage and monitor all stablecoin collection and settlement activities from one place.
Use your **Switch wallet** to fund offramp payments. Use it when the offramp deposit should be taken from your Switch wallet balance instead of sending from an external wallet.
**Onramp** and **Offramp** can now reuse a previous payment beneficiary by just passing the **`beneficiary.id`** instead of entering the full beneficiary details every time.
Easily query and retrieve all previous payment beneficiaries you have used as payout destinations.
See [Get beneficiaries](/api-reference/beneficiary/get-beneficiaries) for guides.
Static deposit address is now supported on **Solana** — use the **`static`** parameter on initiate flows where you need a reusable deposit address for the same beneficiary details.
We’ve launched improved **developer documentation** with clearer structure, refreshed guides, and easier navigation so you can integrate faster and find answers without digging.
We’ve added support for `USDC` on the `Stellar` network, significantly expanding our platform’s stabelcoin coverage.
Supported functionality includes:
* Offramp: Convert stablecoins to local currencies
* Onramp: Convert local currencies to stablecoins
* Swap: Swap between different stablecoins
Developer fees collected on payments are now paid out automatically. The collected fee is sent instantly to the **`developer_recipient`** address you specify on each transaction—no separate settlement step.
Support for the **`CNGN`** stablecoin on **BNB Smart Chain (BSC)** and **Base** is live. Enabling coverage for growing local stablecoin usage.
Supported functionality includes:
* Offramp: Convert stablecoins to local currencies
We’ve launched a refreshed and improved Switch marketing site with clearer product information and a smoother experience for visitors exploring what Switch can do.
**Switch Stablecoin Wallets** - One balance. Any chain.
Switch now lets you create **non-custodial stablecoin wallets** — you own your keys, and your funds can move anywhere.
What’s included:
* **Multi chain support**: Solana, Ethereum, Base, BSC, Arbitrum, Optimism, Polygon, Avalanche, Sonic, Berachain, Mantle, Monad, Linea, HyperEVM, Plasma, Blast, Manta, and Mode.
* **Unified balance** — no bridging, no swapping, no network switching, just transfer when you want.
* **Pay fees in any stablecoin** — as low as \$0.05 (Depending on the chain and amount) per transaction, with no native gas tokens required.
* **Send anywhere, instantly** — transfer to any supported chain, regardless of which chain holds your funds.
* **Deep liquidity across chains** — Withdraw and deposit to any supported chain, regardless of which chain holds your funds.
See [Create wallet](/guides/create-wallet) for guides.
# Supported countries
Source: https://docs.onswitch.xyz/countries
List of countries where you can settle stablecoin payouts in local currency using domestic payment rails.
Payouts are typically settled in real-time or within minutes, depending on the country and payment method. Switch supports the following countries and currencies.
## Africa
| Country | Code | Currency | Channels |
| :-------------------------------- | :--- | :------- | :----------------- |
| 🇧🇯 Benin | BJ | XOF | Mobile money |
| 🇨🇲 Cameroon | CM | XAF | Mobile money |
| 🇨🇮 Côte d'Ivoire | CI | XOF | Mobile money |
| 🇨🇩 Democratic Republic of Congo | CD | CDF | Mobile money |
| 🇪🇹 Ethiopia | ET | ETB | Mobile money |
| 🇬🇦 Gabon | GA | XAF | Mobile money |
| 🇬🇲 Gambia | GM | GMD | Mobile money |
| 🇬🇭 Ghana | GH | GHS | Mobile money |
| 🇬🇳 Guinea | GN | GNF | Mobile money |
| 🇰🇪 Kenya | KE | KES | Mobile money, Bank |
| 🇱🇷 Liberia | LR | LRD | Mobile money |
| 🇲🇼 Malawi | MW | MWK | Mobile money |
| 🇲🇱 Mali | ML | XOF | Mobile money |
| 🇳🇬 Nigeria | NG | NGN | Bank |
| 🇷🇼 Rwanda | RW | RWF | Mobile money |
| 🇸🇳 Senegal | SN | XOF | Mobile money |
| 🇸🇱 Sierra Leone | SL | SLE | Mobile money |
| 🇿🇦 South Africa | ZA | ZAR | Bank |
| 🇹🇿 Tanzania | TZ | TZS | Mobile money, Bank |
| 🇺🇬 Uganda | UG | UGX | Mobile money |
| 🇿🇲 Zambia | ZM | ZMW | Mobile money |
## Need help?
Send us a message
Book a call with us
# Developer fee
Source: https://docs.onswitch.xyz/developer_fee
Take a percentage of each payment amount as a fee
Developer fees let you charge a share of each payment amount and route it to a beneficiary wallet you control. Switch calculates the fee from the stablecoin side of the transaction when you add `developer_fee` and `developer_recipient` on quote and initiate requests. When the offramp completes, Switch sends your fee to **`developer_recipient`** automatically.
To see how a fee changes output amounts before you initiate a payment, use
[Get quote](/guides/get-quote) endpoint with the same payment request payload.
## How developer fees work
On [POST `/offramp/quote`](/guides/get-quote) and **POST `/offramp/initiate`**, pass **`developer_fee`** and **`developer_recipient`**. Omit them or set `developer_fee` to `0` if you are not charging a fee.
After the payment completes, Switch pays your **`developer_fee`** portion to **`developer_recipient`** automatically — no separate action required.
## Fee model
Switch enables you to charge any **percentage** as a developer fee on the incoming stablecoin payment amount:
* **`developer_fee`** — Number from **0** to **100**: the percentage of the payment amount taken as your fee (for example, `0.5` for 0.5%, `2` for 2%).
* **`developer_recipient`** — Blockchain address that should receive the developer fee.
**`developer_recipient`** wallet address must be able to receive the
stablecoin and network of the payment.
### Calculation example
Payment of **100 USDC** with **`developer_fee`: `0.2`** (0.2%, i.e. 20 bps):
* Your fee: 100 × 0.002 = **0.2 USDC** to `developer_recipient`.
With **`developer_fee`: `0.5`**:
* Your fee: **0.5 USDC** on a 100 USDC payment (0.5% of 100).
## Need help?
Send us a message and we will get back to you shortly.
Hop on a call with us.
# Check status
Source: https://docs.onswitch.xyz/guides/check-status
Look up the status of a payment by its reference.
**GET `/payment/status`** returns the current state of a stablecoin transaction using the `reference` used to initiate the payment. This enables you to track the progress of any payment if not using the [Webhook](/webhook) endpoint.
## Example Request
```bash cURL theme={"dark"}
curl -L \
--request GET \
--url 'https://api.onswitch.xyz/payment/status?reference=ccdd7666-8cb6-45d6-9caf-498b94d10929' \
--header 'x-service-key: '"$YOUR_SERVICE_KEY" \
--header 'Accept: */*'
```
```javascript Node.js theme={"dark"}
const reference = 'ccdd7666-8cb6-45d6-9caf-498b94d10929';
const params = new URLSearchParams({ reference });
const res = await fetch(`https://api.onswitch.xyz/payment/status?${params}`, {
method: 'GET',
headers: {
'x-service-key': process.env.YOUR_SERVICE_KEY,
Accept: '*/*',
},
});
const json = await res.json();
console.log(json.data?.status, json.data?.reference);
```
```python Python theme={"dark"}
import os, json, urllib.parse, urllib.request
reference = "ccdd7666-8cb6-45d6-9caf-498b94d10929"
q = urllib.parse.urlencode({"reference": reference})
req = urllib.request.Request(
f"https://api.onswitch.xyz/payment/status?{q}",
method="GET",
headers={
"x-service-key": os.environ["YOUR_SERVICE_KEY"],
"Accept": "*/*",
},
)
with urllib.request.urlopen(req, timeout=60) as r:
payload = json.load(r)
data = payload.get("data") or {}
print(data.get("status"), data.get("reference"))
```
`reference` is required and must be the UUID used to create the payment.
### Example Response
```json theme={"dark"}
{
"success": true,
"message": "Payment status fetched successfully",
"timestamp": "2026-03-31T03:52:50.776Z",
"data": {
"status": "COMPLETED",
"type": "OFFRAMP",
"reference": "ccdd7666-8cb6-45d6-9caf-498b94d10929",
"rate": 1408.761,
"source": {
"amount": 1.745636,
"network": "BASE",
"currency": "USDC"
},
"destination": {
"amount": 2459.183917,
"network": "FIAT",
"currency": "NGN"
},
"deposit": {
"amount": 1.745636,
"address": "0x73E60786CA161d1BfabbaF7c7d467a0A677997aC",
"asset": "base:usdc",
"note": [
"Kindly send the exact amount to the wallet address to complete the transaction.",
"This dynamic wallet address has a 30 minutes expiry window and can only be used once."
]
}
}
}
```
**`status`** may be values such as `AWAITING_DEPOSIT`, `PROCESSING`, `COMPLETED`, `FAILED`, `REVERSED`, `SCHEDULED`, depending on the state of the transaction.
## Need Help?
Send us a message.
Hop on a call with us.
# Create stablecoin wallet
Source: https://docs.onswitch.xyz/guides/create-wallet
Create a unified balance, non-custodial stablecoin wallet.
**POST `/wallet/create`** creates a **non-custodial** stablecoin wallet: one unified balance across supported chains, with deposit addresses per network. You receive the **`private_key`** only in this response—store it securely; Switch cannot show it again later except via **POST `/wallet/export`** when you explicitly request recovery.
Use **GET `/asset`** to see which stablecoins support the wallet product (`wallet_supported`). See [Get stablecoins](/guides/get-stablecoins).
## Example request
```bash cURL theme={"dark"}
curl https://api.onswitch.xyz/wallet/create \
--request POST \
--header 'Content-Type: application/json' \
--header 'x-service-key: '"$YOUR_SERVICE_KEY" \
--data '{
"name": "My Wallet",
"callback_url": "https://your-app.com/webhook"
}'
```
```javascript Node.js theme={"dark"}
const res = await fetch('https://api.onswitch.xyz/wallet/create', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-service-key': process.env.YOUR_SERVICE_KEY,
},
body: JSON.stringify({
name: 'My Wallet',
callback_url: 'https://your-app.com/webhook',
}),
});
const json = await res.json();
const data = json.data ?? {};
console.log(data.id, Object.keys(data.address ?? {}));
```
```python Python theme={"dark"}
import json
import os
import urllib.request
payload = {
"name": "My Wallet",
"callback_url": "https://your-app.com/webhook",
}
req = urllib.request.Request(
"https://api.onswitch.xyz/wallet/create",
method="POST",
data=json.dumps(payload).encode("utf-8"),
headers={
"Content-Type": "application/json",
"x-service-key": os.environ["YOUR_SERVICE_KEY"],
},
)
with urllib.request.urlopen(req, timeout=60) as r:
out = json.load(r)
data = out.get("data") or {}
print(data.get("id"), list((data.get("address") or {}).keys()))
```
## Frequently Asked Questions
Set **`callback_url`** to receive wallet-related notifications at your server. For general webhook behaviour and security practices, see [Webhooks](/webhook).
```json theme={"dark"}
{
"callback_url": "https://your-app.com/webhook"
}
```
After creation, store only **`data.id`** (for subsequent wallet related API calls) and **`data.private_key`** (for custody and signing) using your own secure storage. Store **`data.address`** if you want to show per-chain deposit addresses without calling **GET `/wallet/{wallet_id}`** again.
### Example response
```json theme={"dark"}
{
"success": true,
"message": "Wallet created successfully",
"timestamp": "2026-04-01T12:00:00.000Z",
"data": {
"id": "699a6555fd4cab59e6175f79",
"name": "My Wallet",
"private_key": "0xb8ce59fc3717ada4c02eadf9682a9e934f625ebb",
"address": {
"BASE": "0xA3B3b28d8E3ec225f555f4AB9fC3607De545Ff49",
"ETHEREUM": "0xA3B3b28d8E3ec225f555f4AB9fC3607De545Ff49",
"POLYGON": "0xA3B3b28d8E3ec225f555f4AB9fC3607De545Ff49",
"BSC": "0xA3B3b28d8E3ec225f555f4AB9fC3607De545Ff49",
"ARBITRUM": "0xA3B3b28d8E3ec225f555f4AB9fC3607De545Ff49",
"OPTIMISM": "0xA3B3b28d8E3ec225f555f4AB9fC3607De545Ff49",
"GNOSIS": "0xA3B3b28d8E3ec225f555f4AB9fC3607De545Ff49",
"AVALANCHE": "0xA3B3b28d8E3ec225f555f4AB9fC3607De545Ff49",
"MONAD": "0xA3B3b28d8E3ec225f555f4AB9fC3607De545Ff49",
"PLASMA": "0xA3B3b28d8E3ec225f555f4AB9fC3607De545Ff49",
"LINEA": "0xA3B3b28d8E3ec225f555f4AB9fC3607De545Ff49",
"MANTLE": "0xA3B3b28d8E3ec225f555f4AB9fC3607De545Ff49",
"HYPEREVM": "0xA3B3b28d8E3ec225f555f4AB9fC3607De545Ff49",
"BERACHAIN": "0xA3B3b28d8E3ec225f555f4AB9fC3607De545Ff49",
"SONIC": "0xA3B3b28d8E3ec225f555f4AB9fC3607De545Ff49",
"SOLANA": "2dTHot2BVjqqGD3S5Z2bzEVYnyEcJCdr9SawzqTdKo4h"
},
"note": "Do not share your private key. This key grants full control of your account."
}
}
```
## Security
The **`private_key`** is returned **only when the wallet is created**. Anyone with the key can move funds. Do not log it, embed it in client-side code, or send it over insecure channels. If you lose it and never exported a backup, recovery may not be possible.
## Need Help?
Send us a message.
Hop on a call with us.
# Get currencies
Source: https://docs.onswitch.xyz/guides/get-currencies
List supported local currencies, channels and limits.
**GET `/coverage`** returns the supported local currencies you can use convert to or from stablecoin. Shows metadata such as country (ISO code), channels, the default channel, typical settlement times, transaction limits etc.
## Example Request
```bash cURL theme={"dark"}
curl -L \
--request GET \
--url 'https://api.onswitch.xyz/coverage?direction=OFFRAMP' \
--header 'x-service-key: '"$YOUR_SERVICE_KEY" \
--header 'Accept: */*'
```
```javascript Node.js theme={"dark"}
const params = new URLSearchParams({ direction: 'OFFRAMP' });
const res = await fetch(`https://api.onswitch.xyz/coverage?${params}`, {
method: 'GET',
headers: {
'x-service-key': process.env.YOUR_SERVICE_KEY,
Accept: '*/*',
},
});
const json = await res.json();
console.log(json.data?.length, 'corridors');
```
```python Python theme={"dark"}
import os, json, urllib.parse, urllib.request
q = urllib.parse.urlencode({"direction": "OFFRAMP"})
req = urllib.request.Request(
f"https://api.onswitch.xyz/coverage?{q}",
method="GET",
headers={
"x-service-key": os.environ["YOUR_SERVICE_KEY"],
"Accept": "*/*",
},
)
with urllib.request.urlopen(req, timeout=60) as r:
payload = json.load(r)
print(len(payload.get("data", [])), "corridors")
```
Direction can be `ONRAMP` or `OFFRAMP`. `ONRAMP` returns currencies that can be converted to stablecoin. `OFFRAMP` returns currencies that stablecoin can be converted to.
### Example Response
```json theme={"dark"}
{
"success": true,
"message": "Coverage fetched successfully",
"timestamp": "2026-08-04T10:44:30.935Z",
"data": [
{
"country": "NG",
"currency": ["NGN"],
"continent": "AFRICA",
"default_channel": "BANK",
"channel": ["BANK"],
"direction": ["ONRAMP", "OFFRAMP"],
"settlement_time": {
"BANK": "30-120 seconds",
"MOBILEMONEY": "5-10 minutes"
},
"payout_limit": {
"BANK": { "min": "$1", "max": "$100,000" },
"note": "This represents the maximum USD value allowed per transaction."
}
},
{
"country": "GH",
"currency": ["GHS"],
"continent": "AFRICA",
"default_channel": "MOBILEMONEY",
"channel": ["MOBILEMONEY"],
"direction": ["OFFRAMP", "ONRAMP"],
"settlement_time": {
"MOBILEMONEY": "5-10 minutes"
},
"payout_limit": {
"MOBILEMONEY": { "min": "$10", "max": "$100,000" },
"note": "This represents the maximum USD value allowed per transaction."
}
},
{
"country": "KE",
"currency": ["KES"],
"continent": "AFRICA",
"default_channel": "MOBILEMONEY",
"channel": ["MOBILEMONEY", "BANK"],
"direction": ["OFFRAMP", "ONRAMP"],
"settlement_time": {
"BANK": "5-10 minutes",
"MOBILEMONEY": "5-10 minutes"
},
"payout_limit": {
"MOBILEMONEY": { "min": "$10", "max": "$100,000" },
"BANK": { "min": "$10", "max": "$100,000" },
"note": "This represents the maximum USD value allowed per transaction."
}
},
{
"country": "ZA",
"currency": ["ZAR"],
"continent": "AFRICA",
"default_channel": "BANK",
"channel": ["BANK"],
"direction": ["OFFRAMP"],
"settlement_time": {
"BANK": "5-10 minutes"
},
"payout_limit": {
"BANK": { "min": "$100", "max": "$100,000" },
"note": "This represents the maximum USD value allowed per transaction."
}
},
{
"country": "CD",
"currency": ["CDF"],
"continent": "AFRICA",
"default_channel": "MOBILEMONEY",
"channel": ["MOBILEMONEY"],
"direction": ["OFFRAMP", "ONRAMP"],
"settlement_time": {
"MOBILEMONEY": "5-10 minutes"
},
"payout_limit": {
"MOBILEMONEY": { "min": "$10", "max": "$100,000" },
"note": "This represents the maximum USD value allowed per transaction."
}
},
{
"country": "GN",
"currency": ["GNF"],
"continent": "AFRICA",
"default_channel": "MOBILEMONEY",
"channel": ["MOBILEMONEY"],
"direction": ["OFFRAMP"],
"settlement_time": {
"MOBILEMONEY": "5-10 minutes"
},
"payout_limit": {
"MOBILEMONEY": { "min": "$10", "max": "$100,000" },
"note": "This represents the maximum USD value allowed per transaction."
}
},
{
"country": "UG",
"currency": ["UGX"],
"continent": "AFRICA",
"default_channel": "MOBILEMONEY",
"channel": ["MOBILEMONEY"],
"direction": ["OFFRAMP", "ONRAMP"],
"settlement_time": {
"MOBILEMONEY": "5-10 minutes"
},
"payout_limit": {
"MOBILEMONEY": { "min": "$10", "max": "$100,000" },
"note": "This represents the maximum USD value allowed per transaction."
}
},
{
"country": "ET",
"currency": ["ETB"],
"continent": "AFRICA",
"default_channel": "MOBILEMONEY",
"channel": ["MOBILEMONEY"],
"direction": ["OFFRAMP"],
"settlement_time": {
"MOBILEMONEY": "5-10 minutes"
},
"payout_limit": {
"MOBILEMONEY": { "min": "$10", "max": "$100,000" },
"note": "This represents the maximum USD value allowed per transaction."
}
},
{
"country": "CI",
"currency": ["XOF"],
"continent": "AFRICA",
"default_channel": "MOBILEMONEY",
"channel": ["MOBILEMONEY"],
"direction": ["OFFRAMP", "ONRAMP"],
"settlement_time": {
"MOBILEMONEY": "5-10 minutes"
},
"payout_limit": {
"MOBILEMONEY": { "min": "$1", "max": "$100,000" },
"note": "This represents the maximum USD value allowed per transaction."
}
},
{
"country": "CM",
"currency": ["XAF"],
"continent": "AFRICA",
"default_channel": "MOBILEMONEY",
"channel": ["MOBILEMONEY"],
"direction": ["OFFRAMP", "ONRAMP"],
"settlement_time": {
"MOBILEMONEY": "5-10 minutes"
},
"payout_limit": {
"MOBILEMONEY": { "min": "$10", "max": "$100,000" },
"note": "This represents the maximum USD value allowed per transaction."
}
},
{
"country": "SN",
"currency": ["XOF"],
"continent": "AFRICA",
"default_channel": "MOBILEMONEY",
"channel": ["MOBILEMONEY"],
"direction": ["OFFRAMP", "ONRAMP"],
"settlement_time": {
"MOBILEMONEY": "5-10 minutes"
},
"payout_limit": {
"MOBILEMONEY": { "min": "$10", "max": "$100,000" },
"note": "This represents the maximum USD value allowed per transaction."
}
},
{
"country": "RW",
"currency": ["RWF"],
"continent": "AFRICA",
"default_channel": "MOBILEMONEY",
"channel": ["MOBILEMONEY"],
"direction": ["OFFRAMP", "ONRAMP"],
"settlement_time": {
"MOBILEMONEY": "5-10 minutes"
},
"payout_limit": {
"MOBILEMONEY": { "min": "$10", "max": "$100,000" },
"note": "This represents the maximum USD value allowed per transaction."
}
},
{
"country": "BJ",
"currency": ["XOF"],
"continent": "AFRICA",
"default_channel": "MOBILEMONEY",
"channel": ["MOBILEMONEY"],
"direction": ["OFFRAMP", "ONRAMP"],
"settlement_time": {
"MOBILEMONEY": "5-10 minutes"
},
"payout_limit": {
"MOBILEMONEY": { "min": "$10", "max": "$100,000" },
"note": "This represents the maximum USD value allowed per transaction."
}
},
{
"country": "GA",
"currency": ["XAF"],
"continent": "AFRICA",
"default_channel": "MOBILEMONEY",
"channel": ["MOBILEMONEY"],
"direction": ["OFFRAMP"],
"settlement_time": {
"MOBILEMONEY": "5-10 minutes"
},
"payout_limit": {
"MOBILEMONEY": { "min": "$10", "max": "$100,000" },
"note": "This represents the maximum USD value allowed per transaction."
}
},
{
"country": "ML",
"currency": ["XOF"],
"continent": "AFRICA",
"default_channel": "MOBILEMONEY",
"channel": ["MOBILEMONEY"],
"direction": ["OFFRAMP", "ONRAMP"],
"settlement_time": {
"MOBILEMONEY": "5-10 minutes"
},
"payout_limit": {
"MOBILEMONEY": { "min": "$10", "max": "$100,000" },
"note": "This represents the maximum USD value allowed per transaction."
}
},
{
"country": "TZ",
"currency": ["TZS"],
"continent": "AFRICA",
"default_channel": "MOBILEMONEY",
"channel": ["MOBILEMONEY", "BANK"],
"direction": ["OFFRAMP", "ONRAMP"],
"settlement_time": {
"MOBILEMONEY": "5-10 minutes",
"BANK": "5-10 minutes"
},
"payout_limit": {
"MOBILEMONEY": { "min": "$10", "max": "$100,000" },
"BANK": { "min": "$10", "max": "$100,000" },
"note": "This represents the maximum USD value allowed per transaction."
}
},
{
"country": "ZM",
"currency": ["ZMW"],
"continent": "AFRICA",
"default_channel": "MOBILEMONEY",
"channel": ["MOBILEMONEY"],
"direction": ["OFFRAMP", "ONRAMP"],
"settlement_time": {
"MOBILEMONEY": "5-10 minutes"
},
"payout_limit": {
"MOBILEMONEY": { "min": "$10", "max": "$100,000" },
"note": "This represents the maximum USD value allowed per transaction."
}
},
{
"country": "SL",
"currency": ["SLE"],
"continent": "AFRICA",
"default_channel": "MOBILEMONEY",
"channel": ["MOBILEMONEY"],
"direction": ["OFFRAMP", "ONRAMP"],
"settlement_time": {
"MOBILEMONEY": "5-10 minutes"
},
"payout_limit": {
"MOBILEMONEY": { "min": "$10", "max": "$100,000" },
"note": "This represents the maximum USD value allowed per transaction."
}
},
{
"country": "MW",
"currency": ["MWK"],
"continent": "AFRICA",
"default_channel": "MOBILEMONEY",
"channel": ["MOBILEMONEY"],
"direction": ["OFFRAMP", "ONRAMP"],
"settlement_time": {
"MOBILEMONEY": "5-10 minutes"
},
"payout_limit": {
"MOBILEMONEY": { "min": "$10", "max": "$100,000" },
"note": "This represents the maximum USD value allowed per transaction."
}
},
{
"country": "LR",
"currency": ["LRD"],
"continent": "AFRICA",
"default_channel": "MOBILEMONEY",
"channel": ["MOBILEMONEY"],
"direction": ["OFFRAMP", "ONRAMP"],
"settlement_time": {
"MOBILEMONEY": "5-10 minutes"
},
"payout_limit": {
"MOBILEMONEY": { "min": "$10", "max": "$100,000" },
"note": "This represents the maximum USD value allowed per transaction."
}
},
{
"country": "GM",
"currency": ["GMD"],
"continent": "AFRICA",
"default_channel": "MOBILEMONEY",
"channel": ["MOBILEMONEY"],
"direction": ["OFFRAMP", "ONRAMP"],
"settlement_time": {
"MOBILEMONEY": "5-10 minutes"
},
"payout_limit": {
"MOBILEMONEY": { "min": "$10", "max": "$100,000" },
"note": "This represents the maximum USD value allowed per transaction."
}
}
]
}
```
**`payout_limit`** represents the maximum USD value that is allowed in a single transaction and varies by channel and currency.
## Need Help?
Send us a message.
Hop on a call with us.
# Get quote
Source: https://docs.onswitch.xyz/guides/get-quote
Get a priced off-ramp quote with source and destination amounts
**POST `/offramp/quote`** endpoint returns a quote for converting a supported stablecoin to local currency, including the applied rate, how much the user sends and receives, settlement timing, and when the quote expires.
## Example Request
```bash cURL theme={"dark"}
curl -L \
--request POST \
--url 'https://api.onswitch.xyz/offramp/quote' \
--header 'x-service-key: YOUR_SERVICE_KEY' \
--header 'Content-Type: application/json' \
--data '{
"amount": 100,
"country": "NG",
"asset": "base:usdc",
"currency": "NGN",
"channel": "BANK",
"exact_output": false,
"developer_fee": 0.5
}'
```
```javascript Node.js theme={"dark"}
const res = await fetch('https://api.onswitch.xyz/offramp/quote', {
method: 'POST',
headers: {
'x-service-key': process.env.YOUR_SERVICE_KEY,
'Content-Type': 'application/json',
},
body: JSON.stringify({
amount: 100,
country: 'NG',
asset: 'base:usdc',
currency: 'NGN',
channel: 'BANK',
exact_output: false,
developer_fee: 0.5,
developer_recipient: '0x1234567890123456789012345678901234567890',
}),
});
const body = await res.json();
const { data } = body;
console.log(
`${data.source.amount} ${data.source.currency} → ${data.destination.amount} ${data.destination.currency} (rate ${data.rate}, expires ${data.expiry})`
);
```
```python Python theme={"dark"}
import os, json, urllib.request
payload = {
"amount": 100,
"country": "NG",
"asset": "base:usdc",
"currency": "NGN",
"channel": "BANK",
"exact_output": False,
"developer_fee": 0.5,
"developer_recipient": "0x1234567890123456789012345678901234567890",
}
req = urllib.request.Request(
"https://api.onswitch.xyz/offramp/quote",
data=json.dumps(payload).encode(),
method="POST",
headers={
"x-service-key": os.environ["YOUR_SERVICE_KEY"],
"Content-Type": "application/json",
},
)
with urllib.request.urlopen(req, timeout=30) as r:
body = json.load(r)
d = body["data"]
print(
f"{d['source']['amount']} {d['source']['currency']} → "
f"{d['destination']['amount']} {d['destination']['currency']} "
f"(rate {d['rate']}, expires {d['expiry']})"
)
```
`exact_output` (optional, default `false`) controls how `amount` is interpreted.
With `false`, `amount` is the stablecoin you send (`asset`); i.e. the quote computes how much local currency (`currency`) the user receives.
With `true`, `amount` is the local currency to deliver; i.e. the quote computes how much stablecoin must be sent.
`developer_fee` (optional) is a percentage between 0 and 100 taken as your fee from the payment amount.
Omit the fee or set it to 0 if you are not charging your own fee.
### Example Response
```json theme={"dark"}
{
"success": true,
"message": "Offramp quote fetched successfully",
"timestamp": "2026-03-31T03:05:43.257Z",
"data": {
"expiry": "2026-03-31T04:10:43+01:00",
"settlement": "5-10 minutes",
"channel": "BANK",
"rate": 1409.758,
"source": {
"amount": 100,
"currency": "USDC",
"network": "BASE"
},
"destination": {
"amount": 140975.8,
"currency": "NGN",
"network": "FIAT"
}
}
}
```
Quotes expire at `data.expiry`. Market conditions can change; create the transaction promptly after fetching a quote, or request a fresh quote if it has expired.
## Need Help?
Send us a message.
Hop on a call with us.
# Get rate
Source: https://docs.onswitch.xyz/guides/get-rate
Fetch rate for stablecoin to local currency conversion.
**POST `/offramp/rate`** endpoint returns the current exchange rate for converting a supported stablecoin to a local currency. Call this endpoint before creating an actual payment transaction to show users exactly how much they'll receive.
## Example Request
```bash cURL theme={"dark"}
curl -L \
--request POST \
--url 'https://api.onswitch.xyz/offramp/rate' \
--header 'x-service-key: '"$YOUR_SERVICE_KEY" \
--header 'Content-Type: application/json' \
--data '{
"asset": "base:usdc",
"country": "NG",
"currency": "NGN"
}'
```
```javascript Node.js theme={"dark"}
const res = await fetch('https://api.onswitch.xyz/offramp/rate', {
method: 'POST',
headers: {
'x-service-key': process.env.YOUR_SERVICE_KEY,
'Content-Type': 'application/json',
},
body: JSON.stringify({
asset: 'base:usdc',
country: 'NG',
currency: 'NGN',
}),
});
const { rate } = await res.json();
console.log(`1 USDC = ${rate} NGN`);
```
```python Python theme={"dark"}
import os, json, urllib.request
payload = {
"asset": "base:usdc",
"country": "NG",
"currency": "NGN",
}
req = urllib.request.Request(
"https://api.onswitch.xyz/offramp/rate",
data=json.dumps(payload).encode(),
method="POST",
headers={
"x-service-key": os.environ["YOUR_SERVICE_KEY"],
"Content-Type": "application/json",
},
)
with urllib.request.urlopen(req, timeout=30) as r:
data = json.load(r)
print(f"1 USDC = {data['rate']} NGN")
```
### Example Response
```json theme={"dark"}
{
"success": true,
"message": "Offramp rate fetched successfully",
"timestamp": "2024-01-01T00:00:00.000Z",
"data": {
"currency": "NGN",
"channel": "BANK",
"rate": 1408.761
}
}
```
Rates reflect current market conditions and may change between the time you fetch them and when a transaction is submitted. Always re-fetch the rate immediately before creating a transaction.
## Need Help?
Send us a message.
Hop on a call with us.
# Get stablecoins
Source: https://docs.onswitch.xyz/guides/get-stablecoins
List all supported stablecoins and metadata.
**GET `/asset`** lists every stablecoin supported across all networks, including metadata such as token address, blockchain, decimals etc. and which payment flows (offramp, onramp, swap, wallet) each stablecoin supports.
## Example Request
```bash cURL theme={"dark"}
curl -L \
--request GET \
--url 'https://api.onswitch.xyz/asset' \
--header 'x-service-key: '"$YOUR_SERVICE_KEY" \
--header 'Accept: */*'
```
```javascript Node.js theme={"dark"}
const res = await fetch('https://api.onswitch.xyz/asset', {
method: 'GET',
headers: {
'x-service-key': process.env.YOUR_SERVICE_KEY,
Accept: '*/*',
},
});
const json = await res.json();
console.log(json.data?.length, 'assets');
```
```python Python theme={"dark"}
import os, json, urllib.request
req = urllib.request.Request(
"https://api.onswitch.xyz/asset",
method="GET",
headers={
"x-service-key": os.environ["YOUR_SERVICE_KEY"],
"Accept": "*/*",
},
)
with urllib.request.urlopen(req, timeout=60) as r:
payload = json.load(r)
print(len(payload.get("data", [])), "assets")
```
### Example Response
```json theme={"dark"}
{
"success": true,
"message": "Assets fetched successfully",
"timestamp": "2026-03-31T03:15:12.304Z",
"data": [
{
"id": "base:usdc",
"name": "USD Coin",
"code": "USDC",
"decimals": 6,
"address": "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913",
"blockchain": {
"id": 8453,
"name": "BASE",
"type": "EVM"
},
"offramp_supported": true,
"onramp_supported": true,
"swap_supported": true,
"wallet_supported": true
},
{
"id": "solana:usdc",
"name": "USD Coin",
"code": "USDC",
"decimals": 6,
"address": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
"blockchain": {
"id": 10000,
"name": "SOLANA",
"type": "SVM"
},
"offramp_supported": true,
"onramp_supported": true,
"swap_supported": true,
"wallet_supported": true
}
]
}
```
For a readable reference table, see [Stablecoins](/stablecoins).
## Need Help?
Send us a message.
Hop on a call with us.
# Stablecoin to GHS
Source: https://docs.onswitch.xyz/guides/stablecoin-to-ghs
Convert stablecoin (USDC, USDT) to Ghanaian Cedi.
**POST `/offramp/initiate`** endpoint creates an off-ramp transaction where the customer sends stablecoin and receives Ghanaian Cedi (GHS) instantly.
Switch returns a deposit address — once the customer sends the amount in the speicifed asset to that address, the GHS payout is processed automatically.
Before calling this endpoint to create a payment, call `GET /requirement` to
retrieve the required beneficiary fields based on the country and channel.
## Example request
```bash cURL theme={"dark"}
curl https://api.onswitch.xyz/offramp/initiate \
--request POST \
--header 'Content-Type: application/json' \
--header 'x-service-key: '"$YOUR_SERVICE_KEY" \
--data '{
"country": "GH",
"currency": "GHS",
"amount": 12,
"asset": "base:usdc",
"narration": "Mobile money transfer to John Doe",
"channel": "MOBILEMONEY",
"beneficiary": {
"mobile_network": "MTN",
"mobile_number": "0555927608",
"holder_name": "John Doe",
"holder_type": "INDIVIDUAL"
}
}'
```
```javascript Node.js theme={"dark"}
const res = await fetch('https://api.onswitch.xyz/offramp/initiate', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-service-key': process.env.YOUR_SERVICE_KEY,
},
body: JSON.stringify({
country: 'GH',
currency: 'GHS',
amount: 12,
asset: 'base:usdc',
narration: 'Mobile money transfer to John Doe',
channel: 'MOBILEMONEY',
beneficiary: {
mobile_network: 'MTN',
mobile_number: '0555927608',
holder_name: 'John Doe',
holder_type: 'INDIVIDUAL',
},
}),
});
const json = await res.json();
console.log(json.data?.reference, json.data?.status);
```
```python Python theme={"dark"}
import json
import os
import urllib.request
payload = {
"country": "GH",
"currency": "GHS",
"amount": 12,
"asset": "base:usdc",
"narration": "Mobile money transfer to John Doe",
"channel": "MOBILEMONEY",
"beneficiary": {
"mobile_network": "MTN",
"mobile_number": "0555927608",
"holder_name": "John Doe",
"holder_type": "INDIVIDUAL",
},
}
req = urllib.request.Request(
"https://api.onswitch.xyz/offramp/initiate",
method="POST",
data=json.dumps(payload).encode("utf-8"),
headers={
"Content-Type": "application/json",
"x-service-key": os.environ["YOUR_SERVICE_KEY"],
},
)
with urllib.request.urlopen(req, timeout=60) as r:
out = json.load(r)
data = out.get("data") or {}
print(data.get("reference"), data.get("status"))
```
## Frequently Asked Questions
Add exact\_output and amount if you want an exact amount to be received at the destination. e.g; if you want 10,000 GHS
```json theme={"dark"}
{
"exact_output": true,
"amount": 10000
}
```
Add callback\_url to set the URL to receive webhook notifications when the payment status changes.
```json theme={"dark"}
{
"callback_url": "https://your-webhook-url.com"
}
```
Add developer\_fee and developer\_recipient to set the developer fee you wish to charge from the payment amount.
```json theme={"dark"}
{
"developer_fee": 0.5,
"developer_recipient": "0x1234567890123456789012345678901234567890"
}
```
Include **`holder_type`** on **`beneficiary`** depending on the payout recipient; if Business, set to **`BUSINESS`** else **`INDIVIDUAL`**.
```json theme={"dark"}
{
"beneficiary": {
"holder_type": "INDIVIDUAL"
}
}
```
Add reason to set the purpose of the payment. This is mostly required for transfers out of Nigeria.
```json theme={"dark"}
{
"reason": "CAPITAL_CONTRIBUTIONS"
}
```
Allowed values are listed under [Reason](/reasons).
Add refund\_address to set the wallet address to refund the stablecoin deposit to if the payment fails for any reason.
```json theme={"dark"}
{
"refund_address": "0x1234567890123456789012345678901234567890"
}
```
Add channel to set the specific channel or settlement method to use for the payment, if not set, the default channel for the country will be used.
```json theme={"dark"}
{
"channel": "MOBILEMONEY"
}
```
Allowed values are listed under [Countries](/countries).
Add static if you want to always generate the same deposit address for the same local currency beneficiary details.
```json theme={"dark"}
{
"static": true
}
```
### Example response
```json theme={"dark"}
{
"success": true,
"message": "Offramp initiated successfully",
"timestamp": "2026-03-31T04:03:15.931Z",
"data": {
"status": "AWAITIGH_DEPOSIT",
"type": "OFFRAMP",
"reference": "6c01338d-67e5-4d0c-a394-9d3e367f46a5",
"rate": 11.440874,
"source": {
"amount": 12,
"currency": "USDC",
"network": "BASE"
},
"destination": {
"amount": 137.290488,
"currency": "GHS",
"network": "FIAT"
},
"deposit": {
"amount": 12,
"address": "0xcd257a5867B42556fa14c35de714154B277f70fD",
"asset": "base:usdc",
"note": [
"Kindly send the exact amount to the wallet address to complete the transaction.",
"This dynamic wallet address has a 30 minutes expiry window and can only be used once."
]
}
}
}
```
## Completing the Deposit
After initiating, you are expected to send **exactly** **`deposit.amount`** of **`deposit.asset`** to **`deposit.address`**. Check **`deposit.note`** for specific instructions.
The deposit address is for **one-time use only** and expires after **30
minutes**. if not created with **`static`** set to **`true`**.
If less or more amount than specified in **`deposit.amount`** is sent, the deposit will be processed based on the amount sent, so long as it is within the limits and the asset (**`deposit.asset`**) sent is supported.
## Need Help?
Send us a message.
Hop on a call with us.
# Stablecoin to KES
Source: https://docs.onswitch.xyz/guides/stablecoin-to-kes
Convert stablecoin (USDC, USDT) to Kenyan shilling.
**POST `/offramp/initiate`** endpoint creates an off-ramp transaction where the customer sends stablecoin and receives Kenyan shilling (KES) instantly.
Switch returns a deposit address — once the customer sends the amount in the specified asset to that address, the KES payout is processed automatically.
Before calling this endpoint to create a payment, call `GET /requirement` to
retrieve the required beneficiary fields based on the country and channel.
## Example request
```bash cURL theme={"dark"}
curl https://api.onswitch.xyz/offramp/initiate \
--request POST \
--header 'Content-Type: application/json' \
--header 'x-service-key: '"$YOUR_SERVICE_KEY" \
--data '{
"country": "KE",
"currency": "KES",
"amount": 12,
"asset": "base:usdc",
"narration": "Mobile money transfer to John Doe",
"channel": "MOBILEMONEY",
"beneficiary": {
"mobile_network": "MPESA",
"mobile_number": "254712345678",
"holder_name": "John Doe",
"holder_type": "INDIVIDUAL"
}
}'
```
```javascript Node.js theme={"dark"}
const res = await fetch('https://api.onswitch.xyz/offramp/initiate', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-service-key': process.env.YOUR_SERVICE_KEY,
},
body: JSON.stringify({
country: 'KE',
currency: 'KES',
amount: 12,
asset: 'base:usdc',
narration: 'Mobile money transfer to John Doe',
channel: 'MOBILEMONEY',
beneficiary: {
mobile_network: 'MPESA',
mobile_number: '254712345678',
holder_name: 'John Doe',
holder_type: 'INDIVIDUAL',
},
}),
});
const json = await res.json();
console.log(json.data?.reference, json.data?.status);
```
```python Python theme={"dark"}
import json
import os
import urllib.request
payload = {
"country": "KE",
"currency": "KES",
"amount": 12,
"asset": "base:usdc",
"narration": "Mobile money transfer to John Doe",
"channel": "MOBILEMONEY",
"beneficiary": {
"mobile_network": "MPESA",
"mobile_number": "254712345678",
"holder_name": "John Doe",
"holder_type": "INDIVIDUAL",
},
}
req = urllib.request.Request(
"https://api.onswitch.xyz/offramp/initiate",
method="POST",
data=json.dumps(payload).encode("utf-8"),
headers={
"Content-Type": "application/json",
"x-service-key": os.environ["YOUR_SERVICE_KEY"],
},
)
with urllib.request.urlopen(req, timeout=60) as r:
out = json.load(r)
data = out.get("data") or {}
print(data.get("reference"), data.get("status"))
```
## Frequently Asked Questions
Add exact\_output and amount if you want an exact amount to be received at the destination. e.g; if you want 10,000 KES
```json theme={"dark"}
{
"exact_output": true,
"amount": 10000
}
```
Add callback\_url to set the URL to receive webhook notifications when the payment status changes.
```json theme={"dark"}
{
"callback_url": "https://your-webhook-url.com"
}
```
Add developer\_fee and developer\_recipient to set the developer fee you wish to charge from the payment amount.
```json theme={"dark"}
{
"developer_fee": 0.5,
"developer_recipient": "0x1234567890123456789012345678901234567890"
}
```
Include **`holder_type`** on **`beneficiary`** depending on the payout recipient; if Business, set to **`BUSINESS`** else **`INDIVIDUAL`**.
```json theme={"dark"}
{
"beneficiary": {
"holder_type": "INDIVIDUAL"
}
}
```
Add reason to set the purpose of the payment. This is mostly required for transfers out of Nigeria.
```json theme={"dark"}
{
"reason": "CAPITAL_CONTRIBUTIONS"
}
```
Allowed values are listed under [Reason](/reasons).
Add refund\_address to set the wallet address to refund the stablecoin deposit to if the payment fails for any reason.
```json theme={"dark"}
{
"refund_address": "0x1234567890123456789012345678901234567890"
}
```
Add channel to set the specific channel or settlement method to use for the payment, if not set, the default channel for the country will be used.
```json theme={"dark"}
{
"channel": "MOBILEMONEY"
}
```
Allowed values are listed under [Countries](/countries).
Add static if you want to always generate the same deposit address for the same local currency beneficiary details.
```json theme={"dark"}
{
"static": true
}
```
### Example response
```json theme={"dark"}
{
"success": true,
"message": "Offramp initiated successfully",
"timestamp": "2026-03-31T04:03:15.931Z",
"data": {
"status": "AWAITING_DEPOSIT",
"type": "OFFRAMP",
"reference": "6c01338d-67e5-4d0c-a394-9d3e367f46a5",
"rate": 128.5,
"source": {
"amount": 12,
"currency": "USDC",
"network": "BASE"
},
"destination": {
"amount": 1542,
"currency": "KES",
"network": "FIAT"
},
"deposit": {
"amount": 12,
"address": "0xcd257a5867B42556fa14c35de714154B277f70fD",
"asset": "base:usdc",
"note": [
"Kindly send the exact amount to the wallet address to complete the transaction.",
"This dynamic wallet address has a 30 minutes expiry window and can only be used once."
]
}
}
}
```
## Completing the Deposit
After initiating, you are expected to send **exactly** **`deposit.amount`** of **`deposit.asset`** to **`deposit.address`**. Check **`deposit.note`** for specific instructions.
The deposit address is for **one-time use only** and expires after **30
minutes**. if not created with **`static`** set to **`true`**.
If less or more amount than specified in **`deposit.amount`** is sent, the deposit will be processed based on the amount sent, so long as it is within the limits and the asset (**`deposit.asset`**) sent is supported.
## Need Help?
Send us a message.
Hop on a call with us.
# Stablecoin to NGN
Source: https://docs.onswitch.xyz/guides/stablecoin-to-ngn
Convert stablecoin (USDC, USDT) to Nigerian naira.
**POST `/offramp/initiate`** endpoint creates an off-ramp transaction where the customer sends stablecoin and receives Nigerian Naira (NGN) instantly.
Switch returns a deposit address — once the customer sends the amount in the speicifed asset to that address, the NGN payout is processed automatically.
Before calling this endpoint to create a payment, call `GET /requirement` to
retrieve the required beneficiary fields based on the country and channel.
## Example request
```bash cURL theme={"dark"}
curl https://api.onswitch.xyz/offramp/initiate \
--request POST \
--header 'Content-Type: application/json' \
--header 'x-service-key: '"$YOUR_SERVICE_KEY" \
--data '{
"country": "NG",
"currency": "NGN",
"amount": 10,
"asset": "base:usdc",
"narration": "Bank transfer to John Doe",
"channel": "BANK",
"beneficiary": {
"bank_code": "100004",
"account_number": "8188216769",
"holder_name": "John Doe",
"holder_type": "INDIVIDUAL"
}
}'
```
```javascript Node.js theme={"dark"}
const res = await fetch('https://api.onswitch.xyz/offramp/initiate', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-service-key': process.env.YOUR_SERVICE_KEY,
},
body: JSON.stringify({
country: 'NG',
currency: 'NGN',
amount: 10,
asset: 'base:usdc',
narration: 'Bank transfer to John Doe',
channel: 'BANK',
beneficiary: {
bank_code: '100004',
account_number: '8188216769',
holder_name: 'John Doe',
holder_type: 'INDIVIDUAL',
},
}),
});
const json = await res.json();
console.log(json.data?.reference, json.data?.status);
```
```python Python theme={"dark"}
import json
import os
import urllib.request
payload = {
"country": "NG",
"currency": "NGN",
"amount": 10,
"asset": "base:usdc",
"narration": "Bank transfer to John Doe",
"channel": "BANK",
"beneficiary": {
"bank_code": "100004",
"account_number": "8188216769",
"holder_name": "John Doe",
"holder_type": "INDIVIDUAL",
},
}
req = urllib.request.Request(
"https://api.onswitch.xyz/offramp/initiate",
method="POST",
data=json.dumps(payload).encode("utf-8"),
headers={
"Content-Type": "application/json",
"x-service-key": os.environ["YOUR_SERVICE_KEY"],
},
)
with urllib.request.urlopen(req, timeout=60) as r:
out = json.load(r)
data = out.get("data") or {}
print(data.get("reference"), data.get("status"))
```
## Frequently Asked Questions
Add exact\_output and amount if you want an exact amount to be received at the destination. e.g; if you want 10,000 NGN
```json theme={"dark"}
{
"exact_output": true,
"amount": 10000
}
```
Add sender\_name to set what the bank transfer will show as the sender name to the recipient. For Nigeria bank transfers only.
```json theme={"dark"}
{
"sender_name": "SwiftPay"
}
```
Add callback\_url to set the URL to receive webhook notifications when the payment status changes.
```json theme={"dark"}
{
"callback_url": "https://your-webhook-url.com"
}
```
Add developer\_fee and developer\_recipient to set the developer fee you wish to charge from the payment amount.
```json theme={"dark"}
{
"developer_fee": 0.5,
"developer_recipient": "0x1234567890123456789012345678901234567890"
}
```
Add refund\_address to set the wallet address to refund the stablecoin deposit to if the payment fails for any reason.
```json theme={"dark"}
{
"refund_address": "0x1234567890123456789012345678901234567890"
}
```
Add channel to set the specific channel or settlement method to use for the payment, if not set, the default channel for the country will be used.
```json theme={"dark"}
{
"channel": "BANK"
}
```
Allowed values are listed under [Countries](/countries).
Add static if you want to always generate the same deposit address for the same local currency beneficiary details.
```json theme={"dark"}
{
"static": true
}
```
### Example response
```json theme={"dark"}
{
"success": true,
"message": "Offramp initiated successfully",
"timestamp": "2026-03-31T04:03:15.931Z",
"data": {
"status": "AWAITING_DEPOSIT",
"type": "OFFRAMP",
"reference": "6c01338d-67e5-4d0c-a394-9d3e367f46a5",
"rate": 1409.758,
"source": {
"amount": 10,
"currency": "USDC",
"network": "BASE"
},
"destination": {
"amount": 14097.58,
"currency": "NGN",
"network": "FIAT"
},
"deposit": {
"amount": 10,
"address": "0xcd257a5867B42556fa14c35de714154B277f70fD",
"asset": "base:usdc",
"note": [
"Kindly send the exact amount to the wallet address to complete the transaction.",
"This dynamic wallet address has a 30 minutes expiry window and can only be used once."
]
}
}
}
```
## Completing the Deposit
After initiating, you are expected to send **exactly** **`deposit.amount`** of **`deposit.asset`** to **`deposit.address`**. Check **`deposit.note`** for specific instructions.
The deposit address is for **one-time use only** and expires after **30
minutes**. if not created with **`static`** set to **`true`**.
If less or more amount than specified in **`deposit.amount`** is sent, the deposit will be processed based on the amount sent, so long as it is within the limits and the asset (**`deposit.asset`**) sent is supported.
## Need Help?
Send us a message.
Hop on a call with us.
# Swap stablecoins
Source: https://docs.onswitch.xyz/guides/swap-stablecoins
Convert between supported stablecoins.
**POST `/swap/initiate`** endpoint creates an swap transaction where the customer sends **`from_asset`** to the generated deposit address, the output is delivered to **`beneficiary.wallet_address`** on the destination (**`to_asset`**).
Use **GET `/quote`** with the same payload (**`from_asset`**, **`to_asset`**, **`amount`**, **`exact_output`**) to preview the output without creating a swap.
## Example request
```bash cURL theme={"dark"}
curl https://api.onswitch.xyz/swap/initiate \
--request POST \
--header 'Content-Type: application/json' \
--header 'x-service-key: '"$YOUR_SERVICE_KEY" \
--data '{
"amount": 100,
"from_asset": "base:usdc",
"to_asset": "bsc:usdt",
"beneficiary": {
"wallet_address": "0x1234567890123456789012345678901234567890"
}
}'
```
```javascript Node.js theme={"dark"}
const res = await fetch('https://api.onswitch.xyz/swap/initiate', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-service-key': process.env.YOUR_SERVICE_KEY,
},
body: JSON.stringify({
amount: 100,
from_asset: 'base:usdc',
to_asset: 'bsc:usdt',
beneficiary: {
wallet_address: '0x1234567890123456789012345678901234567890',
},
}),
});
const json = await res.json();
console.log(json.data?.reference, json.data?.status);
```
```python Python theme={"dark"}
import json
import os
import urllib.request
payload = {
"amount": 100,
"from_asset": "base:usdc",
"to_asset": "bsc:usdt",
"beneficiary": {
"wallet_address": "0x1234567890123456789012345678901234567890",
},
}
req = urllib.request.Request(
"https://api.onswitch.xyz/swap/initiate",
method="POST",
data=json.dumps(payload).encode("utf-8"),
headers={
"Content-Type": "application/json",
"x-service-key": os.environ["YOUR_SERVICE_KEY"],
},
)
with urllib.request.urlopen(req, timeout=60) as r:
out = json.load(r)
data = out.get("data") or {}
print(data.get("reference"), data.get("status"))
```
## Frequently Asked Questions
Add exact\_output and amount if you want an exact amount to be received at the destination. e.g; if you want 100 USDT
```json theme={"dark"}
{
"exact_output": true,
"amount": 100
}
```
Add callback\_url to set the URL to receive webhook notifications when the payment status changes.
```json theme={"dark"}
{
"callback_url": "https://your-webhook-url.com"
}
```
### Example response
```json theme={"dark"}
{
"success": true,
"message": "Swap initiated successfully",
"timestamp": "2026-03-31T06:23:38.535Z",
"data": {
"status": "AWAITING_DEPOSIT",
"type": "SWAP",
"reference": "7ec6514e-d894-474e-87de-a796b1b96771",
"rate": 0.999684,
"source": {
"amount": 100,
"currency": "USDC",
"network": "BASE"
},
"destination": {
"amount": 100.031644952349,
"currency": "USDT",
"network": "BSC"
},
"deposit": {
"amount": 100,
"address": "0x24eAD3fe58DE7DbD031ADddd9f8E6199B9cC691e",
"asset": "base:usdc",
"note": [
"Kindly send the exact amount to the wallet address to complete the transaction.",
"This dynamic wallet address has a 30 minutes expiry window and can only be used once."
]
}
}
}
```
## Completing the Deposit
After initiating, you are expected to send **exactly** **`deposit.amount`** of **`deposit.asset`** to **`deposit.address`**. Check **`deposit.note`** for specific instructions.
The deposit address is for **one-time use only** and expires after **30
minutes**. if not created with **`static`** set to **`true`**.
If less or more amount than specified in **`deposit.amount`** is sent, the deposit will be processed based on the amount sent, so long as it is within the limits and the asset (**`deposit.asset`**) sent is supported.
## Need Help?
Send us a message.
Hop on a call with us.
# Introduction
Source: https://docs.onswitch.xyz/introduction
Stablecoin collection and settlement layer.
Switch connects stablecoin deposits to local payout rails. Your users deposit supported assets such as USDC or USDT; Switch routes funds and settles you in the local currency and payment method that fit your market.
Follow the quickstart to call the API and complete your first stablecoin-to-local-currency flow.
## Why Switch?
Teams choose Switch when they need stablecoin in and local currency out—without owning bank rails, mobile-money integrations, or compliance workflows in every market. You get one API and predictable pricing while we handle routing, settlement paths, and the operational complexity behind payouts.
### Key benefits
Payouts in multiple markets via bank transfer and mobile money, where available.
Short path from accepted stablecoin to local-currency settlement, with transparent pricing on exchange rates.
Designed for regulated flows, including sanctions screening and AML checks as required for your use case.
HTTP APIs, documentation, SDKs, and a sandbox for integration testing.
## Use cases
Let users withdraw proceeds to local bank accounts.
Accept stablecoin from payers and settle merchants in local currency.
Move funds from stablecoin wallets into recipient bank or mobile-money accounts.
Offer stablecoin based financial services with local currency payouts to your users.
## Need Help?
Send us a message
Book a call with us
# Quickstart
Source: https://docs.onswitch.xyz/quickstart
Authenticate with your service key, configure payout beneficiary, and complete your first stablecoin to local currency payout in minutes.
### Step 1: Get your service key
1. Go to [Switch dashboard](https://app.onswitch.xyz/).
2. Create an account or sign in.
3. Go to the API keys section.
4. Copy your **service key** (sandbox or live).
Keep your service key secret. Do not expose or share it publicly.
### Step 2: Fetch beneficiary fields
Before you build your payout form, call **`GET /requirement`** with the country, direction, and channel so you know which fields belong in the `beneficiary` object for **`Initiate a payout`**.
Allowed values are listed under [Countries](/countries).
### Step 3: Initiate a payout
**`Initiate a payout`** creates a payout instance and returns a stabelcoin deposit address. Send exactly **`deposit.amount`** of **`deposit.asset`** to **`deposit.address`** to complete the payment automatically.
See [Stablecoin to NGN](/guides/stablecoin-to-ngn) guide for more details.
## Next steps
You have called the API end-to-end. From here, deepen your integration:
Payout and top-up rails, currencies, and channels by market.
How we handle supported assets and networks.
Subscribe to status updates for payouts and other events.
Step-by-step guides for different payout scenarios.
## Need Help?
Send us a message.
Hop on a call with us.
# Payment reasons
Source: https://docs.onswitch.xyz/reasons
Allowed payment reasons for initiating payouts or transfers.
Some countries require a **reason** that describes why funds are being transferred or payment is being made. Use any of the listed reasons that best describes the purpose of your transfer.
| Reason | Description |
| :----------------------- | :-------------------------------------------------- |
| `ADVERTISING_EXPENSES` | Advertising and marketing spend |
| `ADVISORY_FEES` | Advisory, consulting, or professional fees |
| `BUSINESS_INSURANCE` | Business insurance premiums |
| `COMPUTER_SERVICES` | Computer, software, or IT services |
| `CONSTRUCTION_EXPENSES` | Construction or building-related expenses |
| `DELIVERY_FEES` | Delivery, courier, or logistics fees |
| `EDUCATION` | Education, tuition, or training |
| `EXPORTED_GOODS` | Payment related to exported goods |
| `FAMILY_SUPPORT` | Support or maintenance for family |
| `FUND_INVESTMENT` | Mutual funds, pooled funds, or similar investments |
| `GIFT_AND_DONATION` | Gifts or charitable donations |
| `HOTEL_ACCOMMODATION` | Hotel stays and lodging |
| `INFLUENCER_PAYMENT` | Influencer, creator, or similar partner payments |
| `INSURANCE_CLAIMS` | Insurance claim settlements or payouts |
| `LIBERALIZED_REMITTANCE` | Liberalized remittance–scheme transfers |
| `LOAN_PAYMENT` | Loan or credit repayments |
| `MAINTENANCE_EXPENSES` | General maintenance expenses |
| `MEDICAL_TREATMENT` | Medical treatment or healthcare |
| `OFFICE_EXPENSES` | Office and administrative expenses |
| `OTHER` | Other purpose (use only when no other code applies) |
| `OTHER_FEES` | Miscellaneous fees not covered elsewhere |
| `PERSONAL_TRANSFER` | Personal transfer between parties |
| `PROPERTY_PURCHASE` | Purchase of real estate or property |
| `PROPERTY_RENTAL` | Rent or lease of property |
| `REWARD_PAYMENT` | Rewards, incentives, or loyalty payouts |
| `ROYALTY_FEES` | Royalties or licensing fees |
| `SALARY_PAYMENT` | Salaries, wages, or payroll |
| `SERVICE_CHARGES` | General service charges |
| `SHARES_INVESTMENT` | Investment in shares or equities |
| `SMALL_VALUE_REMITTANCE` | Small-value remittance transfers |
| `TAX_PAYMENT` | Tax or duty payments |
| `TRAVEL` | Travel-related expenses |
| `TRANSPORTATION_FEES` | Transportation or freight charges |
| `UTILITY_BILLS` | Utilities (electricity, water, telecom, etc.) |
## Need help?
Send us a message
Book a call with us
# Sandbox
Source: https://docs.onswitch.xyz/sandbox
Use the sandbox environment to integrate Switch APIs safely. Transactions are simulated and carry no real-world value.
### Sandbox API key
1. **Request access** — Book a call or reach out through [onswitch.xyz](https://onswitch.xyz/) to get onboarded.
2. **Open the dashboard** — After approval, sign in to the [Switch dashboard](https://app.onswitch.xyz/).
3. **Create a sandbox key** — In the developer section, create or copy your **sandbox** service key (distinct from your live key).
Keep your sandbox service key secure and never commit it to version control. Use environment variables to store keys in your applications.
### Sandbox base URL
Sandbox and live requests use the same API host. Send sandbox traffic with your **sandbox** service key:
```
https://api.onswitch.xyz
```
See [Authentication](/authentication) for how to send the key.
**Example request:**
```bash theme={"dark"}
curl "https://api.onswitch.xyz/asset" \
-H "x-service-key: $YOUR_SANDBOX_SERVICE_KEY" \
-H "Accept: application/json"
```
## Sandbox vs production
The sandbox environment simulates production behavior while keeping risk low:
| Feature | Sandbox | Production |
| :------------------------ | :----------------------------- | :--------------------------- |
| **API base URL** | `https://api.onswitch.xyz` | `https://api.onswitch.xyz` |
| **Business verification** | Auto-approved after onboarding | Real-world compliance review |
| **Blockchain** | Mainnet (Simulated) | Mainnet |
### Automated onboarding
In sandbox, onboarding is streamlined for testing:
* **Typically auto-approved** — Submissions are often approved within minutes.
* **Compliance checks still apply** — Some checks (for example PEP screening) may still block approval in sandbox.
* **Test data** — Use mocked and appropriate test data for your testing or staging environment.
While onboarding is typically auto-approved in sandbox, certain compliance checks may still block approval. For example, PEP (Politically Exposed Person) screening can prevent users from unlocking rails even in the test environment.
In production, business onboarding verification undergoes real compliance review.
## Transitioning to production
When you move from sandbox to production:
1. **Use your live service key** from the dashboard.
2. **Keep the same base URL** — `https://api.onswitch.xyz`
3. **Update environment variables** so production deployments never use the sandbox key.
4. **Remove sandbox-only calls** — Drop any endpoints or flags used only for simulation or testing.
5. **Perform user compliance checks** — Collect real user compliance information and documents as required.
6. **Test thoroughly** — Start with small live transactions before scaling volume.
Never use your sandbox key in production or your live key for sandbox testing. Keys are environment-specific and are not interchangeable.
## Need Help?
Send us a message
Book a call with us
# Supported stablecoins
Source: https://docs.onswitch.xyz/stablecoins
List of supported USD-pegged stablecoins (USDC, USDT) and Naira-pegged stablecoin (CNGN) accross several networks including EVM chains, Solana (SVM), and Tron.
Use the **asset ID** in API requests or configuration that requires asset selection; it follows `network:symbol` for example, `base:usdc`.
## USDC (USD Coin)
| Network | Asset ID | Contract address |
| :-------- | :------------- | :--------------------------------------------- |
| Arbitrum | arbitrum:usdc | `0xaf88d065e77c8cc2239327c5edb3a432268e5831` |
| Avalanche | avalanche:usdc | `0xb97ef9ef8734c71904d8002f8b6bc66dd9c48a6e` |
| Base | base:usdc | `0x833589fcd6edb6e08f4c7c32d4f71b54bda02913` |
| BNB Chain | bsc:usdc | `0x8ac76a51cc950d9822d68b83fe1ad97b32cd580d` |
| Celo | celo:usdc | `0xceba9300f2b948710d2653dd7b07f33a8b32118c` |
| Ethereum | ethereum:usdc | `0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48` |
| Gnosis | gnosis:usdc | `0x2a22f9c3b484c3629090feed35f17ff8f88f76f0` |
| Monad | monad:usdc | `0x754704bc059f8c67012fed69bc8a327a5aafb603` |
| Optimism | optimism:usdc | `0x0b2c639c533813f4aa9d7837caf62653d097ff85` |
| Polygon | polygon:usdc | `0x3c499c542cef5e3811e1192ce70d8cc03d5c3359` |
| Solana | solana:usdc | `EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v` |
## USDT (Tether USD)
| Network | Asset ID | Contract address |
| :-------- | :------------- | :--------------------------------------------- |
| Arbitrum | arbitrum:usdt | `0xfd086bc7cd5c481dcc9c85ebe478a1c0b69fcbb9` |
| Avalanche | avalanche:usdt | `0x9702230a8ea53601f5cd2dc00fdbc13d4df4a8c7` |
| BNB Chain | bsc:usdt | `0x55d398326f99059ff775485246999027b3197955` |
| Celo | celo:usdt | `0x48065fbbe25f71c9282ddf5e1cd6d6a887483d5e` |
| Ethereum | ethereum:usdt | `0xdac17f958d2ee523a2206206994597c13d831ec7` |
| Gnosis | gnosis:usdt | `0x4ecaba5870353805a9f068101a40e0f32ed605c6` |
| Monad | monad:usdt | `0xe7cd86e13ac4309349f30b3435a9d337750fc82d` |
| Optimism | optimism:usdt | `0x94b008aa00579c1307b0ef2c499ad98a8ce58e58` |
| Plasma | plasma:usdt | `0xb8ce59fc3717ada4c02eadf9682a9e934f625ebb` |
| Polygon | polygon:usdt | `0xc2132d05d31c914a87c6611c10748aeb04b58e8f` |
| Solana | solana:usdt | `Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB` |
| Tron | Tron:usdt | `TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t` |
## CNGN (Compliant Naira)
| Network | Asset ID | Contract address |
| :-------- | :-------- | :------------------------------------------- |
| Base | base:cngn | `0x46c85152bfe9f96829aa94755d9f915f9b10ef5f` |
| BNB Chain | bsc:cngn | `0xa8aea66b361a8d53e8865c62d142167af28af058` |
## Need help?
Send us a message
Book a call with us
# Stablecoin wallet
Source: https://docs.onswitch.xyz/wallet
Non-custodial USDC and USDT wallets with one unified balance across supported chains—transfer anywhere without bridging or native gas.
**Switch non-custodial and unified stablecoin wallet** allows your users’ funds to move across supported networks without treating each chain as a separate balance in your product or requiring them to bridge, swap networks, or hold native gas tokens for each chain.
## How it works
1. **Create a wallet** with **POST `/wallet/create`**. You receive **`private_key`** once (store it securely), **`data.id`** (unique wallet identifier) for subsequent API calls, and per-network **deposit addresses**. See [Create wallet](/guides/create-wallet).
2. **One balance across all blockchain.** Balances aggregate across the supported all supported stablecoins and blockchains. Spending or transferring draws from that unified view, not from a single chain-specific silo that requires you to reconcile manually.
3. **Cross-chain transfers and fees.** Sending to any supported destination chain from your unified balance, regardless of what blockchain or stablecoin funds was deposited. No native taken (ETH, SOL etc.) for gas fees headaches, simply pay network fees in stablecoin.
4. **Notifications.** Get real-time webhook notifications about deposits and withdrawals on your wallet. See [Webhooks](/webhook).
## Unsupported tokens (rescue)
Only **USDC** and **USDT** on the supported blockchains accumulate toward your **unified balance**. If a wrong asset, airdrop token, or anything outside the supported stablecoins is sent to your stablecoin wallet, it will not show up as spendable unified balance.
You can still **recover** any of those unsupported tokens by transferring them to a wallet with **POST `/wallet/rescue`**. The API transfers the wallet’s holding of that token to the recipient you control.
See **Rescue token** (`POST /wallet/rescue`) in the **API Reference** for more details.
## Supported blockchains
**USDC** and **USDT** supported on **16** blockchains:
| Blockchain | Stablecoin | Chain ID |
| ---------- | ---------- | -------- |
| Solana | USDC, USDT | 10000 |
| Ethereum | USDC, USDT | 1 |
| Base | USDC | 8453 |
| BNB Chain | USDC, USDT | 56 |
| Arbitrum | USDC, USDT | 42161 |
| Optimism | USDC, USDT | 10 |
| Polygon | USDC, USDT | 137 |
| Avalanche | USDC, USDT | 43114 |
| Monad | USDC | 143 |
| Sonic | USDC | 146 |
| Berachain | USDC | 80094 |
| Mantle | USDT | 5000 |
| Linea | USDC, USDT | 59144 |
| HyperEVM | USDT | 999 |
| Plasma | USDT | 9745 |
## Feature summary
no bridging, no swapping, no network switching, just transfer when you want.
Pay transaction fees in stablecoin, no native gas tokens required for users.
transfer to any supported chain, regardless of which chain holds your funds.
Withdraw and deposit to any supported chain, regardless of which chain holds your funds.
## Need help?
Send us a message
Book a call with us
# Webhooks
Source: https://docs.onswitch.xyz/webhook
Switch sends JSON **POST** notifications to a URL (**`callback_url`**) you provide when payment status changes or when a wallet receives or sends funds.
## Set webhook URL
### Payments
Add **`callback_url`** to the request body when you call **POST `/offramp/initiate`** to create a payment for stablecoin to local currency conversion. See [Stablecoin to NGN](/guides/stablecoin-to-ngn) for a guide.
### Wallets
Add **`callback_url`** when you call **POST `/wallet/create`**, or update it later on the wallet, so you can get webhook notifications about deposits and withdrawals on your wallet. See [Create stablecoin wallet](/guides/create-wallet).
## How notifications work
Switch sends each notification as:
* **Method:** `POST`
* **Headers:** `Content-Type: application/json`, `x-switch-signature` (HMAC-SHA256 hex digest of the raw body), `x-switch-timestamp` (when the request was sent; use for optional replay windows)
* **Body:** JSON payload (depending on context; see [Payment webhooks](#payment-webhooks) or [Wallet webhooks](#wallet-webhooks))
Your server should acknowledge the request by returning **any 2xx** status
quickly for successfully received notifications. Switch will retry delivery up
to 3 times if the server does not acknowledge the request.
## Verify webhooks
Always verify that incoming requests comes from Switch before you trust.
### How to verify
1. Read the **body** and the `x-switch-signature` header from the incoming request.
2. Compute the expected webhook signature using your `switch service key` and the `body`.
3. Compare the `computed` signature to the `x-switch-signature` from request header.
```javascript Node.js theme={"dark"}
import crypto from 'crypto';
function verifySwitchWebhook(webhookBody, webhookSignature, yourServiceKey) {
const expected = crypto
.createHmac('sha256', yourServiceKey)
.update(webhookBody, 'utf8')
.digest('hex');
const a = Buffer.from(expected, 'utf8');
const b = Buffer.from(webhookSignature.trim(), 'utf8');
return a.length === b.length && crypto.timingSafeEqual(a, b);
}
```
```python Python theme={"dark"}
import hashlib
import hmac
def verify_switch_webhook(webhook_body, webhook_signature, your_service_key):
expected = hmac.new(
your_service_key.encode('utf-8'),
webhook_body.encode('utf-8'),
hashlib.sha256,
).hexdigest()
sig = webhook_signature.strip()
if len(expected) != len(sig):
return False
return hmac.compare_digest(expected, sig)
```
```php PHP theme={"dark"}
If verification fails, ignore the webhook and do not process it.
## Payment webhooks
You receive these notifications whenever **`callback_url`** is set on your payment initiation request payload.
### Payment status
| Status | Description |
| ------------------ | -------------------------------------------------- |
| `AWAITING_DEPOSIT` | Waiting for the user to send the stablecoin. |
| `PROCESSING` | Stablecoin received is being processed. |
| `COMPLETED` | Stablecoin payout to local currency is completed. |
| `FAILED` | Stablecoin payout to local currency failed. |
| `REVERSED` | Stablecoin payout to local currency was reversed. |
| `SCHEDULED` | Payment is delayed due to liquidity constraints. |
| `BLOCKED` | Payment is blocked for compliance or risk reasons. |
### Example payload
For a USDC on Base to Nigeria Naira (NGN) in `AWAITING_DEPOSIT` status.
```json theme={"dark"}
{
"success": true,
"message": "Offramp initiated successfully",
"timestamp": "2026-04-23T14:35:04.264Z",
"data": {
"status": "AWAITING_DEPOSIT",
"type": "OFFRAMP",
"reference": "61f9a35a-e535-4f04-ba50-3058b4c856c4",
"beneficiary": "69e8e664b9fea9f9e6dc3cbb",
"rate": 1380.845,
"developer_fee": {
"amount": 0,
"amount_usd": 0,
"currency": "USDC",
"network": "BASE"
},
"source": {
"amount": 10,
"amount_usd": 10,
"network": "BASE",
"currency": "USDC"
},
"destination": {
"amount": 13808.45,
"amount_usd": 10,
"network": "FIAT",
"currency": "NGN"
},
"deposit": {
"amount": 10,
"address": "0x3131b6f6a32751C9d99C1710e357A6C4297d17Bc",
"asset": "base:usdc",
"note": [
"Kindly send the exact amount to the wallet address to complete the transaction.",
"This wallet address is for one-time use only and has a 30 minutes expiry window."
]
},
"meta": {},
"created_at": "2026-04-23T14:35:02.448Z",
"updated_at": "2026-04-23T14:35:02.448Z"
}
}
```
### Payload field reference
| Field | Type | Description |
| ---------------------- | --------- | ------------------------------------------------------ |
| `status` | string | Current status; see [Payment status](#payment-status). |
| `type` | string | `OFFRAMP` or `ONRAMP`. |
| `reference` | string | Unique transaction reference (UUID). |
| `source.amount` | number | Amount of source to be sent. |
| `source.currency` | string | Source currency code e.g. `USDC`. |
| `source.network` | string | Source network e.g. `Base`. |
| `destination.amount` | number | Amount of destination to be received. |
| `destination.currency` | string | Destination currency code e.g. `NGN`. |
| `destination.network` | string | Destination network e.g. `Fiat`. |
| `deposit.amount` | number | Amount to send to the deposit address. |
| `deposit.address` | string | Wallet address to send the amount to. |
| `deposit.asset` | string | Asset to deposit e.g. `base:usdc`. |
| `deposit.note` | string\[] | Specific instructions for the deposit |
| `meta.session_id` | string | Payout bank transfer session ID. |
| `meta.explorer_url` | string | URL to view the deposit on-chain. |
| `meta.hash` | string | Hash of the deposit transaction. |
**`meta.session_id`** is only present when the bank payment has been initiated
and is successfully processed.
**`meta.explorer_url`** and **`meta.hash`** are only present when the deposit
has been made on-chain to the provided deposit address.
## Wallet webhooks
You receive these notifications whenever **`callback_url`** is set on your wallet (at creation or via update) and the wallet receives or sends funds.
### Example wallet payload
Incoming USDC on Base to the wallet address.
```json theme={"dark"}
{
"address": "0xe0836E4D35047843dA08D510dFb91E7ecd0c43FC",
"wallet": "69d8d8f1ad6205a9a0e89302",
"token": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
"decimals": 6,
"type": "receive",
"sender": "0x04e05B9bCed583e4F25F7942684dCfe8Ab2632E1",
"receiver": "0xe0836E4D35047843dA08D510dFb91E7ecd0c43FC",
"hash": "0xb8ce59fc3717ada4c02eadf9682a9e934f625ebb",
"amount": 100000000,
"asset": "base:usdc"
}
```
### Wallet payload field reference
| Field | Type | Description |
| ---------- | ------ | ----------------------------------------------------------------------------- |
| `address` | string | Wallet address involved in the event. |
| `token` | string | Contract address of the token on the network. |
| `decimals` | number | Token decimals (for interpreting `amount`). |
| `type` | string | `receive` for deposits to the wallet, `send` for withdrawals from the wallet. |
| `sender` | string | Address that sent the funds. |
| `receiver` | string | Address that received the funds. |
| `hash` | string | On-chain transaction hash. |
| `amount` | number | Amount of the token. |
| `asset` | string | Switch asset id (e.g. `base:usdc`). |
## Need Help?
Send us a message
Book a call with us