API Reference · v1

Globconnects API

A single REST API for airtime, data, cable TV, electricity, international top-ups, gift cards and travel eSIMs. All requests and responses are JSON, authenticated with your API keys, and every call returns the same response envelope. Failed orders are reversed to your wallet automatically.

1
Create API keys

In Settings → API keys. You receive a public and a secret key.

2
Fund your wallet

Every sale is charged to your Globconnects wallet balance.

3
Make a request

Verify with GET /ping, then transact with POST /purchase.

Contents

Authentication

Every request must include your public and secret keys. Choose one of two schemes. Both are equivalent.

Authorization header (recommended)
Authorization: Bearer PUBLIC_KEY:SECRET_KEY
Discrete headers
X-Public-Key: PUBLIC_KEY
X-Secret-Key: SECRET_KEY

Keys are created and revoked in Settings → API keys. Keep the secret key server-side; never expose it in client code. All endpoints are relative to the base URL:

base url
https://globconnects.net/api/v1

API Scopes & Access Controls

Security

API keys can be scoped with granular permissions, IP address allowlists, and automatic expiration to minimize blast radius in the event of credential leakage.

ScopeGrants Access To
read:balanceAccount verification (/ping) and wallet balance lookup (/balance).
read:catalogCatalog queries (/services, /products, airtime/data operators, eSIM packages, gift cards).
write:purchaseMutating purchase endpoints (airtime, data, cable, electricity, eSIMs, gift cards).
read:cardsVirtual card listing and metadata retrieval (/cards, /cards/{ref}).
write:cardsVirtual card creation, funding, and freezing (/cards/create, /cards/fund, /cards/freeze).
cards:revealSensitive card details (/cards/{ref}/reveal). Delivers live PAN & CVV over encrypted TLS.
read:transactionsTransaction history, detail lookups, and eSIM profile statuses.
  • IP Allowlist: Restrict key usage to specific static IPv4/IPv6 addresses or CIDR blocks (e.g. 198.51.100.4, 203.0.113.0/24).
  • Key Expiration: Optionally set an automatic TTL (30 days, 90 days, 1 year) after which requests will return 401 Unauthorized.

Idempotency

Recommended

