All providers
Qosic
payment·🇹🇬·Sandbox available
Qosic (QOSPAY) is a West African payment aggregator for mobile money collection, disbursement and card checkout. This spec set covers Togo (Togocel / Yas and Moov Togo). Qosic also supports Benin (MTN, Moov, Celtiis) and card payments via a separate Checkout product not covered here.
Use with AI agents
After installing the plugin or adding the MCP server, prompt your agent:
“Poll Qosic to check whether a Togocel or Moov Togo mobile money transaction completed.”
Install the plugin →Capabilities
| Capability | Type | StatusiVerified — tested in a real application.Available — spec complete, usable by agents. | Method | Example |
|---|---|---|---|---|
| check_payment_status | synchronous | Available | POST | Example |
| disburse | asynchronous | Available | POST | Example |
| initiate_payment | asynchronous | Available | POST | Example |
| webhook_payment_completed | webhook | Available | POST | Example |
Gotchas
check_payment_status
- ⚠Poll on a backoff: don't query check_payment_status more than once per second. Customer validates the USSD over 30-120 seconds typically. Common cadence: 3s -> 5s -> 10s -> 20s, give up after ~3 minutes and mark as expired.
- ⚠responsecode is a STRING, not an integer. '00' != 0 in JavaScript without a cast. Compare as strings.
- ⚠Querying an UNKNOWN transref returns HTTP 200 with responsecode '-1', responsemsg 'FAILED', serviceref '0'. Do not interpret this as 'transaction failed' — it can also mean 'transaction not yet known to Qosic' shortly after initiate_payment. Re-poll a few times before declaring failure.
- ⚠The sandbox auto-completes paid transactions immediately (responsecode '00') without any USSD validation. Production REQUIRES the customer to validate on their phone — expect responsecode '01' for 30-120s.
- ⚠Togocel and Moov Togo use the same body schema for status but DIFFERENT URLs (TOGOCEL -> /tm/v1/, MOOV -> /tg/v1/). Pass the same network value you used at initiation.
- ⚠'02' on Togo means FAILED (not PENDING — that's '01'). See docs.qosic.com for the full Togo error code list.
disburse
- ⚠responsecode '00' on deposit means Qosic ACCEPTED the disbursement, not that the recipient's wallet has been credited. The actual operator credit is asynchronous — poll check_payment_status with the same transref until it returns a terminal state.
- ⚠Successful synchronous responses return HTTP 202 (Accepted), not 200. Treat both 200 and 202 as 'request accepted'.
- ⚠[MUST TELL USER] Disbursement endpoints require IP whitelisting in the Qosic dashboard. Without it, requests from your production server are rejected with 401 regardless of credentials.
- ⚠[MUST TELL USER] Disbursement requires a funded merchant wallet on Qosic. Check your dashboard balance before scheduling a payout — Qosic does not have a separate balance API; you reconcile via the dashboard.
- ⚠[MUST TELL USER] Qosic mandates two-factor authentication (Authenticator app + QR code) for withdrawals from the merchant balance. This is on the dashboard side, not API-driven, but you need to enable it before disbursing in production.
- ⚠Same URL routing as initiate_payment: TOGOCEL -> /tm/v1/deposit, MOOV -> /tg/v1/deposit. Each operator has its own clientid in the dashboard.
- ⚠transref MUST be unique. Reusing a transref across an initiate_payment and a disburse breaks status lookups.
initiate_payment
- ⚠responsecode '01' (pending) is the NORMAL initial response — the customer still has to validate the USSD push. Poll check_payment_status with the SAME transref until you reach a terminal state ('00' success, '02'/'-1' failure).
- ⚠Successful synchronous responses return HTTP 202 (Accepted), not 200. Treat both 200 and 202 as 'request accepted'; the real outcome is in the JSON body's responsecode.
- ⚠serviceref can be null on the initial response (filled in later) and is generally null/'0' for Togo. Do not assume it is a non-null string — type it as string | null.
- ⚠The URL depends on the network: TOGOCEL -> /QosicBridge/tm/v1/requestpayment, MOOV -> /QosicBridge/tg/v1/requestpayment. The canonical_example handles this switch; do not hard-code one URL for both.
- ⚠Each operator has a DIFFERENT clientid (visible in your dashboard, Settings -> API Keys & Callback URL). Sending the Togocel clientid to the Moov URL (or vice-versa) fails. Manage them as separate env vars (e.g. QOSIC_CLIENT_ID_TOGOCEL, QOSIC_CLIENT_ID_MOOV).
- ⚠Auth is HTTP Basic with merchant username:password (NOT the clientid). Username/password are in the dashboard. Set QOSIC_USERNAME and QOSIC_PASSWORD env vars.
- ⚠Sandbox is on staging.qosic.net:9010 over HTTPS. The public docs say http:// but the server now redirects/rejects plain HTTP with a 400; always use https://staging.qosic.net:9010. Public test credentials: USR01 / YG739G5XFVPYYV4ADJVW with the shared sandbox clientid MTNTEST (or MOOVTEST). These are Qosic's generic sandbox test clientids — in production each Togo operator (Togocel, Moov) has its own clientid from your dashboard. The sandbox does NOT actually send USSD pushes; production only.
- ⚠[MUST TELL USER] Configure your server IP in Qosic dashboard -> IP Whitelist before calling any API in production. Requests from non-whitelisted IPs are rejected.
- ⚠transref must be globally unique. Reusing a transref returns an error and breaks idempotency on your side.
webhook_payment_completed
- ⚠[MUST TELL USER] The Qosic webhook does NOT fire by default. You must configure the callback URL per operator (Togocel, Moov, etc.) in dashboard -> Settings -> API Keys & Callback URL. Without this, no callback is ever delivered.
- ⚠[MUST TELL USER] Qosic public documentation does NOT describe any signature, HMAC or shared-secret mechanism for verifying webhook payloads. Treat the payload as UNTRUSTED — any party that knows your URL can forge a webhook. ALWAYS call check_payment_status with the received `transRef` (and the matching network) before fulfilling the order.
- ⚠[MUST TELL USER] The webhook `amount` is NOT verifiable server-side: check_payment_status / gettransactionstatus returns responsecode and responsemsg but NO amount field, so re-verification confirms the payment happened but cannot confirm how much. Never fulfill based on the webhook `amount` — look up the amount you stored when you called initiate_payment (keyed by transRef) and use that. A forged webhook could otherwise inflate the amount.
- ⚠[MUST TELL USER] Qosic requires the callback URL to be HTTPS and browser-loadable. http:// URLs are silently ignored. For local development, use ngrok (or similar) and put the HTTPS tunnel URL in the dashboard.
- ⚠Field NAME CASING DIFFERS between REST and webhook: REST returns transref / serviceref / responsecode (lowercase); webhook posts transRef / serviceRef / code (camelCase, different field for the code). Mapping the two requires care.
- ⚠The `code` field on webhooks ('200' / '-1') is NOT the same as the REST responsecode ('00' / '02' / ...). Do not pass one where the other is expected.
- ⚠Return HTTP 200 quickly. Qosic does not document a documented retry mechanism, so you may have a single delivery attempt — implement a reconciliation job that re-polls check_payment_status for any transaction left in PENDING beyond a few minutes.
Details
- Category
- payment
- Sandbox
- Yes