Partner API documentation

G-Starlink Hub Partner API -- Quick Start Guide

Base URL: https://api.gstarlink.com API Version: v1 Authentication: OAuth 2.0 Client Credentials

This guide is for agents and resellers integrating with the G-Starlink Hub Partner API to browse products, place orders, retrieve eSIM delivery details, and receive event notifications via webhooks.


Official SDKs

The examples in this guide use raw curl so you can see the underlying HTTP calls, but zero-dependency, single-file SDKs are available for four languages. They handle OAuth token management and webhook signature verification for you:

LanguageFileRequirements
TypeScript / JavaScriptsdk/esim-atlas-sdk.tsNode.js 18+ or modern browsers
Pythonsdk/esim_atlas_sdk.pyPython 3.8+ (stdlib only)
PHPsdk/esim-atlas-sdk.phpPHP 8.0+ (cURL + JSON extensions)
Rubysdk/esim_atlas_sdk.rbRuby 3.0+ (stdlib only)

See the SDK README for installation, quick starts, and the full method reference.


Service Scope Disclaimer

Our guarantee covers the recharge face value only. For prepaid SIM products and recharges, we guarantee that the purchased face value is credited to the SIM (e.g. a $39 recharge credits $39). The plan inclusions attached to that face value — data allowance, bonus data, calls, validity — are set solely by the carrier and may change at any time without notice.

Example: a carrier may advertise a $39 prepaid plan with 65GB, composed of 25GB standard data plus 40GB promotional bonus data. The bonus portion is a carrier promotion that can shrink, grow, or disappear from one month to the next. We accept no responsibility for differences between advertised and delivered plan inclusions. Always verify current inclusions on the carrier's official website before resale to your customers.


Table of Contents

  1. Getting API Credentials
  2. Sandbox Environment
  3. Authentication
  4. Browse Products
  5. Create an Order
  6. Get eSIM Delivery Details
  7. Check eSIM Status
  8. Webhooks
  9. End-to-End Example
  10. Status Lifecycle
  11. Error Handling
  12. Rate Limits
  13. Full API Reference

0. Getting API Credentials

You issue your own keys from the Partner Portal. We set up your account and invite you; everything after that is self-service.

  1. We create your partner account and email you an invitation. Set a password and sign in at https://hub.gstarlink.com/dashboard/partner
  2. Under API Keys, generate a key. You get a client_id (starting with sk_test_) and a client_secret. Copy the secret immediately — it is shown once and cannot be retrieved, only replaced.
  3. Use these credentials to obtain an access token (see Authentication below)
  4. Build and verify your integration against the sandbox
  5. When you are ready to go live, ask your account manager to enable production. Once they do, you generate your own sk_live_* key from the same screen.

The portal is also where you register webhook endpoints, watch your API usage, and top up the wallet your orders are charged to.

Keys can be revoked and regenerated from the portal at any time.


1. Sandbox Environment

G-Starlink Hub provides a sandbox environment for testing your integration without affecting production data or incurring charges.

How Sandbox Works

  • Sandbox uses the same base URL: https://api.gstarlink.com
  • Sandbox API keys use the sk_test_* prefix; production keys use sk_live_*
  • Generate sandbox keys yourself in the Partner Portal, under "API Keys"
  • Orders created with sandbox credentials are marked as test orders ("test": true in metadata) and are auto-fulfilled within seconds -- no payment step, no real inventory
  • GET /orders/{id}/esims returns synthetic test eSIMs for sandbox orders: deterministic ICCIDs (prefix 8999), a sandbox SM-DP+ address, and is_test: true on every record. These can never activate on a device
  • order.created and order.fulfilled webhooks fire for sandbox orders, so the full order -> webhook -> eSIM retrieval loop is testable end to end

