OuiPay
API ReferenceServices

Create a service order

POST /v1/services/orders : place the purchase and start collection.

POST /v1/services/orders

Creates the service order, opens the collection on payment_method, and returns the order plus the payment attempt with its next action (checkout_url or instructions). Fulfilment starts when the payment settles.

This is a money-moving call: an idempotency key is required, and a stored failure replays instead of re-charging on retry. See Implement Idempotency.

Headers

HeaderRequiredValue
AuthorizationyesBearer sk_...
Idempotency-KeyyesUnique per logical order (or idempotency_key body field)
Content-Typeyesapplication/json
X-Transaction-AuthorizationnoStep-up proof when the account requires a PIN

Body

FieldTypeRequiredNotes
country_codestringyesService country
service_codestringyesCatalogue code
provider_cost_minorintyesDestination service value, minor units
currencystringyesCurrency of provider_cost_minor
payment_methodstringyesA method from /services/{code}/payment-methods
idempotency_keystringyesBody alternative to the Idempotency-Key header
quote_idstringnoLocks pricing; required after cross-currency quotes
product_codestringnoSelected plan code for fixed-price services
operator_codestringnoSelected operator
meter_typestringnoSelected variant (e.g. prepaid)
account_refstringnoMeter/smartcard/customer ID
phonestringnoRecipient phone for airtime/data
validation_snapshotobjectnoThe /validate response for the recipient
metadataobjectnoYour references for reconciliation

quote_id binds the order to the quoted service, country, amount, and recipient. A mismatched or expired quote rejects with QUOTE_MISMATCH / QUOTE_EXPIRED: re-quote instead of retrying.

Request

curl https://api.ouipay.africa/v1/services/orders \
  -H "Authorization: Bearer sk_test_..." \
  -H "Idempotency-Key: svc-order-8842" \
  -H "Content-Type: application/json" \
  -d '{
    "country_code": "NG",
    "service_code": "electricity",
    "provider_cost_minor": 500000,
    "currency": "NGN",
    "payment_method": "wallet",
    "operator_code": "ikeja-electric",
    "meter_type": "prepaid",
    "account_ref": "04591234567",
    "quote_id": "01M2S17..."
  }'

Response 201

{
  "data": {
    "order": {
      "id": "01M2S18...",
      "transaction_id": "01M2S18...",
      "payment_transaction_id": null,
      "country_code": "NG",
      "service_code": "electricity",
      "status": "pending",
      "customer_amount_minor": 510000,
      "fee_minor": 10000,
      "provider_cost_minor": 500000,
      "currency": "NGN",
      "operator_code": "ikeja-electric",
      "meter_type": "prepaid",
      "account_ref": "04591234567",
      "idempotency_key": "svc-order-8842",
      "quote_id": "01M2S17...",
      "created_at": "2026-09-18T02:00:00Z",
      "updated_at": "2026-09-18T02:00:00Z"
    },
    "payment": {
      "id": "01M2S18...",
      "transaction_id": "01M2S18...",
      "method": "wallet",
      "status": "processing"
    },
    "checkout_url": null,
    "instructions": null
  }
}

Complete the collection the way payment directs: redirect to checkout_url for hosted methods, follow instructions for transfer-style methods, or wait for confirmation when wallet debits inline. The order moves to awaiting_payment, then fulfilling once the payment settles. Track it on GET /services/orders/{id}/status or via service_order.completed / service_order.failed webhooks.

Errors

HTTPerror.codeCause
400VALIDATIONMissing or malformed field
403SERVICE_COUNTRY_NOT_ELIGIBLEService country not eligible for this account
403PIN_REQUIREDRe-send with X-Transaction-Authorization
403COMPLIANCE_DENIEDScreening blocked the order: do not retry
409QUOTE_MISMATCHQuote does not match this service, country, or amount
409QUOTE_EXPIREDQuote expired: re-quote
409IDEMPOTENCY_REPLAYKey reused with a different payload
422IDEMPOTENCY_KEY_REQUIREDMoney-moving call without an idempotency key
422SERVICE_UNAVAILABLEService not offered in the requested country
422SERVICE_NOT_EXECUTABLEService visible but not purchasable
422INSUFFICIENT_FUNDSWallet cannot cover customer_amount_minor
422LIMIT_EXCEEDEDAbove the customer's tier limit
422FX_QUOTE_UNAVAILABLECross-currency order without a live FX quote
502PROVIDER_FAILUREFulfilment rail error: retry-safe via idempotency