SANDBOX ENVIRONMENT — payments on this deployment are simulated. No real money is moved.
Developers

API Documentation

Integrate FlyPay Nexus using our versioned REST API and signed webhooks.

Getting started

Base URL: https://app.flypaynexus.com/api/v1. All requests and responses use JSON. Amounts you send are decimal strings in major units ("1500.50"); amounts we return are integers in minor units (amount_minor) with an explicit currency.

  1. Create an account and open API Keys.
  2. Create a test secret key (sk_test_…). It is shown once — store it in your server’s secrets.
  3. Create a payment link or payment, then listen for webhooks.
curl https://app.flypaynexus.com/api/v1/payment-links \
  -H "Authorization: Bearer $FLYPAY_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: link-2026-10-13-001" \
  -d '{"title":"Consultation","amount":"50000","currency":"TZS"}'

Python example

import os, requests

r = requests.post(
    "https://app.flypaynexus.com/api/v1/payment-links",
    headers={"Authorization": f"Bearer {os.environ['FLYPAY_SECRET_KEY']}", "Idempotency-Key": "link-001"},
    json={"title": "Consultation", "amount": "50000", "currency": "TZS"},
    timeout=15,
)
r.raise_for_status()
print(r.json()["url"])

Authentication

Send your secret key as a bearer token: Authorization: Bearer sk_test_…. Keys are scoped to one business. Never expose secret keys in browsers or mobile apps — call the API from your server.

Key prefixModeBehaviour
sk_test_SandboxUses simulated sandbox methods only. No real money moves.
sk_live_LiveAvailable only for verified businesses on deployments with live providers enabled.

Requests are rate-limited per key (120 requests/minute by default). Exceeding it returns 429.

Sandbox testing

Sandbox methods: sandbox_mpesa, sandbox_airtel, sandbox_mixx, sandbox_halopesa, sandbox_card, sandbox_bank. Mobile money outcomes depend on the payer phone number’s last three digits:

Phone ends withResult
000Fails — insufficient_funds
111Customer cancels
999Stays pending (never approved; expires after 30 minutes)
anything elseSucceeds after a short delay

Card and bank-transfer sandbox payments open a simulated provider page where you choose Approve or Decline. Our checkout never collects card numbers.

Idempotency

Send an Idempotency-Key header on POST requests. Retrying with the same key and body returns the original response without creating a duplicate. Reusing a key with a different body returns 409 idempotency_conflict. Keys are kept for 24 hours.

Errors

{
  "error": {
    "code": "validation_failed",
    "message": "Amount must be a positive number with at most 2 decimals",
    "details": { "amount": "..." },
    "request_id": "6f1c…"
  }
}
StatusCommon codes
400invalid_json
401unauthorized
403forbidden, not_verified
404not_found
409idempotency_conflict, not_payable
422validation_failed, method_not_allowed, insufficient_balance
429rate_limited
502provider_error

Every response includes an X-Request-Id header — include it when contacting support.

Pagination

List endpoints accept page (from 1) and limit (1–100, default 20) and return { object: "list", data, page, limit, total, has_more }.

Versioning

The version is in the path (/api/v1). We add fields and endpoints without a new version; breaking changes ship as a new version with at least 6 months’ notice for the previous one.

Troubleshooting

  • 401 unauthorized — check the key is complete, not revoked, and sent as Bearer.
  • 422 method_not_allowed — the method is not enabled for your business, not available in this mode, or doesn’t support the currency.
  • Payment stuck in pending — mobile money waits for the customer to approve. Poll GET /payments/{id} or rely on webhooks; unpaid payments expire after 30 minutes.
  • Webhook signature mismatch — compute the HMAC over the raw request body, not re-serialised JSON.