Testing Checklist

  1. Generate a sandbox API key (sk_test_*)
  2. Obtain an access token using sandbox credentials
  3. Browse products (same catalog as production)
  4. Register a webhook subscribed to order.created and order.fulfilled
  5. Create a test order -- it returns pending_payment, then auto-fulfils; the order.fulfilled webhook arrives within seconds
  6. Fetch GET /orders/{id}/esims and process the synthetic test eSIMs
  7. Once integration is verified, switch to production credentials (sk_live_*)

Sandbox Limitations

  • eSIMs are synthetic (is_test: true) and cannot be installed on devices
  • Physical SIM activations (POST /activations) are NOT simulated -- they enter the real activation queue, so only submit real SIMs you own
  • Payment/wallet deductions are simulated (sandbox orders skip payment)

2. Authentication

The API uses OAuth 2.0 Client Credentials flow. You need a client_id and client_secret issued by your G-Starlink Hub account manager.

Step 1: Obtain an Access Token

curl -X POST https://api.gstarlink.com/api/v1/oauth/token \
  -H "Content-Type: application/json" \
  -d '{
    "grant_type": "client_credentials",
    "client_id": "YOUR_CLIENT_ID",
    "client_secret": "YOUR_CLIENT_SECRET",
    "scope": "read:products write:orders read:orders read:esims write:webhooks read:webhooks"
  }'

Response:

{
  "access_token": "eyJhbGciOiJIUzI1NiIs...",
  "token_type": "Bearer",
  "expires_in": 3600,
  "scope": "read:products write:orders read:orders read:esims write:webhooks read:webhooks"
}

Step 2: Use the Token

Include the token in the Authorization header for all subsequent requests:

Authorization: Bearer eyJhbGciOiJIUzI1NiIs...

Tokens expire after 1 hour (3600 seconds). Request a new token when the current one expires.

Available Scopes

ScopeDescription
read:productsBrowse product catalog
read:ordersView orders
write:ordersCreate orders
read:esimsAccess eSIM delivery details
read:webhooksList webhooks
write:webhooksCreate/manage webhooks

3. Browse Products

List All Products

curl -X GET "https://api.gstarlink.com/api/v1/products?limit=10&region=Asia" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN"

Response:

{
  "data": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440001",
      "sku": "ASIA-5GB-30D",
      "name": "Asia 5GB - 30 Days",
      "description": "Data-only eSIM for 12 Asian countries",
      "region": "Asia",
      "coverage": ["JP", "KR", "TH", "SG"],
      "data_amount_mb": 5120,
      "validity_days": 30,
      "price_cents": 1999,
      "currency": "USD",
      "type": "data_only",
      "status": "active"
    }
  ],
  "pagination": {
    "has_more": true,
    "next_cursor": "eyJjcmVhdGVkX2F0Ijo..."
  }
}

Query Parameters

ParameterTypeDefaultDescription
limitnumber20Results per page (max 100)
cursorstring--Pagination cursor from previous response
regionstring--Filter by region (e.g., Asia, Europe)
typestring--data_only or voice_data
statusstringactiveactive, inactive, or out_of_stock
sort_bystringcreated_atcreated_at, price_cents, or name
sort_orderstringdescasc or desc

Get a Single Product

curl -X GET "https://api.gstarlink.com/api/v1/products/550e8400-e29b-41d4-a716-446655440001" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN"

4. Create an Order

Single Order

curl -X POST https://api.gstarlink.com/api/v1/orders \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: YOUR_UNIQUE_IDEMPOTENCY_KEY" \
  -d '{
    "line_items": [
      {
        "product_id": "550e8400-e29b-41d4-a716-446655440001",
        "quantity": 5
      }
    ]
  }'

Response:

{
  "id": "order-uuid-here",
  "order_number": "ORD-20260413-ABC12",
  "status": "pending_payment",
  "total_amount_cents": 9995,
  "currency": "USD",
  "created_at": "2026-04-13T10:00:00Z"
}

