Base URL: https://api.paycircle.site. All responses are JSON. Endpoints marked π require Authorization: Bearer <token> from the verify or login call.
Authentication
| POST /auth/register | email, phone. Sends a 6-digit WhatsApp/SMS code. Calling again for an existing account resends the code. |
| POST /auth/verify | email, phone, otp. Activates the account, returns { token } (JWT, 7-day expiry). |
| POST /auth/login | email, password. For accounts registered with a password. |
# register then verify POST /auth/register { "email": "you@corp.com", "phone": "+254700000000" } POST /auth/verify { "email": "...", "phone": "...", "otp": "123456" } β { "token": "eyJhbGci...", "activated": true }
OTP_DEV_MODE=true, register and resend responses include dev_code so you can self-complete verification. This field disappears in production OTP delivery.Membership
30-day free trial β 3-day cooling period β Β£10/month charged in advance. Cancel anytime before billing. The 1% payout fee applies from day one.
| POST /membership/signup | email. Starts the trial clock. Idempotent. |
| GET /membership?email= | Returns derived status (trial | cooling | active | cancelled | none), days_left, reference, renewal date. |
| POST /membership/cancel | email. Cancels; no further membership fees. |
| POST /membership/payment π | email + header X-Admin-Key. Operator confirms fee receipt β activates until next renewal (+30 days). |
GET /membership?email=you@corp.com β { "status": "trial", "days_left": 27, "reference": "PC-A0A96F59", ... }
Hosted at https://membership-status-paycircle.fbirele.workers.dev. The dashboard polls this every 10 seconds.
Deposits
| POST /suby/deposit π | amount_usd β₯ 250. Returns a unique reference (PC-DEP-######) and payment instructions. Status starts pending. |
| GET /suby/deposits π | Operator list of all deposits. |
| POST /suby/deposit/received/:id π | Operator confirms funds landed β credits treasury balance. Optional gbp_value. |
POST /suby/deposit { "amount_usd": 6000 } β { "deposit_id": 9, "reference": "PC-DEP-000009", "status": "pending", "instructions": "..." }
Bank rails: Revolut Business β sort 23-01-20 Β· account 74276923 Β· IBAN GB20 REVO 2301 2074 2769 23 Β· BIC REVOGB21. Full details at /bank-details. Always quote your reference.
Transfers & fees
| POST /transfer/send π | amount, recipient. Fee = amount Γ 1%. Response includes { status, id, amount, fee, total }. |
POST /transfer/send { "amount": 1000, "recipient": "supplier-ke-01" } β { "status": "initiated", "id": "po_x", "amount": 1000, "fee": 10, "total": 1010 }
Treasury sweep
| GET /margin/status π | Treasury balance, Β£10,000 sweep threshold, eligibility, rail configuration flag. |
| POST /margin/sweep π | amount in GBP. Rules per infrastructure spec: minimum Β£1,000, maximum Β£50,000 per transaction, triggered above Β£10,000 balance. |
GET /margin/status β { "balance": 4700, "sweep_threshold": 10000, "eligible": false, "rail_configured": false }
Support β Hermes
| POST /support/chat π | message. Instant answer for known topics (OTP, payouts, membership, rates, bank details). Unknown questions open a ticket automatically. |
| POST /support/ticket π | subject, body. Creates a tracked ticket. |
| POST /support/escalate π | ticket_id. Applies the ladder: Kenya call centre after 24h, UK after 48h. |
| GET /support/status/:id π | Ticket status and channel. |
POST /support/chat { "message": "i never got my otp" } β { "channel": "hermes", "resolved": true, "reply": "Codes expire after 10 minutes..." }
Live FX
| GET /rates/live | No auth. Corridor FX (USDβKES, GBPβNGN, EURβGHS), rail fees, PayCircle commission, total cost per corridor, timestamp. Cached 30s server-side. |
GET https://paycircle.site/rates/live β { "fx": { "USD_KES": 129.42, ... }, "rails": { "mpesa": 0.015, ... }, "paycircle": { "commission_rate": 0.01, "monthly_fee": 10 }, "total_cost": { "USD_KES": 0.025, ... }, "timestamp": "2026-08-22T21:52:40.028Z" }
Errors
| 400 | Validation β below minimums, missing fields. Message states the rule. |
| 401 | Missing/invalid Bearer token, bad signature, bad operator key. |
| 503 | Rail not configured β response names the exact missing environment variables. |
x-suby-signature HMAC-SHA256 header over the raw body. Unsigned requests are rejected at the edge. Endpoints: /webhooks/suby/merchant and /webhooks/suby/payout.