To safely retry purchase requests without creating double debits or accidental duplicate transactions, pass a unique Idempotency-Key header (e.g. UUIDv4) with every mutating request (POST /purchase, POST /cards/*, POST /esim/*, POST /airtime/purchase).

Idempotency header
Idempotency-Key: 9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d
  • 24-Hour Replay Window: If a request with an existing idempotency key is retried within 24 hours, our server safely replays the original response with the Idempotency-Replay: true header and zero additional wallet debit.
  • Concurrency Protection: Simultaneous duplicate requests in-flight return 409 Conflict until the initial transaction has settled.

Response format

Responses share one envelope. On success, status is true and the result is in data. On failure, status is false with a human-readable message, and data is null.

envelope
{
  "status": true,        // false on error
  "message": "…",       // human-readable summary
  "data": { … }          // result, or null on error
}

Endpoint index

All v1 endpoints at a glance.

MethodEndpointDescription
GET/pingVerify API keys
GET/balanceWallet balance
GET/servicesFull catalogue
GET/products/{category}Products in a category
POST/purchaseBuy airtime, data, cable, power…
GET/transactions/{reference}Retrieve a transaction
GET/airtime/operatorsInternational airtime operators
POST/airtime/purchaseSend airtime abroad
GET/data/operatorsInternational data operators
POST/data/purchaseSend data abroad
GET/giftcardsGift-card products
POST/giftcards/purchaseBuy a gift card
GET/esim/packageseSIM plans for a destination
POST/esim/purchaseBuy an eSIM
GET/esim/status/{reference}Retrieve an eSIM profile
GET/cardsList virtual USD cards business KYC
POST/cards/createIssue a virtual USD card business KYC
POST/cards/fundTop up a card business KYC
POST/cards/freezeFreeze or unfreeze a card business KYC
GET/cards/{reference}Retrieve one card business KYC
POST/cards/{reference}/revealFull PAN & CVV business KYC
GET/ping

Confirms your API keys are valid. Does not touch your wallet.

request
curl https://globconnects.net/api/v1/ping \
  -H "Authorization: Bearer PUBLIC_KEY:SECRET_KEY"
GET/balance

Returns your current wallet balance.

request
curl https://globconnects.net/api/v1/balance -H "Authorization: Bearer PUBLIC_KEY:SECRET_KEY"
response
{ "status": true, "data": { "wallet_balance": 15000.00, "currency": "NGN" } }
GET/services

Returns the full catalogue as categories → products → variations. Use a variation's code as the variation_code when creating a purchase.

response
{ "status": true, "data": [
  { "code": "data-gifting", "service_type": "data", "products": [
    { "code": "data-gifting-mtn", "network": "MTN", "variations": [
      { "code": "mtn-1gb-30", "price": 600, "data_value": "1GB" }
    ] } ] } ] }
GET/products/{category}

Returns the products in a single category.

Path parameters

ParameterType Description
category string required Category code, e.g. airtime, data-gifting, cable, electricity, education.
request
curl https://globconnects.net/api/v1/products/airtime -H "Authorization: Bearer PUBLIC_KEY:SECRET_KEY"
POST/purchaseCore

Creates a purchase for any local service. Manual-amount services (airtime, electricity) also require amount.

Body parameters

ParameterType Description
variation_code string required The variation to buy, from /services.
recipient string required Phone, smartcard, meter or customer ID for the service.
amount number optional Required for manual-amount services (airtime, electricity).
meter_type string optional Electricity only. Use prepaid or postpaid.
subscription_type string optional Cable only. Use change or renew.
request
curl -X POST https://globconnects.net/api/v1/purchase \
  -H "Authorization: Bearer PUBLIC_KEY:SECRET_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "variation_code": "mtn-1gb-30", "recipient": "08012345678" }'
response
{ "status": true, "message": "Transaction successful.", "data": {
  "reference": "GC240717ABC123", "status": "successful",
  "amount": 600, "wallet_balance": 14400.00 } }
GET/transactions/{reference}

Retrieves a transaction by reference. Use it to resolve orders that returned pending.

Path parameters

ParameterType Description
reference string required The reference returned when the transaction was created.
request
curl https://globconnects.net/api/v1/transactions/GC240717ABC123 \
  -H "Authorization: Bearer PUBLIC_KEY:SECRET_KEY"

International airtime

Top up mobile numbers in 150+ countries. Airtime is priced live per country, so list operators first, then buy. Each operator is either FIXED (choose an options[] entry and send its sender value) or RANGE (send any amount between range.min and range.max). The ngn field is what your wallet is charged.

Opaque tokens: the id and code values here (and for data, gift cards and eSIMs) are opaque. Send them back exactly as received; do not parse or construct them.

GET/airtime/operators

Query parameters

ParameterType Description
country string required ISO-3166 alpha-2 country code, e.g. US, GB, GH.
request
curl "https://globconnects.net/api/v1/airtime/operators?country=US" \
  -H "Authorization: Bearer PUBLIC_KEY:SECRET_KEY"
response
{ "status": true, "data": {
  "country": "US", "operators": [
    { "id": "~mJ8x2", "name": "AT&T USA", "currency": "USD",
      "denominationType": "RANGE",
      "range": { "min": 5, "max": 100, "ngnMin": 8400, "ngnMax": 168000 },
      "options": [] } ] } }
POST/airtime/purchase

Body parameters

ParameterType Description
operator_id string required Operator id from /airtime/operators.
amount number required A FIXED option's sender value, or any value within the RANGE.
phone string required Recipient number in international format, e.g. +14155550123.
country string required ISO alpha-2 country code.
request
curl -X POST https://globconnects.net/api/v1/airtime/purchase \
  -H "Authorization: Bearer PUBLIC_KEY:SECRET_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "operator_id": "~mJ8x2", "amount": 10,
        "phone": "+14155550123", "country": "US" }'

A pending status settles shortly. Poll /transactions/{reference} to confirm.

International data

Data bundles for numbers abroad, using the same live list-then-buy model as airtime. Operators return real bundle sizes in their options[] labels.

GET/data/operators

Query parameters

ParameterType Description
country string required ISO alpha-2 country code.
response
{ "status": true, "data": {
  "country": "US", "operators": [
    { "id": "~kQ2pR", "name": "T-Mobile Data USA", "currency": "USD",
      "denominationType": "FIXED",
      "options": [ { "sender": 10, "ngn": 16800, "label": "3GB - 30 days" } ] } ] } }
POST/data/purchase

Body parameters

ParameterType Description
operator_id string required Operator id from /data/operators.
amount number required The chosen option's sender value.
phone string required Recipient number in international format.
country string required ISO alpha-2 country code.
request
curl -X POST https://globconnects.net/api/v1/data/purchase \
  -H "Authorization: Bearer PUBLIC_KEY:SECRET_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "operator_id": "~kQ2pR", "amount": 10,
        "phone": "+14155550123", "country": "US" }'

Gift cards

Sell branded gift cards priced live per country. The card is delivered by email to the recipient.

GET/giftcards

Query parameters

ParameterType Description
country string required ISO alpha-2 country code.
response
{ "status": true, "data": {
  "country": "US", "products": [
    { "id": "~7ZgT", "name": "Amazon US", "brand": "Amazon",
      "denominationType": "FIXED",
      "options": [ { "sender": 25, "ngn": 42000 } ] } ] } }
POST/giftcards/purchase

Body parameters

ParameterType Description
product_id string required Product id from /giftcards.
amount number required A FIXED sender value, or a value within the RANGE.
recipient_email string required Where the gift-card code is sent.
request
curl -X POST https://globconnects.net/api/v1/giftcards/purchase \
  -H "Authorization: Bearer PUBLIC_KEY:SECRET_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "product_id": "~7ZgT", "amount": 25,
        "recipient_email": "friend@example.com" }'

Travel eSIMs

Global travel-data eSIMs, priced live in NGN. The flow is three steps: list plans for a destination, buy one, then fetch its QR / activation profile. Ordering is asynchronous. If the profile isn't ready, the eSIM returns PROCESSING; poll the status endpoint for it.

GET/esim/packages

Query parameters

ParameterType Description
region string required ISO alpha-2 country code (US, GB), !GL for a global plan, or a region code.
response
{ "status": true, "data": {
  "region": "US", "packages": [
    { "code": "~ifv-1qVTi6TN", "name": "USA 1GB 7 Days",
      "data": "1GB", "days": 7, "ngn": 2500.00 } ] } }
POST/esim/purchase

Body parameters

ParameterType Description
package_code string required The code from /esim/packages.
email string required Customer email. The QR and receipt are sent here.
response
{ "status": true, "message": "eSIM ordered. View the QR to install.", "data": {
  "reference": "GC240717ESIM01", "status": "successful", "amount": 2500,
  "esim": {
    "plan": "USA 1GB 7 Days", "iccid": "8910300000...",
    "activation_code": "LPA:1$smdp.io$MATCHING-ID",
    "qr_image": "https://.../qr.png", "smdp": "smdp.io",
    "status": "GOT_RESOURCE" },
  "wallet_balance": 11900.00 } }

To install: scan qr_image, or enter activation_code manually (Settings → Add eSIM).

GET/esim/status/{reference}

Re-fetches the QR / activation profile. Use it when a purchase returned status: PROCESSING.

request
curl https://globconnects.net/api/v1/esim/status/GC240717ESIM01 \
  -H "Authorization: Bearer PUBLIC_KEY:SECRET_KEY"

Virtual USD cards

These endpoints require a verified business account. Unlike the rest of the API, an API key alone is not enough. A card is a spendable instrument on the open card networks, so we verify who is behind the account first. Two things must be true:

  1. Your identity is verified, using your BVN, under Settings → Identity verification. Automatic.
  2. Your business is approved. Upload your CAC certificate, TIN and director's ID under Settings → Business verification. Reviewed by a person, usually within one business day.

Until both pass, every endpoint below answers 403 with a machine-readable reason and the outstanding steps, so you can surface the right message to your own user:

response · 403
{ "status": false,
  "message": "Virtual cards need a verified business account.",
  "data": {
    "reason": "BUSINESS_NOT_SUBMITTED",
    "steps": [ "Submit your CAC certificate, TIN and director's ID …" ] } }

Reason codes

ReasonMeaning
IDENTITY_NOT_VERIFIEDBVN not yet verified on the account.
BUSINESS_NOT_SUBMITTEDNo incorporation documents on file.
BUSINESS_REVIEW_PENDINGDocuments submitted, awaiting review. No action needed.
BUSINESS_REJECTEDDocuments declined. The reason is shown in Settings; correct and resubmit.
IDENTITY_NOT_ON_FILEReturned by /cards/create when the account has no verified BVN retained to issue against.

Cards are issued in USD and funded from your Naira wallet at the rate quoted by GET /cards (ngn_per_unit, which already includes the margin). All amount values are in USD.

GET/cards

Every card on the account, plus the current FX quote and funding limits.

response
{ "status": true, "data": {
  "currency": "USD", "ngn_per_unit": 1680.00,
  "min_fund": 5, "max_fund": 1000,
  "cards": [
    { "reference": "CRD250822AB12CD", "label": "Ads card",
      "brand": "Visa", "currency": "USD", "last4": "4821",
      "expiry_month": "07", "expiry_year": "2029",
      "name_on_card": "ADA OKAFOR", "status": "active",
      "balance": 25.00 } ] } }
POST/cards/create

Issues a card. The cardholder fields are required on the first card and reused for every card after, so later calls only need amount and label.

Body parameters

ParameterType Description
amount number optional Initial load in USD. Omit or send 0 to issue an empty card.
label string optional Your own name for the card, e.g. Ads card.
date_of_birth string required Cardholder date of birth, YYYY-MM-DD.
street string required Cardholder street address.
city string required Cardholder city.
state string required Cardholder state.
country string optional ISO alpha-2. Defaults to NG.
zip_code string required Postal code.

There is no id_type / id_number parameter. The cardholder's identity is taken from the verified BVN already on the account, a number posted on the request is only an assertion, whereas one on file passed a government check against the account holder's own name. If nothing is on file the call returns 422 with reason: "IDENTITY_NOT_ON_FILE".

request
curl -X POST https://globconnects.net/api/v1/cards/create \
  -H "Authorization: Bearer PUBLIC_KEY:SECRET_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "amount": 20, "label": "Ads card",
    "date_of_birth": "1990-04-12",
    "street": "12 Awolowo Road", "city": "Ikoyi",
    "state": "Lagos", "country": "NG", "zip_code": "101233" }'

Returns 201 with the new card and your updated wallet_balance. If issuing fails after the wallet was debited, the debit is reversed automatically.

POST/cards/fund

Body parameters

ParameterType Description
card_reference string required The card’s reference.
amount number required USD to add, between min_fund and max_fund.

A frozen, expired or terminated card cannot be funded. Unfreeze it first. Repeated failures auto-freeze a card to protect the account.

POST/cards/freeze

Body parameters

ParameterType Description
card_reference string required The card’s reference.
frozen boolean optional true to freeze (default), false to unfreeze.
GET/cards/{reference}

One card, in the same shape as the list endpoint. 404 if the reference isn't yours.

POST/cards/{reference}/reveal

Full PAN and CVV, fetched live from the issuer. Never stored by us, and never returned by any other endpoint. Requires dedicated cards:reveal API scope. Response is sent with Cache-Control: no-store, max-age=0, private and Pragma: no-cache.

response
{ "status": true, "data": { "card": {
  "pan": "4821000000004821", "cvv": "123",
  "expiry_month": "07", "expiry_year": "2029",
  "name_on_card": "ADA OKAFOR" } } }

Rate limited to 10 calls per hour per account. A 429 carries retry_after in seconds. Every call is audit-logged. Treat the response as cardholder data: do not log it, do not cache it, and serve it to your user over TLS only.

Webhooks

Orders that come back pending settle later. Rather than polling /transactions/{reference}, set a webhook URL in Settings → Webhook and we will POST the update to you as soon as it happens.

When we call you

EventSent when
transaction.successfulThe order was delivered. Any commission has been credited to your wallet.
transaction.reversedThe order failed or was refunded. The amount is back in your wallet.

Only real state changes are sent. A pending order that is still pending produces nothing, and a repeated refund is not re-sent. You will never receive a pending event, because that status is already in the response to your /purchase call.

Request we send

POST your-endpoint
Content-Type: application/json
Accept: application/json
User-Agent: Globconnects-Webhook/1.1
X-Globconnects-Signature: t=1725700000,v1=9f2c3d4e5f6a...  (HMAC-SHA512 of timestamp.event_id.raw_body)
X-Globconnects-Timestamp: 1725700000
X-Globconnects-Event-Id: evt_9f2c3d4e5f6a7b8c

{
  "id": "evt_9f2c3d4e5f6a7b8c",
  "event": "transaction.successful",
  "timestamp": 1725700000,
  "sent_at": "2026-08-07T09:14:22+00:00",
  "data": {
    "reference": "GC240717ABC123",
    "status": "successful",
    "service": "data-gifting",
    "product": "mtn",
    "plan": "gifting-mtn-1gb",
    "network": "MTN",
    "recipient": "08012345678",
    "amount": 480.00,
    "commission": 12.00,
    "channel": "api",
    "provider_reference": "AS-99182734",
    "message": "Delivered successfully.",
    "created_at": "2026-08-07 09:14:02"
  }
}

Payload fields

ParameterType Description
id string required Unique event ID (e.g. evt_...). Matches the X-Globconnects-Event-Id header.
event string required One of the events above (e.g. transaction.successful).
timestamp integer required Unix epoch timestamp when the webhook was generated.
sent_at string required ISO-8601 UTC timestamp of this delivery attempt.
data.reference string required Your order reference. Match this against your own records.
data.status string required successful or reversed. Always the settled value.
data.amount number required Amount charged, in NGN.
data.commission number required Commission earned. Reversed if the order is later refunded.
data.provider_reference string optional Upstream reference, when the provider supplies one.
data.message string optional Human-readable outcome.

Responding

Reply 2xx to acknowledge. Anything else is recorded as a failed delivery. We wait up to 5 seconds for your response, do not follow redirects, and strictly require secure HTTPS URLs (plain HTTP and private/internal IP ranges are blocked for SSRF protection).

Durable Webhook Delivery & Automatic Retries: If your server is unreachable or returns a non-2xx response, our outbound webhook engine automatically queues and retries with exponential backoff:
  • Attempt 1: immediate
  • Attempt 2: +2 minutes
  • Attempt 3: +5 minutes
  • Attempt 4: +15 minutes
  • Attempt 5: +1 hour
  • Attempt 6: +6 hours (final retry before moving to dead-letter log)

Verifying signatures & replay protection

Every outbound webhook carries security headers to prove authenticity and eliminate replay attacks:

  • X-Globconnects-Signature: Formatted as t=<timestamp>,v1=<signature> where the signature is HMAC-SHA512 of timestamp . "." . event_id . "." . raw_body.
  • X-Globconnects-Timestamp: Unix timestamp (seconds) when the delivery was initiated. Reject any requests older than 5 minutes.
  • X-Globconnects-Event-Id: Unique event ID (e.g. evt_...). Integrators must verify this matches the id field in the JSON payload body.
verify
<?php
$secret = getenv('GLOBCONNECTS_WEBHOOK_SECRET');
$raw = file_get_contents('php://input');
$sigHeader = $_SERVER['HTTP_X_GLOBCONNECTS_SIGNATURE'] ?? '';
$eventIdHeader = $_SERVER['HTTP_X_GLOBCONNECTS_EVENT_ID'] ?? '';

// 1. Parse timestamp and signature
if (!preg_match('/t=(\d+),v1=([a-f0-9]+)/', $sigHeader, $m)) {
    http_response_code(401);
    exit('Invalid signature format');
}
[$full, $timestamp, $sig] = $m;

// 2. Replay attack protection (within 5 minutes)
if (abs(time() - (int) $timestamp) > 300) {
    http_response_code(401);
    exit('Timestamp expired');
}

$event = json_decode($raw, true);

// 3. Verify header Event-ID matches payload body ID
if ($eventIdHeader === '' || ($event['id'] ?? '') !== $eventIdHeader) {
    http_response_code(401);
    exit('Event ID mismatch');
}

// 4. Timing-safe HMAC verification over timestamp.event_id.raw_body
$expected = hash_hmac('sha512', $timestamp . '.' . $eventIdHeader . '.' . $raw, $secret);
if (!hash_equals($expected, $sig)) {
    http_response_code(401);
    exit('Signature mismatch');
}

// Look the order up by reference and update your own records.
fulfil($event['data']['reference'], $event['data']['status']);

http_response_code(200);

Deliveries can arrive more than once if an order is settled and later refunded, so key your handling on data.reference or X-Globconnects-Event-Id and make it idempotent. Rotate the secret by clearing and re-saving your webhook URL.

Statuses & errors

The status field on a transaction is one of:

StatusMeaning
successfulDelivered to the recipient.
pendingAccepted and settling. Re-check with /transactions/{reference}, or let a webhook tell you.
reversedFailed and automatically refunded to your wallet.

HTTP status codes

CodeMeaning
200 / 201Success.
401Missing or invalid API keys.
402Insufficient wallet balance.
404Resource not found.
409Conflict / In-flight operation. A request with the same Idempotency-Key is currently processing.
422Validation error. See message.
429Too many requests. Retry after a short delay.
502Upstream provider timeout or error. Retry only using your original Idempotency-Key to guarantee against double processing.