Important: Always include an Idempotency-Key header (UUID recommended). Sending the same key twice returns the original order instead of creating a duplicate.

Note: Some products require activation scheduling (e.g., eSIM with EID push delivery). For these products, include activation_requests in your order. Check the product's delivery_method field -- if it's supplier_email or eid_push, activation info is required.

Bulk Order (E-commerce Integration)

Create up to 100 orders in a single request:

curl -X POST https://api.gstarlink.com/api/v1/orders/bulk \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "orders": [
      {
        "external_order_id": "SHOP-10001",
        "line_items": [
          { "product_id": "PRODUCT_ID_1", "quantity": 1 }
        ],
        "customer": {
          "email": "customer1@example.com",
          "name": "Alice"
        }
      },
      {
        "external_order_id": "SHOP-10002",
        "line_items": [
          { "product_id": "PRODUCT_ID_2", "quantity": 2 }
        ],
        "customer": {
          "email": "customer2@example.com",
          "name": "Bob"
        }
      }
    ]
  }'

Each order in the batch is processed independently. The response includes per-order success/failure status.

List Orders

curl -X GET "https://api.gstarlink.com/api/v1/orders?limit=20" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN"

Get Order Details

curl -X GET "https://api.gstarlink.com/api/v1/orders/ORDER_ID" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN"

5. Get eSIM Delivery Details

After an order is fulfilled, retrieve the eSIM activation codes and QR codes:

curl -X GET "https://api.gstarlink.com/api/v1/orders/ORDER_ID/esims" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN"

Response:

{
  "order_id": "order-uuid-here",
  "esims": [
    {
      "id": "esim-uuid",
      "iccid": "8944501234567890123",
      "activation_code": "LPA:1$smdp.example.com$ACTIVATION_CODE",
      "qr_code_url": "https://api.gstarlink.com/qr/esim-uuid.png",
      "status": "inactive"
    }
  ],
  "count": 5
}

Key fields for end-user delivery:

  • activation_code -- The LPA string the user enters manually on their device
  • qr_code_url -- A scannable QR code image URL for direct installation

6. Check eSIM Status

Query the real-time status of a specific eSIM by its ICCID:

curl -X GET "https://api.gstarlink.com/api/v1/esims/8944501234567890123/status" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN"

Check Order Activation Status

For orders with supplier-delivered products, check the activation progress:

curl -X GET "https://api.gstarlink.com/api/v1/orders/ORDER_ID/activation-status" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN"

Response:

{
  "order_id": "order-uuid",
  "order_number": "ORD-20260413-ABC12",
  "has_scheduled_activations": true,
  "total_activations": 1,
  "pending_count": 0,
  "submitted_count": 1,
  "activated_count": 0,
  "failed_count": 0,
  "activations": [
    {
      "item_id": "order-item-uuid",
      "product_name": "Europe 10GB - Orange",
      "status": "submitted",
      "customer_email": "customer@example.com",
      "activation_date": "2026-05-01",
      "submitted_at": "2026-04-13T10:30:00Z"
    }
  ]
}

Activation statuses: pending | submitted | confirmed | activated | failed | cancelled


6b. Check Wallet Balance

curl -X GET "https://api.gstarlink.com/api/v1/balance" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN"

Response:

{
  "balances": [
    {
      "currency": "USD",
      "balance_cents": 250000,
      "last_transaction_at": "2026-07-13T08:15:00Z"
    }
  ],
  "count": 1
}

Balances are held per currency. Orders are charged in the product's currency, so make sure the matching wallet is funded.


7. Webhooks

Register a Webhook

curl -X POST https://api.gstarlink.com/api/v1/webhooks \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://your-app.com/webhooks/esim-atlas",
    "events": [
      "order.created",
      "order.fulfilled",
      "esim.activated",
      "activation.activated",
      "activation.failed"
    ]
  }'

Response:

