Seev PlusDocs
Docs
On/Off-Ramp API

Webhooks & Status Polling

Track ramp order progress by polling status or handling webhook events for real-time notifications.

Ramp orders are processed asynchronously. After creating an order, you need to track its progress to know when it's completed, failed, or expired.

Polling order status

The simplest integration is polling the order endpoint:

curl https://api.your-domain.com/api/v1/ramp/orders/{reference} \
  -H "Authorization: Bearer $SECRET_KEY"
Order statusPoll intervalAction
fiat_pendingEvery 5 secondsUser is approving MoMo prompt
crypto_pendingEvery 10 secondsWaiting for user to send USDC
fiat_confirmed / crypto_confirmedEvery 5 secondsProcessing — settlement in progress
crypto_sending / fiat_sendingEvery 5 secondsAlmost done
completed / failed / expiredStop pollingTerminal state reached

Example polling implementation

async function pollOrder(reference, secretKey) {
  const baseUrl = 'https://api.your-domain.com/api/v1/ramp/orders';
  const terminalStates = ['completed', 'failed', 'expired', 'cancelled'];

  while (true) {
    const res = await fetch(`${baseUrl}/${reference}`, {
      headers: { 'Authorization': `Bearer ${secretKey}` }
    });
    const { data } = await res.json();

    console.log(`Order ${reference}: ${data.status}`);

    if (terminalStates.includes(data.status)) {
      return data;
    }

    // Wait before next poll
    await new Promise(r => setTimeout(r, 5000));
  }
}

Merchant webhooks

If your merchant account has a webhook URL configured, payment events will be forwarded to your endpoint when the underlying fiat transaction completes.

Webhook payload

{
  "event": "payment.completed",
  "data": {
    "reference": "PAY-20260810-3e848662-699d-41e8-8d90-fa25cecdb8ac",
    "status": "completed",
    "amount": 1015,
    "currency": "GHS",
    "provider_ref": "41e88d90fa25cecdb8ac",
    "channel": "mobile_money",
    "meta": {
      "ramp_order_id": "f16890cc-8fd0-4af7-982e-2100bf3dda14",
      "ramp_type": "onramp"
    }
  }
}

The meta.ramp_order_id and meta.ramp_type fields identify which ramp order the payment belongs to. Use this to match webhook events back to your orders.

Webhook events for ramp

EventWhenRamp action
payment.completedFiat collected (on-ramp) or fiat disbursed (off-ramp)USDC send initiated (on-ramp) or order completing (off-ramp)
payment.failedFiat collection or payout failedOrder may retry or fail

Webhooks confirm the fiat leg only. For the complete order lifecycle (including crypto send confirmation), poll the order status endpoint.

Order status transitions

On-ramp lifecycle

quote_locked → fiat_pending → fiat_confirmed → crypto_sending → completed
                     │                                    │
                     ├──→ expired                         └──→ failed
                     └──→ cancelled

Off-ramp lifecycle

crypto_pending → crypto_confirmed → fiat_sending → completed
       │                                   │
       ├──→ expired                        └──→ fiat_retry → fiat_sending
       └──→ cancelled                                 └──→ failed

Response fields by status

Terminal: completed

{
  "status": "completed",
  "crypto_tx_hash": "ab517c09c4b67f8c...",
  "completed_at": "2026-08-10T06:25:57Z"
}

For on-ramp, crypto_tx_hash is the Stellar transaction where USDC was sent to the user. For off-ramp, crypto_tx_hash is the Stellar transaction where the user sent USDC.

Terminal: failed

{
  "status": "failed",
  "failure_reason": "Crypto send failed after max retries"
}

Always check failure_reason for details on what went wrong.

Terminal: expired

{
  "status": "expired",
  "expires_at": "2026-08-10T06:22:00Z"
}

The rate TTL elapsed before payment was confirmed. Create a new order.

Handling late payments

If a user's MoMo payment confirms after the rate expires:

  • The system automatically detects the late payment
  • A refund is initiated back to the user's Mobile Money
  • The order status moves to fiat_refunded

For off-ramp, if USDC arrives after expiry:

  • The system detects the late crypto payment
  • USDC is automatically refunded to the user's source wallet
  • The order status moves to crypto_refunded

Best practices

  • Always treat completed as the only success state — don't fulfil based on intermediate states
  • Implement exponential backoff if you encounter rate limits during polling
  • Log the full order response for debugging — especially failure_reason on failed orders
  • For production, combine polling with webhooks for the fastest detection
  • Set a maximum poll duration (e.g., 30 minutes) and handle timeout gracefully
  • Verify crypto_tx_hash on the Stellar network if your application requires proof of settlement

On this page