Seev PlusDocs
Docs
On/Off-Ramp API

On-Ramp (Buy USDC)

Create orders for GHS → USDC conversions. Collect fiat via Mobile Money and deliver USDC to the user's Stellar wallet.

The on-ramp flow lets users buy USDC with Ghana Cedis via Mobile Money. Your app creates an order, the user approves a MoMo prompt on their phone, and USDC is automatically sent to their Stellar wallet.

Prerequisites

  • User must have completed KYC verification
  • Merchant must have an active USDC wallet (set up during activation)
  • Merchant must have sufficient USDC liquidity to fulfil the order

Get a rate preview

Before committing to an order, you can preview the current exchange rate without any side effects.

curl https://api.your-domain.com/api/v1/ramp/quote \
  -H "Authorization: Bearer $SECRET_KEY" \
  -G \
  -d "type=onramp" \
  -d "fiat_amount=100"

Response:

{
  "success": true,
  "data": {
    "type": "onramp",
    "you_pay": "100.00 GHS",
    "you_receive": "8.21 USDC",
    "rate": "1 USDC = 12.18 GHS",
    "fiat_amount": 100,
    "fiat_currency": "GHS",
    "crypto_amount": 8.21,
    "crypto_currency": "USDC",
    "rate_applied": 12.18,
    "fee_amount": 1.5,
    "fee_bearer": "customer",
    "net_fiat_amount": 100,
    "net_crypto_amount": 8.21,
    "expires_in_seconds": 300
  }
}

You can specify either fiat_amount (GHS the user wants to pay) or crypto_amount (USDC the user wants to receive). The API calculates the other side.

Create an on-ramp order

This is the all-in-one endpoint that locks the rate, reserves liquidity, and initiates the Mobile Money collection in a single call.

curl -X POST https://api.your-domain.com/api/v1/ramp/orders \
  -H "Authorization: Bearer $SECRET_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "onramp",
    "fiat_amount": 100,
    "destination_wallet": "GBXYZ...USER_STELLAR_ADDRESS",
    "payment_method": "mobile_money",
    "idempotency_key": "order_abc_123",
    "user": {
      "first_name": "Kofi",
      "last_name": "Mensah",
      "phone": "0241234567",
      "network": "MTN"
    }
  }'

Request body

FieldTypeRequiredDescription
typestringYesMust be "onramp"
fiat_amountnumberOne ofAmount in GHS the user pays (specify this OR crypto_amount)
crypto_amountnumberOne ofAmount in USDC the user receives
destination_walletstringYesUser's Stellar address to receive USDC
payment_methodstringYes"mobile_money" or "bank_transfer"
idempotency_keystringNoUnique key to prevent duplicate orders
user.first_namestringYesUser's first name (must match KYC)
user.last_namestringYesUser's last name
user.phonestringYesUser's phone number (must be KYC verified)
user.networkstringYesMoMo network: MTN, VODAFONE, TELECEL, or AIRTELTIGO

Response

{
  "success": true,
  "data": {
    "reference": "RAMP-20260810-a1b2c3d4",
    "order_id": "f16890cc-8fd0-4af7-982e-2100bf3dda14",
    "type": "onramp",
    "status": "fiat_pending",
    "you_pay": "100.00 GHS",
    "you_receive": "8.21 USDC",
    "rate": "1 USDC = 12.18 GHS",
    "fiat_amount": 100,
    "fiat_currency": "GHS",
    "crypto_amount": 8.21,
    "crypto_currency": "USDC",
    "rate_applied": 12.18,
    "fee_amount": 1.5,
    "fee_bearer": "customer",
    "net_fiat_amount": 100,
    "net_crypto_amount": 8.21,
    "expires_at": "2026-08-10T06:22:00Z",
    "message": "Payment initiated. Approve the mobile money prompt on your phone."
  }
}

The order is immediately in fiat_pending status. A Mobile Money prompt is sent to the user's phone. The user must approve it before the rate expires.