{
  "webhook": {
    "id": "wh_uuid",
    "url": "https://your-app.com/webhooks/esim-atlas",
    "events": ["order.created", "order.fulfilled", "esim.activated", "activation.activated", "activation.failed"],
    "status": "active"
  },
  "secret": "whsec_..."
}

Save the secret immediately. It is only shown once and is required for signature verification.

Available Events

EventDescription
order.createdOrder has been created
order.fulfilledeSIMs assigned to order
order.cancelledOrder was cancelled
esim.activatedeSIM activated on device
esim.expiredeSIM has expired
payment.completedPayment succeeded
payment.failedPayment failed
activation.submittedActivation request sent to supplier
activation.confirmedSupplier confirmed receipt
activation.activatedActivation completed
activation.failedActivation failed

Webhook Payload Examples

Each webhook delivery sends a JSON payload with the event type, a unique event ID, a timestamp, and the event data.

order.created

{
  "event": "order.created",
  "event_id": "evt_abc123",
  "timestamp": "2026-04-13T10:00:00Z",
  "data": {
    "id": "550e8400-e29b-41d4-a716-446655440001",
    "order_number": "ORD-20260413-A1B2C",
    "status": "pending_payment",
    "total_price_cents": 1500,
    "currency": "USD",
    "line_items": [{ "product_id": "...", "quantity": 1 }]
  }
}

order.fulfilled

{
  "event": "order.fulfilled",
  "event_id": "evt_def456",
  "timestamp": "2026-04-13T10:05:00Z",
  "data": {
    "id": "550e8400-e29b-41d4-a716-446655440001",
    "order_number": "ORD-20260413-A1B2C",
    "status": "fulfilled",
    "esims": [
      {
        "iccid": "8901234567890123456",
        "activation_code": "LPA:1$smdp.example.com$MATCHING_ID",
        "qr_code_url": "https://api.gstarlink.com/qr/esim-uuid.png"
      }
    ]
  }
}

activation.activated

{
  "event": "activation.activated",
  "event_id": "evt_ghi789",
  "timestamp": "2026-04-13T10:10:00Z",
  "data": {
    "id": "75f7b6f8-1234-5678-9abc-def012345678",
    "iccid": "8961030000012345678",
    "carrier": "Lycamobile",
    "mobile_number": "0402033268",
    "status": "completed"
  }
}

activation.failed

{
  "event": "activation.failed",
  "event_id": "evt_jkl012",
  "timestamp": "2026-04-13T10:10:00Z",
  "data": {
    "id": "75f7b6f8-1234-5678-9abc-def012345678",
    "iccid": "8961030000012345678",
    "carrier": "Lycamobile",
    "error_type": "carrier_error",
    "error_message": "ID verification failed",
    "status": "failed"
  }
}

Webhook Delivery

  • Retries: Up to 5 attempts with exponential backoff (1s, 2s, 4s, 8s, 16s)
  • Timeout: Your endpoint must respond within 10 seconds
  • Expected response: HTTP 2xx status code

Signature Verification (Node.js)

Every webhook request includes an X-Webhook-Signature header with the format t=TIMESTAMP,v1=SIGNATURE.

const crypto = require('crypto');

function verifyWebhookSignature(payload, signatureHeader, secret) {
  // Parse the signature header
  const parts = signatureHeader.split(',');
  const timestampPart = parts.find(p => p.startsWith('t='));
  const signaturePart = parts.find(p => p.startsWith('v1='));

  if (!timestampPart || !signaturePart) {
    return false;
  }

  const timestamp = timestampPart.split('=')[1];
  const receivedSignature = signaturePart.split('=')[1];

  // Reject requests older than 5 minutes (replay attack prevention)
  const age = Math.abs(Date.now() / 1000 - parseInt(timestamp, 10));
  if (age > 300) {
    return false;
  }

  // Compute expected signature: HMAC-SHA256(timestamp + "." + payload, secret)
  const signedPayload = `${timestamp}.${payload}`;
  const expectedSignature = crypto
    .createHmac('sha256', secret)
    .update(signedPayload)
    .digest('hex');

  // Timing-safe comparison
  return crypto.timingSafeEqual(
    Buffer.from(receivedSignature),
    Buffer.from(expectedSignature)
  );
}

