# Routegate documentation > Generated from https://useroutegate.com/docs. Base URL https://api.useroutegate.com. --- # Routegate documentation Source: https://useroutegate.com/docs Routegate takes one request, scores every channel you have connected, sends it to the one most likely to deliver, fails over inside the same request when that one does not answer, and settles the money in a double-entry ledger. These pages are written to be read by people and by agents. Every page is available as markdown at its own path plus `.md`, the whole set is at [llms-full.txt](/llms-full.txt), and the shapes are defined once in the OpenAPI spec. ## The five minute path 1. Get a test key from the dashboard. No approval and no call. 2. Send your first vend with an `Idempotency-Key`. 3. Receive the webhook, verify the signature, update your order. 4. Swap the key for a live one. Nothing else changes. Start at the [quickstart](/docs/quickstart). ## What to read next | If you want to | Read | | ------------------------------------ | ------------------------------------------ | | Make your first call | [Quickstart](/docs/quickstart) | | Understand how a channel is chosen | [Routing](/docs/concepts/routing) | | Understand the money | [Wallet and ledger](/docs/concepts/wallet) | | Stop a retry becoming a double debit | [Idempotency](/docs/concepts/idempotency) | | Hand the whole thing to an agent | [For agents](/docs/agents) | --- # Quickstart Source: https://useroutegate.com/docs/quickstart Everything below works with a test key. Test mode returns the same envelope, the same statuses and the same webhooks as live, against a sandbox provider, so the integration you write here is the one you ship. ## 1. Get a key Create an account and open **API keys** in the dashboard. A test key starts with `rg_test_`. It is shown once, so put it straight into your environment. ```bash export ROUTEGATE_API_KEY=rg_test_4k9d2m8v ``` ## 2. Send a vend One endpoint, one key, one idempotency key. The amount is an integer in kobo, so a hundred naira is `10000`. ```bash curl -X POST https://api.useroutegate.com/api/v1/airtime \ -H "Authorization: Bearer $ROUTEGATE_API_KEY" \ -H "Idempotency-Key: ord-00042-a1" \ -H "Content-Type: application/json" \ -d '{ "network": "mtn", "destination": "+2348012345678", "amount_kobo": 100000, "client_reference": "ord-00042" }' ``` You get `202 Accepted` and a transaction. `status` is `pending` until a channel answers. ```json { "data": { "id": "rg_tx_01J8QX4M2K9R7V3N", "status": "pending", "network": "mtn", "destination": "+2348012345678", "amount_kobo": 100000, "client_reference": "ord-00042" } } ``` ## 3. Take the webhook Do not poll. The final state arrives at your endpoint, signed, and retries until you take it. ```json { "event_id": "evt_01J8QX4P7B2M9C3D", "type": "transaction.successful", "data": { "id": "rg_tx_01J8QX4M2K9R7V3N", "status": "successful" } } ``` Verify `Routegate-Signature` before you trust it, and treat `event_id` as the key you deduplicate on. See [webhooks](/docs/concepts/webhooks). ## 4. Go live Swap the key. Nothing else in your code changes. > If any of this took longer than five minutes, tell us which step. That is a bug in these docs, not in you. --- # Idempotency Source: https://useroutegate.com/docs/concepts/idempotency Every money call takes an `Idempotency-Key`. It protects you from the one failure that costs real money: a request that succeeds on our side and then dies on the wire before the response reaches you. Your server sees a timeout and retries, which is correct. Without a key, that retry is a second top-up and a second debit. ## How it works We store the key with the result of the first request. A second request carrying the same key does not run again; it returns the first result, with the original transaction id and status. Your retry becomes a read. Keys are scoped to your account and kept for 24 hours. ## Choosing a key Derive it from the thing that is already unique in your system, which is usually the order. ```javascript const key = `${order.id}-a${attempt}`; ``` Two rules: - Generate it once, with the order, and reuse it for every attempt at that order. A fresh key per attempt means every attempt is a new transaction. - Never reuse a key for a different request. Same key with a different amount or destination is a bug, not a retry, and we answer `409` rather than quietly returning the wrong transaction. ## What you get back | Situation | Response | | ------------------------ | ---------------------------------- | | First request | `202` and a new transaction | | Same key, same body | `202` and the original transaction | | Same key, different body | `409` with `idempotency_conflict` | | Key older than 24 hours | Treated as new | --- # Payment callback Source: https://useroutegate.com/docs/api/callbacks/paymentCallback Authenticate with a bearer API key (`rg_test_` in test mode, `rg_live_` in live). | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `gateway` | path | string | yes | | ## Request ```bash curl -X POST https://api.useroutegate.com/callbacks/payments/ \ -H "Authorization: Bearer $ROUTEGATE_API_KEY" ``` ## Response `200` ```json { "success": false, "data": "", "message": "", "code": "" } ``` ## Error `401` Error envelope ```json { "success": false, "data": "", "message": "", "code": "" } ``` ## Give this to your coding agent Read `https://useroutegate.com/docs/api/callbacks/paymentCallback.md` and implement this call. Keep one `Idempotency-Key` per attempt at the same order, treat `202` as accepted and resolve the final state from the webhook. Amounts are integers in kobo. --- # Provider callback Source: https://useroutegate.com/docs/api/callbacks/providerCallback Authenticate with a bearer API key (`rg_test_` in test mode, `rg_live_` in live). | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `provider` | path | string | yes | | ## Request ```bash curl -X POST https://api.useroutegate.com/callbacks/providers/ \ -H "Authorization: Bearer $ROUTEGATE_API_KEY" ``` ## Response `200` ```json { "success": false, "data": "", "message": "", "code": "" } ``` ## Error `409` Error envelope ```json { "success": false, "data": "", "message": "", "code": "" } ``` ## Give this to your coding agent Read `https://useroutegate.com/docs/api/callbacks/providerCallback.md` and implement this call. Keep one `Idempotency-Key` per attempt at the same order, treat `202` as accepted and resolve the final state from the webhook. Amounts are integers in kobo. --- # Vend airtime Source: https://useroutegate.com/docs/api/public/vendAirtime Authenticate with a bearer API key (`rg_test_` in test mode, `rg_live_` in live). | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `Idempotency-Key` | header | string | yes | | | `wait` | query | integer | no | Long-poll up to this many seconds for a final state; capped by the server | ## Request ```bash curl -X POST https://api.useroutegate.com/api/v1/airtime \ -H "Authorization: Bearer $ROUTEGATE_API_KEY" \ -H "Idempotency-Key: ord-00042-a1" \ -H "Content-Type: application/json" \ -d '{ "network": "mtn", "network_id": "", "destination": "+2348012345678", "receiver_phone_number": "", "phone_number": "", "amount_kobo": 100000, "amount": 0, "product_code": "mtn-sme-1gb", "service_id": "", "serviceid": "", "client_reference": "ord-00042", "metadata": {} }' ``` ## Response `202` ```json {} ``` ## Error `402` Error envelope ```json { "success": false, "data": "", "message": "", "code": "" } ``` ## Give this to your coding agent Read `https://useroutegate.com/docs/api/public/vendAirtime.md` and implement this call. Keep one `Idempotency-Key` per attempt at the same order, treat `202` as accepted and resolve the final state from the webhook. Amounts are integers in kobo. --- # Vend cable Source: https://useroutegate.com/docs/api/public/vendCable Authenticate with a bearer API key (`rg_test_` in test mode, `rg_live_` in live). | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `Idempotency-Key` | header | string | yes | | | `wait` | query | integer | no | Long-poll up to this many seconds for a final state; capped by the server | ## Request ```bash curl -X POST https://api.useroutegate.com/api/v1/bills/cable \ -H "Authorization: Bearer $ROUTEGATE_API_KEY" \ -H "Idempotency-Key: ord-00042-a1" \ -H "Content-Type: application/json" \ -d '{ "product_code": "mtn-sme-1gb", "service_id": "", "serviceid": "", "network": "mtn", "biller": "", "smartcard_number": "", "account_number": "", "destination": "+2348012345678", "phone_number": "", "amount_kobo": 100000, "amount": 0, "client_reference": "ord-00042", "metadata": {} }' ``` ## Response `202` ```json {} ``` ## Error `402` Error envelope ```json { "success": false, "data": "", "message": "", "code": "" } ``` ## Give this to your coding agent Read `https://useroutegate.com/docs/api/public/vendCable.md` and implement this call. Keep one `Idempotency-Key` per attempt at the same order, treat `202` as accepted and resolve the final state from the webhook. Amounts are integers in kobo. --- # Vend electricity Source: https://useroutegate.com/docs/api/public/vendElectricity Authenticate with a bearer API key (`rg_test_` in test mode, `rg_live_` in live). | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `Idempotency-Key` | header | string | yes | | | `wait` | query | integer | no | Long-poll up to this many seconds for a final state; capped by the server | ## Request ```bash curl -X POST https://api.useroutegate.com/api/v1/bills/electricity \ -H "Authorization: Bearer $ROUTEGATE_API_KEY" \ -H "Idempotency-Key: ord-00042-a1" \ -H "Content-Type: application/json" \ -d '{ "network": "mtn", "biller": "", "product_code": "mtn-sme-1gb", "meter_number": "", "account_number": "", "destination": "+2348012345678", "phone_number": "", "meter_type": "prepaid", "amount_kobo": 100000, "amount": 0, "client_reference": "ord-00042", "metadata": {} }' ``` ## Response `202` ```json {} ``` ## Error `402` Error envelope ```json { "success": false, "data": "", "message": "", "code": "" } ``` ## Give this to your coding agent Read `https://useroutegate.com/docs/api/public/vendElectricity.md` and implement this call. Keep one `Idempotency-Key` per attempt at the same order, treat `202` as accepted and resolve the final state from the webhook. Amounts are integers in kobo. --- # Verify bill Source: https://useroutegate.com/docs/api/public/verifyBill Authenticate with a bearer API key (`rg_test_` in test mode, `rg_live_` in live). ## Request ```bash curl -X POST https://api.useroutegate.com/api/v1/bills/verify \ -H "Authorization: Bearer $ROUTEGATE_API_KEY" \ -H "Idempotency-Key: ord-00042-a1" \ -H "Content-Type: application/json" \ -d '{ "service": "", "biller": "", "network": "mtn", "account_number": "", "meter_number": "", "smartcard_number": "", "meter_type": "prepaid" }' ``` ## Response `200` ```json { "success": false, "code": "", "message": "", "data": { "customer_name": "", "customer_address": "", "account_number": "", "biller": "", "service": "", "meter_type": "", "outstanding_amount_kobo": 0, "minimum_amount_kobo": 0, "current_package": "", "due_date": "" } } ``` ## Error `401` Error envelope ```json { "success": false, "data": "", "message": "", "code": "" } ``` ## Give this to your coding agent Read `https://useroutegate.com/docs/api/public/verifyBill.md` and implement this call. Keep one `Idempotency-Key` per attempt at the same order, treat `202` as accepted and resolve the final state from the webhook. Amounts are integers in kobo. --- # List networks Source: https://useroutegate.com/docs/api/public/listNetworks Authenticate with a bearer API key (`rg_test_` in test mode, `rg_live_` in live). ## Request ```bash curl -X GET https://api.useroutegate.com/api/v1/catalog/networks \ -H "Authorization: Bearer $ROUTEGATE_API_KEY" ``` ## Response `200` ```json {} ``` ## Error `default` Error envelope ```json { "success": false, "data": "", "message": "", "code": "" } ``` ## Give this to your coding agent Read `https://useroutegate.com/docs/api/public/listNetworks.md` and implement this call. Keep one `Idempotency-Key` per attempt at the same order, treat `202` as accepted and resolve the final state from the webhook. Amounts are integers in kobo. --- # List products Source: https://useroutegate.com/docs/api/public/listProducts Authenticate with a bearer API key (`rg_test_` in test mode, `rg_live_` in live). | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `network` | query | string | no | | | `service` | query | string | no | | ## Request ```bash curl -X GET https://api.useroutegate.com/api/v1/catalog/products \ -H "Authorization: Bearer $ROUTEGATE_API_KEY" ``` ## Response `200` ```json {} ``` ## Error `default` Error envelope ```json { "success": false, "data": "", "message": "", "code": "" } ``` ## Give this to your coding agent Read `https://useroutegate.com/docs/api/public/listProducts.md` and implement this call. Keep one `Idempotency-Key` per attempt at the same order, treat `202` as accepted and resolve the final state from the webhook. Amounts are integers in kobo. --- # List services Source: https://useroutegate.com/docs/api/public/listServices Authenticate with a bearer API key (`rg_test_` in test mode, `rg_live_` in live). ## Request ```bash curl -X GET https://api.useroutegate.com/api/v1/catalog/services \ -H "Authorization: Bearer $ROUTEGATE_API_KEY" ``` ## Response `200` ```json {} ``` ## Error `default` Error envelope ```json { "success": false, "data": "", "message": "", "code": "" } ``` ## Give this to your coding agent Read `https://useroutegate.com/docs/api/public/listServices.md` and implement this call. Keep one `Idempotency-Key` per attempt at the same order, treat `202` as accepted and resolve the final state from the webhook. Amounts are integers in kobo. --- # Vend data Source: https://useroutegate.com/docs/api/public/vendData Authenticate with a bearer API key (`rg_test_` in test mode, `rg_live_` in live). | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `Idempotency-Key` | header | string | yes | | | `wait` | query | integer | no | Long-poll up to this many seconds for a final state; capped by the server | ## Request ```bash curl -X POST https://api.useroutegate.com/api/v1/data \ -H "Authorization: Bearer $ROUTEGATE_API_KEY" \ -H "Idempotency-Key: ord-00042-a1" \ -H "Content-Type: application/json" \ -d '{ "product_code": "mtn-sme-1gb", "service_id": "", "serviceid": "", "destination": "+2348012345678", "receiver_phone_number": "", "phone_number": "", "network": "mtn", "network_id": "", "amount_kobo": 100000, "amount": 0, "client_reference": "ord-00042", "metadata": {} }' ``` ## Response `202` ```json {} ``` ## Error `402` Error envelope ```json { "success": false, "data": "", "message": "", "code": "" } ``` ## Give this to your coding agent Read `https://useroutegate.com/docs/api/public/vendData.md` and implement this call. Keep one `Idempotency-Key` per attempt at the same order, treat `202` as accepted and resolve the final state from the webhook. Amounts are integers in kobo. --- # Get public pricing plans Source: https://useroutegate.com/docs/api/public/getPublicPricingPlans Authenticate with a bearer API key (`rg_test_` in test mode, `rg_live_` in live). | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `network` | query | string | no | | | `service` | query | string | no | | | `network_id` | query | string | no | | | `service_id` | query | string | no | | ## Request ```bash curl -X GET https://api.useroutegate.com/api/v1/pricing/plans \ -H "Authorization: Bearer $ROUTEGATE_API_KEY" ``` ## Response `200` ```json {} ``` ## Error `default` Error envelope ```json { "success": false, "data": "", "message": "", "code": "" } ``` ## Give this to your coding agent Read `https://useroutegate.com/docs/api/public/getPublicPricingPlans.md` and implement this call. Keep one `Idempotency-Key` per attempt at the same order, treat `202` as accepted and resolve the final state from the webhook. Amounts are integers in kobo. --- # List public transactions Source: https://useroutegate.com/docs/api/public/listPublicTransactions Authenticate with a bearer API key (`rg_test_` in test mode, `rg_live_` in live). | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `status` | query | string | no | | | `destination` | query | string | no | Filter by recipient phone number or destination | | `network` | query | string | no | Filter by telecom network code (e.g. mtn, airtel, glo, 9mobile) | | `service` | query | string | no | Filter by service type (e.g. airtime, data) | | `from` | query | string | no | Filter items created on or after this timestamp (RFC 3339) | | `to` | query | string | no | Filter items created on or before this timestamp (RFC 3339) | | `search` | query | string | no | Search by reference, client reference, or destination | | `cursor` | query | string | no | | | `limit` | query | integer | no | | ## Request ```bash curl -X GET https://api.useroutegate.com/api/v1/transactions \ -H "Authorization: Bearer $ROUTEGATE_API_KEY" ``` ## Response `200` ```json {} ``` ## Error `default` Error envelope ```json { "success": false, "data": "", "message": "", "code": "" } ``` ## Give this to your coding agent Read `https://useroutegate.com/docs/api/public/listPublicTransactions.md` and implement this call. Keep one `Idempotency-Key` per attempt at the same order, treat `202` as accepted and resolve the final state from the webhook. Amounts are integers in kobo. --- # Get transaction by reference Source: https://useroutegate.com/docs/api/public/getTransactionByReference Authenticate with a bearer API key (`rg_test_` in test mode, `rg_live_` in live). | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `reference` | path | string | yes | | ## Request ```bash curl -X GET https://api.useroutegate.com/api/v1/transactions/rg_tx_01J8QX4M2K9R7V3N \ -H "Authorization: Bearer $ROUTEGATE_API_KEY" ``` ## Response `200` ```json {} ``` ## Error `404` Error envelope ```json { "success": false, "data": "", "message": "", "code": "" } ``` ## Give this to your coding agent Read `https://useroutegate.com/docs/api/public/getTransactionByReference.md` and implement this call. Keep one `Idempotency-Key` per attempt at the same order, treat `202` as accepted and resolve the final state from the webhook. Amounts are integers in kobo. --- # Vend Source: https://useroutegate.com/docs/api/public/vend Authenticate with a bearer API key (`rg_test_` in test mode, `rg_live_` in live). | Name | In | Type | Required | Description | | --- | --- | --- | --- | --- | | `Idempotency-Key` | header | string | yes | | | `wait` | query | integer | no | Long-poll up to this many seconds for a final state; capped by the server | ## Request ```bash curl -X POST https://api.useroutegate.com/api/v1/vend \ -H "Authorization: Bearer $ROUTEGATE_API_KEY" \ -H "Idempotency-Key: ord-00042-a1" \ -H "Content-Type: application/json" \ -d '{ "receiver_phone_number": "", "destination": "+2348012345678", "phone_number": "", "network": "mtn", "network_id": "", "serviceid": "", "service_id": "", "product_code": "mtn-sme-1gb", "amount": 0, "amount_kobo": 100000, "client_reference": "ord-00042", "metadata": {} }' ``` ## Response `202` ```json {} ``` ## Error `402` Error envelope ```json { "success": false, "data": "", "message": "", "code": "" } ``` ## Give this to your coding agent Read `https://useroutegate.com/docs/api/public/vend.md` and implement this call. Keep one `Idempotency-Key` per attempt at the same order, treat `202` as accepted and resolve the final state from the webhook. Amounts are integers in kobo. --- # Get wallet balance Source: https://useroutegate.com/docs/api/public/getWalletBalance Authenticate with a bearer API key (`rg_test_` in test mode, `rg_live_` in live). ## Request ```bash curl -X GET https://api.useroutegate.com/api/v1/wallet/balance \ -H "Authorization: Bearer $ROUTEGATE_API_KEY" ``` ## Response `200` ```json {} ``` ## Error `default` Error envelope ```json { "success": false, "data": "", "message": "", "code": "" } ``` ## Give this to your coding agent Read `https://useroutegate.com/docs/api/public/getWalletBalance.md` and implement this call. Keep one `Idempotency-Key` per attempt at the same order, treat `202` as accepted and resolve the final state from the webhook. Amounts are integers in kobo.