{"info":{"_postman_id":"f4499ae9-c802-403c-b30a-adde8530acd2","name":"InByte API Service","description":"<html><head></head><body><p>StartFragment</p>\n<h1 id=\"inbyte-api\">InByte API</h1>\n<p><strong>Version:</strong> 1.1<br><strong>Base URL:</strong> <code>https://inbyte.ng/v2/api</code><strong>Protocol:</strong> HTTPS only</p>\n<p>InByte is a Nigerian VTU (Virtual Top-Up) platform that lets you purchase mobile data bundles, airtime, and manage wallet operations programmatically via this REST API.</p>\n<hr>\n<h2 id=\"quick-start\">Quick Start</h2>\n<ol>\n<li><p><strong>Generate an API key</strong> — log into your InByte dashboard, go to <strong>Settings → API Access → Generate new key</strong>, give it a name (e.g. <code>\"Production server\"</code>), and copy the key shown. <strong>This is the only time you'll see the full key</strong> — InByte only stores a hash of it after this point, so if you lose it, revoke it and generate a new one. There's no \"reveal\" or \"reset\" option.</p>\n</li>\n<li><p><strong>Find your User ID</strong> — your 10-digit phone number, no leading <code>0</code> and no country code (e.g. <code>08160271109</code> → <code>8160271109</code>). It's shown on the same Settings page.</p>\n</li>\n<li><p><strong>Make your first call:</strong></p>\n</li>\n</ol>\n<pre class=\"click-to-expand-wrapper is-snippet-wrapper\"><code class=\"language-bash\">curl https://inbyte.ng/v2/api/balance \\\n  -H \"x-user-id: 8160271109\" \\\n  -H \"x-api-key: inbyte_live_your_key_here\"\n\n</code></pre>\n<pre class=\"click-to-expand-wrapper is-snippet-wrapper\"><code class=\"language-json\">{ \"success\": true, \"balance\": 1250.00 }\n\n</code></pre>\n<p>That's it — no OAuth flow, no token refresh, no client secrets. Every request just carries these two headers.</p>\n<hr>\n<h2 id=\"authentication\">Authentication</h2>\n<div class=\"click-to-expand-wrapper is-table-wrapper\"><table>\n<thead>\n<tr>\n<th>Header</th>\n<th>Type</th>\n<th>Description</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><code>x-user-id</code></td>\n<td><code>string</code></td>\n<td>Your 10-digit phone number, e.g. <code>8160271109</code> (no leading zero, no country code)</td>\n</tr>\n<tr>\n<td><code>x-api-key</code></td>\n<td><code>string</code></td>\n<td>A key generated from your dashboard. Starts with <code>inbyte_live_</code>.</td>\n</tr>\n</tbody>\n</table>\n</div><h3 id=\"getting-and-managing-your-api-key\">Getting and managing your API key</h3>\n<p>API keys are fully self-service — there's no support ticket required:</p>\n<ul>\n<li><p><strong>Generate:</strong> Dashboard → Settings → API Access → <em>Generate new key</em>. Name it something that identifies where it's used (e.g. <code>\"Order bot\"</code>, <code>\"Inventory sync\"</code>) — you can hold multiple keys at once, which makes it easy to revoke one integration's access without breaking the others.</p>\n</li>\n<li><p><strong>Reveal:</strong> Only happens once, at creation. InByte stores a one-way hash of your key, not the key itself — this is intentional and can't be changed, so treat the one-time reveal as your only backup.</p>\n</li>\n<li><p><strong>Revoke:</strong> Dashboard → Settings → API Access → <em>Revoke</em>, next to any key. Takes effect <strong>immediately</strong> — any request using that key fails from that moment on, no propagation delay.</p>\n</li>\n<li><p><strong>Last used:</strong> Each key's row shows when it was last used, so you can spot a key that should've been revoked a while ago.</p>\n</li>\n</ul>\n<p>There is currently no way to rotate a key in place — revoking and generating a new one is the rotation flow.</p>\n<h3 id=\"authentication-failure-responses\">Authentication failure responses</h3>\n<div class=\"click-to-expand-wrapper is-table-wrapper\"><table>\n<thead>\n<tr>\n<th>Scenario</th>\n<th>HTTP Status</th>\n<th>Response</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>Missing <code>x-user-id</code></td>\n<td><code>401</code></td>\n<td><code>{ \"success\": false, \"message\": \"Missing required header: x-user-id.\" }</code></td>\n</tr>\n<tr>\n<td>Missing <code>x-api-key</code></td>\n<td><code>401</code></td>\n<td><code>{ \"success\": false, \"message\": \"Missing required header: x-api-key.\" }</code></td>\n</tr>\n<tr>\n<td>Both missing</td>\n<td><code>401</code></td>\n<td><code>{ \"success\": false, \"message\": \"Missing required headers: x-user-id and x-api-key.\" }</code></td>\n</tr>\n<tr>\n<td>Key doesn't exist / never generated</td>\n<td><code>403</code></td>\n<td><code>{ \"success\": false, \"message\": \"Invalid credentials.\" }</code></td>\n</tr>\n<tr>\n<td>Key was revoked</td>\n<td><code>403</code></td>\n<td><code>{ \"success\": false, \"message\": \"Invalid credentials.\" }</code></td>\n</tr>\n<tr>\n<td>Key is valid but <code>x-user-id</code> doesn't match the account it belongs to</td>\n<td><code>403</code></td>\n<td><code>{ \"success\": false, \"message\": \"Invalid credentials.\" }</code></td>\n</tr>\n</tbody>\n</table>\n</div><p>Note that all <code>403</code> cases return the same generic message deliberately — the API doesn't distinguish \"wrong key\" from \"revoked key\" from \"mismatched user\" in the response body, so a bad actor probing for valid userIds or key states can't learn anything from the error text. If you're debugging your own integration and get an unexplained <code>403</code>, check (in order): is the key still active in your dashboard, does the <code>x-user-id</code> you're sending match the account that generated the key, and did you copy the whole key including the <code>inbyte_live_</code> prefix.</p>\n<hr>\n<h2 id=\"response-format\">Response Format</h2>\n<p>All responses are JSON. Every response includes a <code>success</code> boolean as the first field so you can always branch on it without inspecting HTTP status codes.</p>\n<pre class=\"click-to-expand-wrapper is-snippet-wrapper\"><code class=\"language-json\">// Success shape\n{\n  \"success\": true,\n  ...                  // endpoint-specific fields\n}\n// Error shape\n{\n  \"success\": false,\n  \"message\": \"Human-readable explanation of what went wrong.\"\n}\n\n</code></pre>\n<h3 id=\"http-status-codes\">HTTP Status Codes</h3>\n<div class=\"click-to-expand-wrapper is-table-wrapper\"><table>\n<thead>\n<tr>\n<th>Code</th>\n<th>Meaning</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><code>200</code></td>\n<td>Request succeeded</td>\n</tr>\n<tr>\n<td><code>400</code></td>\n<td>Bad request — missing field, wrong PIN, invalid network, insufficient balance</td>\n</tr>\n<tr>\n<td><code>401</code></td>\n<td>Missing authentication header(s)</td>\n</tr>\n<tr>\n<td><code>403</code></td>\n<td>Invalid credentials (bad, revoked, or mismatched API key)</td>\n</tr>\n<tr>\n<td><code>404</code></td>\n<td>Resource not found (user, bundle)</td>\n</tr>\n<tr>\n<td><code>500</code></td>\n<td>Server or provider error — see <code>message</code> field for detail</td>\n</tr>\n</tbody>\n</table>\n</div><hr>\n<h2 id=\"transaction-pin\">Transaction PIN</h2>\n<p>The <code>/data</code> and <code>/airtime</code> endpoints require a 4-digit transaction PIN (<code>pin</code> field in the request body). This is separate from both your account password and your API key — it's the same PIN you'd use confirming a purchase in the WhatsApp or Telegram bot. If you haven't set one yet, it's configured from Dashboard → Settings → Security.</p>\n<p><strong>Design note for integrators:</strong> since the PIN travels in the request body on every purchase call, make sure your integration stores it as a secret with the same care as the API key itself — not in logs, not in client-side code, not in version control.</p>\n<hr>\n<h2 id=\"wallet--refund-guarantee\">Wallet &amp; Refund Guarantee</h2>\n<p>All purchases are deducted from your InByte wallet balance. The deduction is <strong>atomic</strong> — meaning even if two purchases happen at the same time, your balance will never go negative due to a race condition.</p>\n<p>If a purchase is submitted to a provider and the provider returns a failure, <strong>your wallet is automatically refunded in full</strong> before the error response is sent. You will never be charged for a failed purchase.</p>\n<h3 id=\"integration-note-no-idempotency-support-yet\">Integration note: no idempotency support yet</h3>\n<p>There is currently no idempotency-key mechanism on <code>/data</code> or <code>/airtime</code>. If your client times out waiting for a response and retries the same purchase, the API has no way to recognize it as a duplicate — it will attempt a <strong>second</strong> purchase. If your integration retries on timeout, either:</p>\n<ul>\n<li><p>Check <code>/api/transactions</code> for a matching recent entry before retrying, or</p>\n</li>\n<li><p>Check <code>/api/balance</code> to confirm whether the deduction already happened</p>\n</li>\n</ul>\n<p>before firing the same request again. This is a known gap, not a documented feature — treat retries as your responsibility to de-duplicate for now.</p>\n<h3 id=\"no-rate-limiting\">No rate limiting</h3>\n<p>As of this version, the API does not enforce rate limits. Please still be a good citizen — cache <code>/api/bundles</code> responses on your side (bundle prices don't change every second) rather than polling it before every purchase.</p>\n<hr>\n<h2 id=\"endpoints\">Endpoints</h2>\n<hr>\n<h3 id=\"get-apibalance\"><code>GET /api/balance</code></h3>\n<p>Returns your current wallet balance in NGN.</p>\n<p><strong>No request body required.</strong></p>\n<pre class=\"click-to-expand-wrapper is-snippet-wrapper\"><code class=\"language-bash\">curl https://inbyte.ng/v2/api/balance \\\n  -H \"x-user-id: 8160271109\" \\\n  -H \"x-api-key: inbyte_live_your_key_here\"\n\n</code></pre>\n<p><strong>Example response:</strong></p>\n<pre class=\"click-to-expand-wrapper is-snippet-wrapper\"><code class=\"language-json\">{\n  \"success\": true,\n  \"balance\": 1250.00\n}\n\n</code></pre>\n<p><strong>Use this endpoint to:</strong></p>\n<ul>\n<li><p>Display available balance before initiating a purchase</p>\n</li>\n<li><p>Confirm a deduction after a transaction</p>\n</li>\n</ul>\n<hr>\n<h3 id=\"post-apibundles\"><code>POST /api/bundles</code></h3>\n<p>Returns available data bundles, grouped by bundle type and sorted cheapest-first within each group. Disabled bundles are excluded automatically.</p>\n<p><strong>Request body:</strong></p>\n<pre class=\"click-to-expand-wrapper is-snippet-wrapper\"><code class=\"language-json\">{\n  \"network\": \"mtn\"\n}\n\n</code></pre>\n<div class=\"click-to-expand-wrapper is-table-wrapper\"><table>\n<thead>\n<tr>\n<th>Field</th>\n<th>Type</th>\n<th>Required</th>\n<th>Description</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><code>network</code></td>\n<td><code>string</code></td>\n<td>No</td>\n<td>Filter by network: <code>mtn</code>, <code>airtel</code>, <code>glo</code>, <code>9mobile</code>. <strong>Omit entirely to get all four networks in one call.</strong></td>\n</tr>\n</tbody>\n</table>\n</div><pre class=\"click-to-expand-wrapper is-snippet-wrapper\"><code class=\"language-bash\">curl -X POST https://inbyte.ng/v2/api/bundles \\\n  -H \"x-user-id: 8160271109\" \\\n  -H \"x-api-key: inbyte_live_your_key_here\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"network\": \"mtn\"}'\n\n</code></pre>\n<p><strong>Example response:</strong></p>\n<pre class=\"click-to-expand-wrapper is-snippet-wrapper\"><code class=\"language-json\">{\n  \"success\": true,\n  \"bundles\": {\n    \"mtn\": {\n      \"count\": 14,\n      \"grouped\": {\n        \"sme\": [\n          {\n            \"bundleId\":   \"mtn-1gb-sme\",\n            \"bundleName\": \"1GB SME\",\n            \"validity\":   \"30 Days\",\n            \"salePrice\":  280,\n            \"dataType\":   \"SME\"\n          }\n        ],\n        \"gifting\": [ \"...\" ],\n        \"cg\":      [ \"...\" ]\n      }\n    }\n  }\n}\n\n</code></pre>\n<p><strong>Error response (no active bundles at all):</strong></p>\n<pre class=\"click-to-expand-wrapper is-snippet-wrapper\"><code class=\"language-json\">{ \"success\": false, \"message\": \"No active bundles found.\" }\n\n</code></pre>\n<blockquote>\n</blockquote>\n<p><strong>Important:</strong> Save the <code>bundleId</code> from this response — it's required when calling <code>POST /api/data</code>. Bundle IDs and prices can change, so don't hardcode them long-term; re-fetch periodically.</p>\n<p><strong>Bundle types you may see:</strong></p>\n<div class=\"click-to-expand-wrapper is-table-wrapper\"><table>\n<thead>\n<tr>\n<th>Key</th>\n<th>Full name</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><code>sme</code></td>\n<td>SME Data</td>\n</tr>\n<tr>\n<td><code>sme1</code> / <code>sme2</code></td>\n<td>SME variants</td>\n</tr>\n<tr>\n<td><code>cg</code></td>\n<td>Corporate Gifting</td>\n</tr>\n<tr>\n<td><code>gifting</code></td>\n<td>Gifting Data</td>\n</tr>\n<tr>\n<td><code>extra</code></td>\n<td>Extra Data</td>\n</tr>\n<tr>\n<td><code>promo</code></td>\n<td>Promotional</td>\n</tr>\n<tr>\n<td><code>social</code></td>\n<td>Social Bundle</td>\n</tr>\n</tbody>\n</table>\n</div><hr>\n<h3 id=\"post-apidata\"><code>POST /api/data</code></h3>\n<p>Purchases a mobile data bundle and sends it to a recipient phone number.</p>\n<p><strong>Request body:</strong></p>\n<pre class=\"click-to-expand-wrapper is-snippet-wrapper\"><code class=\"language-json\">{\n  \"bundleId\":    \"mtn-1gb-sme\",\n  \"network\":     \"mtn\",\n  \"phoneNumber\": \"08160271109\",\n  \"pin\":         \"1234\"\n}\n\n</code></pre>\n<div class=\"click-to-expand-wrapper is-table-wrapper\"><table>\n<thead>\n<tr>\n<th>Field</th>\n<th>Type</th>\n<th>Required</th>\n<th>Description</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><code>bundleId</code></td>\n<td><code>string</code></td>\n<td>Yes</td>\n<td>The <code>bundleId</code> from <code>POST /api/bundles</code></td>\n</tr>\n<tr>\n<td><code>network</code></td>\n<td><code>string</code></td>\n<td>Yes</td>\n<td><code>mtn</code>, <code>airtel</code>, <code>glo</code>, or <code>9mobile</code> — must match the network the bundle belongs to</td>\n</tr>\n<tr>\n<td><code>phoneNumber</code></td>\n<td><code>string</code></td>\n<td>Yes</td>\n<td>11-digit Nigerian number of the recipient (can be different from your own number)</td>\n</tr>\n<tr>\n<td><code>pin</code></td>\n<td><code>string</code></td>\n<td>Yes</td>\n<td>Your 4-digit transaction PIN</td>\n</tr>\n</tbody>\n</table>\n</div><pre class=\"click-to-expand-wrapper is-snippet-wrapper\"><code class=\"language-bash\">curl -X POST https://inbyte.ng/v2/api/data \\\n  -H \"x-user-id: 8160271109\" \\\n  -H \"x-api-key: inbyte_live_your_key_here\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"bundleId\": \"mtn-1gb-sme\",\n    \"network\": \"mtn\",\n    \"phoneNumber\": \"08160271109\",\n    \"pin\": \"1234\"\n  }'\n\n</code></pre>\n<p><strong>Success response:</strong></p>\n<pre class=\"click-to-expand-wrapper is-snippet-wrapper\"><code class=\"language-json\">{\n  \"success\":        true,\n  \"message\":        \"1GB SME sent to 08160271109 successfully.\",\n  \"transactionRef\": \"DATA|29110FFB-F7D8-442F-81C4-7661A9F95EB8\",\n  \"newBalance\":     970.00\n}\n\n</code></pre>\n<p><strong>Possible error responses:</strong></p>\n<pre class=\"click-to-expand-wrapper is-snippet-wrapper\"><code class=\"language-json\">{ \"success\": false, \"message\": \"Incorrect PIN.\" }\n{ \"success\": false, \"message\": \"Bundle not found.\" }\n{ \"success\": false, \"message\": \"This bundle is currently unavailable.\" }\n{ \"success\": false, \"message\": \"Insufficient wallet balance. Required: ₦280, Available: ₦50.\" }\n{ \"success\": false, \"message\": \"Missing required fields: phoneNumber, pin.\" }\n// Provider-side failure — wallet is automatically refunded\n{ \"success\": false, \"message\": \"Purchase failed at provider. Your wallet has been refunded.\" }\n// Hard provider exception — wallet was never charged\n{ \"success\": false, \"message\": \"Provider error. Your wallet was not charged.\" }\n\n</code></pre>\n<p><strong>How the purchase flow works:</strong></p>\n<ol>\n<li><p>Your PIN is verified</p>\n</li>\n<li><p>The bundle is looked up and confirmed active</p>\n</li>\n<li><p>Your balance is checked — if insufficient, the request stops here (no deduction)</p>\n</li>\n<li><p>Your wallet is deducted atomically</p>\n</li>\n<li><p>The request is sent to the data provider</p>\n</li>\n<li><p>If the provider fails → your wallet is refunded in full before the response is sent</p>\n</li>\n<li><p>The transaction is saved to your history</p>\n</li>\n</ol>\n<hr>\n<h3 id=\"post-apiairtime\"><code>POST /api/airtime</code></h3>\n<p>Purchases airtime and sends it to a recipient phone number.</p>\n<p><strong>Request body:</strong></p>\n<pre class=\"click-to-expand-wrapper is-snippet-wrapper\"><code class=\"language-json\">{\n  \"network\":     \"mtn\",\n  \"amount\":      200,\n  \"phoneNumber\": \"08160271109\",\n  \"pin\":         \"1234\"\n}\n\n</code></pre>\n<div class=\"click-to-expand-wrapper is-table-wrapper\"><table>\n<thead>\n<tr>\n<th>Field</th>\n<th>Type</th>\n<th>Required</th>\n<th>Description</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><code>network</code></td>\n<td><code>string</code></td>\n<td>Yes</td>\n<td><code>mtn</code>, <code>airtel</code>, <code>glo</code>, or <code>9mobile</code></td>\n</tr>\n<tr>\n<td><code>amount</code></td>\n<td><code>number</code></td>\n<td>Yes</td>\n<td>Amount in NGN. Must be a positive number.</td>\n</tr>\n<tr>\n<td><code>phoneNumber</code></td>\n<td><code>string</code></td>\n<td>Yes</td>\n<td>11-digit Nigerian number of the recipient</td>\n</tr>\n<tr>\n<td><code>pin</code></td>\n<td><code>string</code></td>\n<td>Yes</td>\n<td>Your 4-digit transaction PIN</td>\n</tr>\n</tbody>\n</table>\n</div><pre class=\"click-to-expand-wrapper is-snippet-wrapper\"><code class=\"language-bash\">curl -X POST https://inbyte.ng/v2/api/airtime \\\n  -H \"x-user-id: 8160271109\" \\\n  -H \"x-api-key: inbyte_live_your_key_here\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"network\": \"mtn\",\n    \"amount\": 200,\n    \"phoneNumber\": \"08160271109\",\n    \"pin\": \"1234\"\n  }'\n\n</code></pre>\n<p><strong>Success response:</strong></p>\n<pre class=\"click-to-expand-wrapper is-snippet-wrapper\"><code class=\"language-json\">{\n  \"success\":        true,\n  \"message\":        \"₦200 MTN airtime sent to 08160271109 successfully.\",\n  \"transactionRef\": \"AIRT1781780460231\",\n  \"newBalance\":     800.00\n}\n\n</code></pre>\n<p><strong>Possible error responses:</strong></p>\n<pre class=\"click-to-expand-wrapper is-snippet-wrapper\"><code class=\"language-json\">{ \"success\": false, \"message\": \"Incorrect PIN.\" }\n{ \"success\": false, \"message\": \"Amount must be a positive number.\" }\n{ \"success\": false, \"message\": \"Invalid network \\\"etl\\\". Valid options: mtn, airtel, glo, 9mobile.\" }\n{ \"success\": false, \"message\": \"Insufficient wallet balance. Required: ₦200, Available: ₦50.\" }\n{ \"success\": false, \"message\": \"Missing required fields: amount, pin.\" }\n// Provider failure — wallet refunded\n{ \"success\": false, \"message\": \"Airtime purchase failed. Your wallet has been refunded.\" }\n\n</code></pre>\n<p>The purchase flow is the same as <code>/api/data</code> — balance is checked before deduction, and a provider failure triggers an automatic refund.</p>\n<hr>\n<h3 id=\"get-apitransactions\"><code>GET /api/transactions</code></h3>\n<p>Returns your transaction history, newest first. At most 20 records are stored per user — older ones are automatically pruned after each new purchase.</p>\n<p><strong>Query parameters:</strong></p>\n<div class=\"click-to-expand-wrapper is-table-wrapper\"><table>\n<thead>\n<tr>\n<th>Parameter</th>\n<th>Type</th>\n<th>Required</th>\n<th>Description</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><code>limit</code></td>\n<td><code>number</code></td>\n<td>No</td>\n<td>How many records to return. Default <code>20</code>. Maximum <code>20</code>. Example: <code>?limit=10</code></td>\n</tr>\n</tbody>\n</table>\n</div><pre class=\"click-to-expand-wrapper is-snippet-wrapper\"><code class=\"language-bash\">curl \"https://inbyte.ng/v2/api/transactions?limit=10\" \\\n  -H \"x-user-id: 8160271109\" \\\n  -H \"x-api-key: inbyte_live_your_key_here\"\n\n</code></pre>\n<p><strong>Example response:</strong></p>\n<pre class=\"click-to-expand-wrapper is-snippet-wrapper\"><code class=\"language-json\">{\n  \"success\": true,\n  \"count\": 2,\n  \"transactions\": [\n    {\n      \"transactionRef\":       \"DATA|29110FFB-F7D8-442F-81C4-7661A9F95EB8\",\n      \"transaction_category\": \"DATA PURCHASE\",\n      \"transaction_type\":     \"DEBIT\",\n      \"amount\":               280,\n      \"prev_bal\":             1250.00,\n      \"new_bal\":              970.00,\n      \"status\":               \"success\",\n      \"service\":              \"1GB SME to 08160271109\",\n      \"channel\":              \"API\",\n      \"createdAt\":            1781673930215\n    },\n    {\n      \"transactionRef\":       \"AIRT1781673000000\",\n      \"transaction_category\": \"AIRTIME PURCHASE\",\n      \"transaction_type\":     \"DEBIT\",\n      \"amount\":               200,\n      \"prev_bal\":             1450.00,\n      \"new_bal\":              1250.00,\n      \"status\":               \"success\",\n      \"service\":              \"MTN ₦200 airtime to 08160271109\",\n      \"channel\":              \"API\",\n      \"createdAt\":            1781670000000\n    }\n  ]\n}\n\n</code></pre>\n<p><strong>Transaction fields explained:</strong></p>\n<div class=\"click-to-expand-wrapper is-table-wrapper\"><table>\n<thead>\n<tr>\n<th>Field</th>\n<th>Description</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><code>transactionRef</code></td>\n<td>Unique reference for this transaction, sourced from the provider where possible</td>\n</tr>\n<tr>\n<td><code>transaction_category</code></td>\n<td><code>DATA PURCHASE</code>, <code>AIRTIME PURCHASE</code>, or <code>WALLET FUNDING</code></td>\n</tr>\n<tr>\n<td><code>transaction_type</code></td>\n<td><code>DEBIT</code> for purchases, <code>CREDIT</code> for wallet funding</td>\n</tr>\n<tr>\n<td><code>amount</code></td>\n<td>NGN amount involved in this transaction</td>\n</tr>\n<tr>\n<td><code>prev_bal</code></td>\n<td>Your wallet balance before this transaction</td>\n</tr>\n<tr>\n<td><code>new_bal</code></td>\n<td>Your wallet balance after this transaction</td>\n</tr>\n<tr>\n<td><code>status</code></td>\n<td><code>success</code> or <code>fail</code></td>\n</tr>\n<tr>\n<td><code>service</code></td>\n<td>Human-readable description of what was purchased and for whom</td>\n</tr>\n<tr>\n<td><code>channel</code></td>\n<td>Where the order originated. Purchases made through this API are always <code>API</code>.</td>\n</tr>\n<tr>\n<td><code>createdAt</code></td>\n<td>Unix timestamp in milliseconds</td>\n</tr>\n</tbody>\n</table>\n</div><p><strong>Empty history response:</strong></p>\n<pre class=\"click-to-expand-wrapper is-snippet-wrapper\"><code class=\"language-json\">{ \"success\": true, \"count\": 0, \"transactions\": [] }\n\n</code></pre>\n<hr>\n<h2 id=\"quick-reference\">Quick Reference</h2>\n<div class=\"click-to-expand-wrapper is-table-wrapper\"><table>\n<thead>\n<tr>\n<th>Method</th>\n<th>Endpoint</th>\n<th>Auth</th>\n<th>Body</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><code>GET</code></td>\n<td><code>/api/balance</code></td>\n<td>Required</td>\n<td>None</td>\n</tr>\n<tr>\n<td><code>POST</code></td>\n<td><code>/api/bundles</code></td>\n<td>Required</td>\n<td><code>{ network? }</code></td>\n</tr>\n<tr>\n<td><code>POST</code></td>\n<td><code>/api/data</code></td>\n<td>Required</td>\n<td><code>{ bundleId, network, phoneNumber, pin }</code></td>\n</tr>\n<tr>\n<td><code>POST</code></td>\n<td><code>/api/airtime</code></td>\n<td>Required</td>\n<td><code>{ network, amount, phoneNumber, pin }</code></td>\n</tr>\n<tr>\n<td><code>GET</code></td>\n<td><code>/api/transactions</code></td>\n<td>Required</td>\n<td>None (<code>?limit</code> query)</td>\n</tr>\n</tbody>\n</table>\n</div><hr>\n<h2 id=\"troubleshooting-checklist\">Troubleshooting Checklist</h2>\n<p>Before reaching out for support, check:</p>\n<ul>\n<li><p>[ ] Both <code>x-user-id</code> and <code>x-api-key</code> headers are present on <strong>every</strong> request, including <code>GET</code> calls</p>\n</li>\n<li><p>[ ] <code>x-user-id</code> is 10 digits, no leading <code>0</code>, no <code>234</code> prefix</p>\n</li>\n<li><p>[ ] The API key includes the full <code>inbyte_live_</code> prefix and hasn't been truncated in copy/paste</p>\n</li>\n<li><p>[ ] The key hasn't been revoked (check Dashboard → Settings → API Access)</p>\n</li>\n<li><p>[ ] <code>Content-Type: application/json</code> is set on <code>POST</code> requests with a body</p>\n</li>\n<li><p>[ ] The transaction PIN sent matches what's configured in Dashboard → Settings → Security — not your account password</p>\n</li>\n</ul>\n<hr>\n<h2 id=\"changelog\">Changelog</h2>\n<p><strong>2.1</strong> — Self-service API key management. Keys are now generated, named, and revoked directly from the dashboard (Settings → API Access) instead of requiring a support request. Multiple keys per account are supported. Keys are shown once at creation and never again — only a hash is retained. Revocation is now immediate.</p>\n<p><strong>2.0</strong> — Initial public release: <code>/balance</code>, <code>/bundles</code>, <code>/data</code>, <code>/airtime</code>, <code>/transactions</code>.</p>\n<hr>\n<h2 id=\"support\">Support</h2>\n<p>For PIN resets or general integration issues, reach out via the InByte platform or contact the development team directly. API key generation and revocation no longer require contacting support — see <a href=\"https://claude.ai/chat/d5543705-12f6-4cdd-a993-ac62a3bf203b#getting-and-managing-your-api-key\">Getting and managing your API key</a> above.</p>\n<p>EndFragment</p>\n</body></html>","schema":"https://schema.getpostman.com/json/collection/v2.0.0/collection.json","toc":[{"content":"InByte API","slug":"inbyte-api"}],"owner":"38716395","collectionId":"f4499ae9-c802-403c-b30a-adde8530acd2","publishedId":"2sBXwwn7Wf","public":true,"customColor":{"top-bar":"FFFFFF","right-sidebar":"303030","highlight":"FF6C37"},"publishDate":"2026-06-23T11:54:43.000Z"},"item":[{"name":"Get Balance","id":"21ce2716-0ae5-4b06-adec-4e6ca6c57939","protocolProfileBehavior":{"disableBodyPruning":true},"request":{"method":"GET","header":[{"key":"x-user-id","value":"","type":"text"},{"key":"x-api-key","value":"","type":"text"}],"body":{"mode":"raw","raw":"","options":{"raw":{"language":"json"}}},"url":" /balance","urlObject":{"path":["balance"],"host":[" "],"query":[],"variable":[]}},"response":[{"id":"ea7d0660-c97c-42d5-a5db-c1e3665e9e25","name":"New Request","originalRequest":{"method":"GET","header":[{"key":"x-user-id","value":"","type":"text"},{"key":"x-api-key","value":"","type":"text"}],"url":"/balance"},"_postman_previewlanguage":"","header":[],"cookie":[{"expires":"Invalid Date","domain":"","path":""}],"responseTime":null,"body":""}],"_postman_id":"21ce2716-0ae5-4b06-adec-4e6ca6c57939"},{"name":"Get Bundles","id":"e34cae6a-db5a-4fb8-8e4e-1fcf789f4d4d","protocolProfileBehavior":{"disableBodyPruning":true},"request":{"method":"POST","header":[{"key":"x-user-id","value":"","type":"text"},{"key":"x-api-key","value":"","type":"text"}],"body":{"mode":"raw","raw":"{\r\n    \"network\": \"\"\r\n}","options":{"raw":{"language":"json"}}},"url":"/bundles","urlObject":{"path":["bundles"],"host":[""],"query":[],"variable":[]}},"response":[{"id":"52470f2c-0560-4aa3-8ced-b41c1ac8f0ee","name":"Get Bundles","originalRequest":{"method":"POST","header":[{"key":"x-user-id","value":"","type":"text"},{"key":"x-api-key","value":"","type":"text"},{"key":"Content-Type","value":"application/json","type":"text"}],"body":{"mode":"raw","raw":"{\r\n    \"network\": \"mtn\"\r\n}","options":{"raw":{"language":"json"}}},"url":"/bundles"},"_postman_previewlanguage":"","header":[],"cookie":[{"expires":"Invalid Date","domain":"","path":""}],"responseTime":null,"body":""}],"_postman_id":"e34cae6a-db5a-4fb8-8e4e-1fcf789f4d4d"},{"name":"Buy Data","id":"9281659e-88de-4495-a9f3-2f3475275060","protocolProfileBehavior":{"disableBodyPruning":true},"request":{"method":"POST","header":[],"url":"/data","urlObject":{"path":["data"],"host":[""],"query":[],"variable":[]}},"response":[{"id":"47030661-6163-486b-a4b1-fad59e3a9c8c","name":"Buy Data","originalRequest":{"method":"GET","header":[{"key":"x-user-id","value":"","type":"text"},{"key":"x-api-key","value":"","type":"text"},{"key":"Content-Type","value":"application/json","type":"text"}],"body":{"mode":"raw","raw":"{\r\n    \"bundleId\":\"ms1_7\",\r\n    \"network\":\"mtn\",\r\n    \"phoneNumber\": \"07041143286\",\r\n    \"pin\":\"2301\"\r\n}","options":{"raw":{"language":"json"}}},"url":"/data"},"_postman_previewlanguage":"","header":[],"cookie":[{"expires":"Invalid Date","domain":"","path":""}],"responseTime":null,"body":""}],"_postman_id":"9281659e-88de-4495-a9f3-2f3475275060"},{"name":"Buy Airtime","id":"77f9fdcd-914e-4faf-8d5b-77cf79aa0a24","protocolProfileBehavior":{"disableBodyPruning":true},"request":{"method":"POST","header":[{"key":"x-user-id","value":"","type":"text"},{"key":"x-api-key","value":"","type":"text"}],"body":{"mode":"raw","raw":"{\r\n  \"network\":     \"mtn\",\r\n  \"amount\":      200,\r\n  \"phoneNumber\": \"08160271109\",\r\n  \"pin\":         \"1234\"\r\n}","options":{"raw":{"language":"json"}}},"url":"/airtime","urlObject":{"path":["airtime"],"host":[""],"query":[],"variable":[]}},"response":[],"_postman_id":"77f9fdcd-914e-4faf-8d5b-77cf79aa0a24"},{"name":"Get Transactions","id":"7d326c2b-f333-4376-a05a-2f3503791b6c","protocolProfileBehavior":{"disableBodyPruning":true},"request":{"method":"GET","header":[{"key":"x-user-id","value":"","type":"text"},{"key":"x-api-key","value":"","type":"text"}],"url":"/transactions","urlObject":{"path":["transactions"],"host":[""],"query":[],"variable":[]}},"response":[],"_postman_id":"7d326c2b-f333-4376-a05a-2f3503791b6c"}],"variable":[{"key":"{","value":""},{"key":"baseUrl","value":""},{"key":"userid","value":""},{"key":"apiKey","value":""}]}