// Usage in an Express handler
app.post('/webhooks/esim-atlas', (req, res) => {
  const payload = JSON.stringify(req.body);
  const signature = req.headers['x-webhook-signature'];

  if (!verifyWebhookSignature(payload, signature, process.env.WEBHOOK_SECRET)) {
    return res.status(401).send('Invalid signature');
  }

  const event = req.body;
  console.log('Received event:', event.type);

  // Process the event...

  res.status(200).send('OK');
});

List Webhooks

curl -X GET "https://api.gstarlink.com/api/v1/webhooks" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN"

8. End-to-End Example

Complete workflow: authenticate, find a product, place an order, and retrieve the eSIM.

#!/bin/bash
# G-Starlink Hub Partner API — Complete Integration Example
# Replace YOUR_CLIENT_ID and YOUR_CLIENT_SECRET with real credentials

BASE_URL="https://api.gstarlink.com"

# Step 1: Get access token
echo "=== Step 1: Authenticate ==="
TOKEN=$(curl -s -X POST "$BASE_URL/api/v1/oauth/token" \
  -H "Content-Type: application/json" \
  -d '{
    "grant_type": "client_credentials",
    "client_id": "YOUR_CLIENT_ID",
    "client_secret": "YOUR_CLIENT_SECRET"
  }' | python3 -c "import sys,json; print(json.load(sys.stdin)['access_token'])")
echo "Token: ${TOKEN:0:20}..."

# Step 2: Browse products (find Japan eSIM)
echo "=== Step 2: Find Products ==="
curl -s "$BASE_URL/api/v1/products?region=Asia&limit=3" \
  -H "Authorization: Bearer $TOKEN" | python3 -m json.tool | head -30

# Step 3: Get a specific product ID (use the first result)
PRODUCT_ID=$(curl -s "$BASE_URL/api/v1/products?region=Asia&limit=1" \
  -H "Authorization: Bearer $TOKEN" | python3 -c "import sys,json; print(json.load(sys.stdin)['data'][0]['id'])")
echo "Product ID: $PRODUCT_ID"

# Step 4: Check inventory
echo "=== Step 4: Check Availability ==="
curl -s "$BASE_URL/api/v1/inventory/status?product_ids=$PRODUCT_ID" \
  -H "Authorization: Bearer $TOKEN" | python3 -m json.tool

# Step 5: Create order
echo "=== Step 5: Create Order ==="
ORDER=$(curl -s -X POST "$BASE_URL/api/v1/orders" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: demo-$(date +%s)" \
  -d "{
    \"line_items\": [{
      \"product_id\": \"$PRODUCT_ID\",
      \"quantity\": 1
    }]
  }")
echo "$ORDER" | python3 -m json.tool
ORDER_ID=$(echo "$ORDER" | python3 -c "import sys,json; print(json.load(sys.stdin)['id'])")
echo "Order ID: $ORDER_ID"

# Step 6: Check order status (poll until fulfilled)
echo "=== Step 6: Check Order Status ==="
curl -s "$BASE_URL/api/v1/orders/$ORDER_ID" \
  -H "Authorization: Bearer $TOKEN" | python3 -m json.tool

# Step 7: Get eSIM delivery (only works when order is fulfilled)
echo "=== Step 7: Get eSIM Delivery ==="
curl -s "$BASE_URL/api/v1/orders/$ORDER_ID/esims" \
  -H "Authorization: Bearer $TOKEN" | python3 -m json.tool

echo "=== Done ==="

9. Status Lifecycle

Order Status