What happens after order creation

  1. MoMo prompt sent — The user receives a payment prompt on their phone
  2. User approves — Status transitions to fiat_confirmed
  3. USDC sent — Status moves to crypto_sending, then completed with a crypto_tx_hash
  4. Rate expires — If the user doesn't approve within the TTL, status becomes expired

Check order status

Poll the order to track progress:

curl https://api.your-domain.com/api/v1/ramp/orders/RAMP-20260810-a1b2c3d4 \
  -H "Authorization: Bearer $SECRET_KEY"

Completed response includes the Stellar transaction hash:

{
  "success": true,
  "data": {
    "reference": "RAMP-20260810-a1b2c3d4",
    "type": "onramp",
    "status": "completed",
    "crypto_tx_hash": "ab517c09c4b67f8c3573621a40cb6ad00887f16fe6264f6388267bb33b76ad51",
    "fiat_amount": 100,
    "crypto_amount": 8.21,
    "rate_applied": 12.18,
    "completed_at": "2026-08-10T06:25:57Z"
  }
}

List orders

Retrieve paginated order history:

curl "https://api.your-domain.com/api/v1/ramp/orders?type=onramp&limit=20&offset=0" \
  -H "Authorization: Bearer $SECRET_KEY"

Cancel an order

Cancel an order that hasn't been paid yet:

curl -X POST https://api.your-domain.com/api/v1/ramp/orders/RAMP-20260810-a1b2c3d4/cancel \
  -H "Authorization: Bearer $SECRET_KEY"

Cancellation is only possible when the order is in quote_locked status (two-step flow). Orders created via the single-step POST /orders endpoint go directly to fiat_pending and cannot be cancelled once the MoMo prompt is sent.

Two-step flow (legacy)

For more control, you can separate quote creation from payment initiation:

Step 1: Create a quote

curl -X POST https://api.your-domain.com/api/v1/ramp/quote \
  -H "Authorization: Bearer $SECRET_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "onramp",
    "fiat_amount": 100,
    "destination_wallet": "GBXYZ...USER_STELLAR_ADDRESS",
    "user": {
      "first_name": "Kofi",
      "last_name": "Mensah",
      "phone": "0241234567",
      "provider_name": "MTN"
    }
  }'

The order is created in quote_locked status with a locked rate.

Step 2: Initiate payment

curl -X POST https://api.your-domain.com/api/v1/ramp/RAMP-20260810-a1b2c3d4/pay \
  -H "Authorization: Bearer $SECRET_KEY" \
  -H "Content-Type: application/json" \
  -d '{"channel": "mobile_money", "phone": "0241234567", "network": "MTN"}'

This sends the MoMo prompt and transitions the order to fiat_pending.

Idempotency

Include an idempotency_key in your create order request to safely retry failed requests. If a request with the same key has already created an order, the existing order is returned without creating a duplicate.

Error codes

ErrorMeaning
KYC_REQUIREDPhone number not verified — complete KYC first
RAMP_NOT_ACTIVEMerchant ramp wallets not activated
RATE_UNAVAILABLEExchange rate service temporarily unavailable
INVALID_DESTINATION_WALLETStellar address is invalid or missing USDC trustline
INSUFFICIENT_LIQUIDITYNot enough USDC in merchant's liquidity pool
LIMIT_ERRORAmount exceeds transaction, daily, or monthly limits
RATE_EXPIREDQuote TTL elapsed — create a new order
COLLECTION_FAILEDMoMo collection could not be initiated

Best practices

  • Always validate the destination Stellar wallet has a USDC trustline before creating an order
  • Use idempotency_key for all order creation to handle network retries safely
  • Poll order status or set up webhooks to detect completion — do not assume success after creation
  • Show the user the rate and amount before they approve the MoMo prompt
  • Handle expired status gracefully by prompting the user to retry

On this page