pending_payment ──> paid ──> fulfilled
   │                 │
   │                 ├──> waiting_review ──> fulfilled
   │                 └──> failed
   │
   └──> cancelled
StatusDescription
pending_paymentOrder created, awaiting payment
paidPayment received, fulfillment starting
waiting_reviewHeld for manual review before fulfillment
fulfilledOrder complete, eSIM delivered
failedFulfillment failed (check error details)
cancelledOrder cancelled by partner or admin

How payment and fulfillment work

Production orders are paid from your pre-funded wallet. There is no card or checkout step in the API:

  1. You top up your wallet by bank transfer (details from your account manager). Once we confirm the funds, your balance is credited.
  2. POST /orders charges the order total to your wallet atomically and the order is created as paid. If the balance is too low, the order is not created at all and you get a 402 Insufficient Balance (see below).
  3. Fulfillment allocates your eSIMs and the order becomes fulfilled — the order.fulfilled webhook fires and GET /orders/{id}/esims returns the delivery details.

Check your balance any time with GET /balance. Every successful order also returns wallet_balance_after_cents, so you can track headroom without an extra call.

In sandbox, payment is simulated: orders are never charged to the wallet (no funding required) and auto-fulfil within seconds.

Insufficient balance (402)

{
  "type": "https://docs.gstarlink.com/errors/insufficient_balance",
  "title": "Insufficient Balance",
  "status": 402,
  "detail": "Wallet balance is too low for this order. Top up your account and retry.",
  "currency": "USD",
  "balance_cents": 905,
  "required_cents": 8190,
  "shortfall_cents": 7285
}

No order is created and no funds are taken — top up and retry the same request.

Activation Status (Physical SIM)

pending ──> processing ──> completed
                │
                └──> failed ──> manual_required
StatusDescription
pendingActivation request submitted, queued for processing
processingActivation is being processed with the carrier
completedSIM activated successfully, mobile number assigned
failedActivation failed (e.g. identity verification issue)
manual_requiredCould not be completed automatically; our operations team is handling it

10. Error Handling

All error responses follow the RFC 7807 Problem Details format:

{
  "type": "https://docs.gstarlink.com/errors/validation-error",
  "title": "Validation Error",
  "status": 400,
  "detail": "Invalid product_id in line_items[0]",
  "requestId": "req_abc123",
  "timestamp": "2026-04-13T10:00:00Z"
}

HTTP Status Codes

CodeMeaning
200Success
201Created
400Bad request -- check the detail and type fields
401Unauthorized -- invalid or expired token
403Forbidden -- insufficient scopes
404Not found
409Conflict -- duplicate idempotency key with different payload
429Rate limit exceeded
500Server error

Token Expiry

When your token expires, the API returns 401. Request a new token and retry:

# 1. Get a new token
curl -X POST https://api.gstarlink.com/api/v1/oauth/token \
  -H "Content-Type: application/json" \
  -d '{
    "grant_type": "client_credentials",
    "client_id": "YOUR_CLIENT_ID",
    "client_secret": "YOUR_CLIENT_SECRET"
  }'

# 2. Retry the failed request with the new token

11. Rate Limits

API requests are rate-limited per API key. Current limits:

TierRequests per minute
Standard60
Premium300

When rate-limited, the API returns HTTP 429 with a Retry-After header indicating how many seconds to wait.


12. Full API Reference

The complete OpenAPI specification is available at:


Integration Checklist

  1. Obtain your client_id and client_secret from your account manager
  2. Request an access token via the OAuth endpoint
  3. Browse the product catalog and select products to sell
  4. Create a test order with an idempotency key
  5. Retrieve eSIM delivery details (QR code + activation code)
  6. Register a webhook endpoint to receive order and activation events
  7. Implement webhook signature verification
  8. Test the full flow end-to-end in sandbox (sk_test_* keys)
  9. Switch to production keys (sk_live_*) when ready to go live