Partner API documentation

eSIM Atlas Partner API SDK

Lightweight, zero-dependency, single-file SDKs for the eSIM Atlas Partner API, available in four languages:

LanguageFileRequirements
TypeScript / JavaScriptesim-atlas-sdk.tsNode.js 18+ or modern browsers (native fetch)
Pythonesim_atlas_sdk.pyPython 3.8+ (standard library only)
PHPesim-atlas-sdk.phpPHP 8.0+ with cURL and JSON extensions
Rubyesim_atlas_sdk.rbRuby 3.0+ (standard library only)

All four SDKs expose the same set of API methods, handle OAuth 2.0 token management automatically, and include a webhook signature verification helper. The detailed documentation below uses the TypeScript SDK; see Other Languages for Python, PHP, and Ruby quick starts.

Installation

Copy the single file into your project:

cp esim-atlas-sdk.ts src/lib/esim-atlas-sdk.ts

Or, when published as an npm package:

npm install @esim-atlas/sdk

Quick Start

import { EsimAtlasClient } from './esim-atlas-sdk';

const client = new EsimAtlasClient({
  clientId: 'sk_live_abc123def456',
  clientSecret: 'your_secret_here',
  // baseUrl: 'https://api.gstarlink.com',  // optional, this is the default
});

// List products
const products = await client.listProducts({ region: 'Asia', limit: 10 });
console.log(products.data);

// Create an order (idempotency key prevents duplicates)
const order = await client.createOrder(
  {
    line_items: [{ product_id: 'uuid-here', quantity: 5 }],
  },
  'unique-idempotency-key-123'
);
console.log(order.order_number);

Authentication

The SDK handles OAuth 2.0 token management automatically:

  1. On the first API call, the client exchanges your credentials for an access token.
  2. The token is cached in memory and reused for subsequent calls.
  3. When the token is about to expire (within 60 seconds), a new one is fetched transparently.

You never need to call the token endpoint manually.

Error Handling

All API errors throw typed EsimAtlasError (RFC 7807 Problem Detail) or EsimAtlasOAuthError:

import { EsimAtlasError, EsimAtlasOAuthError } from './esim-atlas-sdk';

try {
  const order = await client.getOrder('invalid-id');
} catch (err) {
  if (err instanceof EsimAtlasError) {
    console.error(err.status);   // 404
    console.error(err.title);    // "Not Found"
    console.error(err.detail);   // "Order not found"
    console.error(err.type);     // "https://docs.gstarlink.com/errors/not_found"
    console.error(err.errors);   // field-level validation errors (if any)
  }
  if (err instanceof EsimAtlasOAuthError) {
    console.error(err.code);     // "invalid_client"
    console.error(err.status);   // 401
  }
}

Available Methods

Products

MethodDescription
listProducts(params?)List products with filtering (region, type, status) and cursor pagination
getProduct(id)Get product details including features and compatible devices

Orders

MethodDescription
createOrder(data, idempotencyKey)Create an order with idempotency protection
listOrders(params?)List orders with optional status filter
getOrder(id)Get order with full fulfillment details
getOrderEsims(orderId)Get eSIM delivery details (ICCID, LPA code, QR URL)
getActivationStatus(orderId)Get status of all scheduled activations for an order
createBulkOrders(data)Create up to 100 orders in a single batch

Activations (Physical SIM)

MethodDescription
submitActivation(data)Submit a physical SIM activation with KYC data
listActivations(params?)List activation requests with optional filters
getActivation(id)Get activation details including assigned mobile number

eSIMs

MethodDescription
getEsimStatus(iccid)Check eSIM status, data usage, and location

Inventory

MethodDescription
getInventoryStatus(params?)Check product stock and availability

Webhooks

MethodDescription
createWebhook(data)Register a webhook endpoint (returns signing secret once)
listWebhooks(params?)List registered webhooks
getWebhook(id)Get webhook details
deleteWebhook(id)Delete a webhook (irreversible)

Catalog

MethodDescription
syncCatalog(params?)Export catalog for Shopify, WooCommerce, or generic use

Webhook Signature Verification

When receiving webhook events, verify the X-Webhook-Signature header to ensure authenticity:

import { verifyWebhookSignature } from './esim-atlas-sdk';

// Express / Node.js example
app.post('/webhooks/esim-atlas', async (req, res) => {
  const signature = req.headers['x-webhook-signature'] as string;
  const rawBody = req.body; // must be the raw string, not parsed JSON

  const isValid = await verifyWebhookSignature(rawBody, signature, WEBHOOK_SECRET);
  if (!isValid) {
    return res.status(401).send('Invalid signature');
  }

  const event = JSON.parse(rawBody);
  switch (event.event) {
    case 'order.fulfilled':
      // Handle fulfilled order
      break;
    case 'esim.activated':
      // Handle eSIM activation
      break;
    case 'activation.completed':
      // Handle physical SIM activation completed
      break;
  }

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

// Next.js App Router example
export async function POST(request: Request) {
  const rawBody = await request.text();
  const signature = request.headers.get('X-Webhook-Signature')!;

  const isValid = await verifyWebhookSignature(rawBody, signature, process.env.WEBHOOK_SECRET!);
  if (!isValid) {
    return new Response('Invalid signature', { status: 401 });
  }

  const event = JSON.parse(rawBody);
  // Process event...
  return new Response('OK', { status: 200 });
}

The signature format is t=<timestamp>,v1=<hmac_sha256_hex>. The signed payload is <timestamp>.<raw_body>.

Supported Webhook Events

EventDescription
order.createdNew order placed
order.fulfilledOrder fully fulfilled
order.cancelledOrder cancelled
esim.activatedeSIM activated on network
esim.expiredeSIM validity expired
payment.completedPayment successfully processed
payment.failedPayment failed
activation.submittedPhysical SIM activation submitted
activation.confirmedPhysical SIM activation confirmed by carrier
activation.activatedPhysical SIM activated on network
activation.failedPhysical SIM activation failed

Physical SIM Activation Example

The carrier is auto-detected from the ICCID prefix. Currently supported carriers: Lycamobile and Optus — other carriers return 422 Carrier Not Supported. KYC fields differ per carrier (Lycamobile needs sim_last4 + puk; Optus needs title).

Service scope: our guarantee covers the recharge face value only. Plan inclusions (data allowance, bonus data, validity) are set by the carrier and may change at any time — see the Quick Start's Service Scope Disclaimer.

const result = await client.submitActivation({
  iccid: '8961030000012345678',
  activation_date: '2026-04-14',
  first_name: 'Wei',
  last_name: 'Zhang',
  passport_no: 'E12345678',
  passport_country: 'CN',
  passport_expiry: '2030-06-20',
  date_of_birth: '1990-03-15',
  address: '45 George St',
  suburb: 'Sydney',
  state: 'NSW',
  postcode: '2000',
  sim_last4: '5678',         // Last 4 digits printed on the SIM
  puk: '12345678',           // PUK code from the SIM pack
});

console.log(result.id);              // Activation UUID
console.log(result.carrier);         // "Lycamobile" (auto-detected)
console.log(result.processing_mode); // "scheduled" or "immediate"

// Check activation status later
const detail = await client.getActivation(result.id);
console.log(detail.status);          // "completed"
console.log(detail.mobile_number);   // "0402033268"

Other Languages

The Python, PHP, and Ruby SDKs mirror the TypeScript API surface. Method names follow each language's convention (listProductslist_products in Python and Ruby), responses are returned as plain dicts/arrays/hashes, and errors are raised as typed exceptions (ApiError for RFC 7807 Problem Details, OAuthError for token-endpoint failures).

Python

from esim_atlas_sdk import EsimAtlasClient, ApiError, verify_webhook_signature

client = EsimAtlasClient(
    client_id="sk_live_abc123def456",
    client_secret="your_secret_here",
)

# List products
products = client.list_products(region="Asia", limit=10)
for product in products["data"]:
    print(product["name"])

# Create an order (idempotency key prevents duplicates)
try:
    order = client.create_order(
        {"line_items": [{"product_id": "uuid-here", "quantity": 5}]},
        idempotency_key="unique-idempotency-key-123",
    )
    print(order["order_number"])
except ApiError as err:
    print(err.status, err.title, err.detail)

# Verify a webhook signature (e.g. in Flask/Django/FastAPI)
is_valid = verify_webhook_signature(raw_body, signature_header, webhook_secret)

PHP

require 'esim-atlas-sdk.php';

use EsimAtlas\Client;
use EsimAtlas\ApiError;

$client = new Client('sk_live_abc123def456', 'your_secret_here');

// List products
$products = $client->listProducts(['region' => 'Asia', 'limit' => 10]);
foreach ($products['data'] as $product) {
    echo $product['name'], PHP_EOL;
}

// Create an order (idempotency key prevents duplicates)
try {
    $order = $client->createOrder(
        ['line_items' => [['product_id' => 'uuid-here', 'quantity' => 5]]],
        'unique-idempotency-key-123'
    );
    echo $order['order_number'], PHP_EOL;
} catch (ApiError $err) {
    echo $err->status, ' ', $err->title, ': ', $err->detail, PHP_EOL;
}

// Verify a webhook signature
$isValid = EsimAtlas\verifyWebhookSignature($rawBody, $signatureHeader, $webhookSecret);

Note: the PHP OAuthError exposes the OAuth error code as $errorCode (not $code, which is the built-in int-typed Exception property).

Ruby

require_relative 'esim_atlas_sdk'

client = EsimAtlas::Client.new(
  client_id: 'sk_live_abc123def456',
  client_secret: 'your_secret_here'
)

# List products
products = client.list_products(region: 'Asia', limit: 10)
products['data'].each { |product| puts product['name'] }

# Create an order (idempotency key prevents duplicates)
begin
  order = client.create_order(
    { line_items: [{ product_id: 'uuid-here', quantity: 5 }] },
    'unique-idempotency-key-123'
  )
  puts order['order_number']
rescue EsimAtlas::ApiError => e
  puts "#{e.status} #{e.title}: #{e.detail}"
end

# Verify a webhook signature (e.g. in Rails/Sinatra)
is_valid = EsimAtlas.verify_webhook_signature(raw_body, signature_header, webhook_secret)

eSIM Atlas Partner API SDK (Chinese / 中文)

轻量级、零依赖、单文件 SDK,用于 eSIM Atlas 合作伙伴 API,提供四种语言版本:

语言文件环境要求
TypeScript / JavaScriptesim-atlas-sdk.tsNode.js 18+ 或现代浏览器(原生 fetch
Pythonesim_atlas_sdk.pyPython 3.8+(仅标准库)
PHPesim-atlas-sdk.phpPHP 8.0+(需 cURL 和 JSON 扩展)
Rubyesim_atlas_sdk.rbRuby 3.0+(仅标准库)

四个 SDK 提供相同的 API 方法集,自动管理 OAuth 2.0 令牌,并内置 Webhook 签名验证工具。以下详细文档以 TypeScript 为例;Python / PHP / Ruby 的快速 开始见其他语言

安装

将单文件复制到项目中:

cp esim-atlas-sdk.ts src/lib/esim-atlas-sdk.ts

或通过 npm 安装(发布后):

npm install @esim-atlas/sdk

快速开始

import { EsimAtlasClient } from './esim-atlas-sdk';

const client = new EsimAtlasClient({
  clientId: 'sk_live_abc123def456',
  clientSecret: 'your_secret_here',
});

// 查询产品列表
const products = await client.listProducts({ region: 'Asia', limit: 10 });
console.log(products.data);

// 创建订单(幂等键防止重复下单)
const order = await client.createOrder(
  {
    line_items: [{ product_id: 'uuid-here', quantity: 5 }],
  },
  'unique-idempotency-key-123'
);
console.log(order.order_number);

认证说明

SDK 自动管理 OAuth 2.0 令牌:

  1. 首次 API 调用时,客户端使用凭据换取访问令牌
  2. 令牌缓存在内存中,后续调用自动复用
  3. 令牌到期前 60 秒自动刷新,无需手动操作

错误处理

所有 API 错误抛出带类型的 EsimAtlasError(RFC 7807 标准)或 EsimAtlasOAuthError

import { EsimAtlasError } from './esim-atlas-sdk';

try {
  await client.getOrder('invalid-id');
} catch (err) {
  if (err instanceof EsimAtlasError) {
    console.error(err.status);   // HTTP 状态码,如 404
    console.error(err.title);    // 错误标题
    console.error(err.detail);   // 详细错误信息
    console.error(err.errors);   // 字段级验证错误(如有)
  }
}

所有方法一览

产品

方法说明
listProducts(params?)按区域、类型筛选产品,支持游标分页
getProduct(id)获取产品详情(功能特性、兼容设备等)

订单

方法说明
createOrder(data, idempotencyKey)创建订单(幂等保护)
listOrders(params?)查询订单列表
getOrder(id)获取订单及履约详情
getOrderEsims(orderId)获取 eSIM 交付信息(ICCID、LPA 码、二维码 URL)
getActivationStatus(orderId)查询订单的所有预约激活状态
createBulkOrders(data)批量创建订单(最多 100 个)

激活(实体 SIM 卡)

方法说明
submitActivation(data)提交实体 SIM 卡激活请求(含 KYC 资料)
listActivations(params?)查询激活请求列表
getActivation(id)获取激活详情(含分配的手机号)

eSIM

方法说明
getEsimStatus(iccid)查询 eSIM 状态、数据用量和位置

库存

方法说明
getInventoryStatus(params?)查询产品库存和可用性

Webhook

方法说明
createWebhook(data)注册 Webhook 端点(签名密钥仅返回一次)
listWebhooks(params?)查询已注册的 Webhook
getWebhook(id)获取 Webhook 详情
deleteWebhook(id)删除 Webhook(不可撤销)

产品目录

方法说明
syncCatalog(params?)导出产品目录(支持 Shopify、WooCommerce 格式)

Webhook 签名验证

接收 Webhook 事件时,验证 X-Webhook-Signature 请求头以确保真实性:

import { verifyWebhookSignature } from './esim-atlas-sdk';

// Next.js App Router 示例
export async function POST(request: Request) {
  const rawBody = await request.text();
  const signature = request.headers.get('X-Webhook-Signature')!;

  const isValid = await verifyWebhookSignature(rawBody, signature, process.env.WEBHOOK_SECRET!);
  if (!isValid) {
    return new Response('签名无效', { status: 401 });
  }

  const event = JSON.parse(rawBody);
  // 处理事件...
  return new Response('OK', { status: 200 });
}

签名格式为 t=<timestamp>,v1=<hmac_sha256_hex>,签名内容为 <timestamp>.<原始请求体>

实体 SIM 卡激活示例

系统根据 ICCID 前缀自动识别运营商。当前支持的运营商:LycamobileOptus——其他运营商的 ICCID 会返回 422 Carrier Not Supported。 各运营商 KYC 字段不同(Lycamobile 需要 sim_last4 + puk;Optus 需要 title)。

服务范围: 我们的保障仅覆盖充值面额本身。套餐内容(流量额度、 赠送流量、有效期)由运营商设定,可能随时变化——详见快速接入指南中的 "服务范围免责声明"。

const result = await client.submitActivation({
  iccid: '8961030000012345678',  // SIM 卡号,自动识别运营商
  activation_date: '2026-04-14', // 激活日期(悉尼时区)
  first_name: 'Wei',
  last_name: 'Zhang',
  passport_no: 'E12345678',
  passport_country: 'CN',
  passport_expiry: '2030-06-20',
  date_of_birth: '1990-03-15',
  address: '45 George St',
  suburb: 'Sydney',
  state: 'NSW',
  postcode: '2000',
  sim_last4: '5678',             // SIM 卡上印刷的后 4 位
  puk: '12345678',               // SIM 卡包装上的 PUK 码
});

console.log(result.carrier);         // "Lycamobile"(自动识别)
console.log(result.processing_mode); // "scheduled" 或 "immediate"

// 稍后查询激活状态
const detail = await client.getActivation(result.id);
console.log(detail.mobile_number);   // 分配的手机号,如 "0402033268"

其他语言

Python / PHP / Ruby SDK 与 TypeScript 版接口一致。方法命名遵循各语言惯例 (listProducts 在 Python 和 Ruby 中为 list_products),响应以原生 dict / array / hash 返回,错误以带类型的异常抛出(ApiError 对应 RFC 7807 错误,OAuthError 对应令牌端点错误)。

Python

from esim_atlas_sdk import EsimAtlasClient, ApiError, verify_webhook_signature

client = EsimAtlasClient(
    client_id="sk_live_abc123def456",
    client_secret="your_secret_here",
)

# 查询产品列表
products = client.list_products(region="Asia", limit=10)
for product in products["data"]:
    print(product["name"])

# 创建订单(幂等键防止重复下单)
try:
    order = client.create_order(
        {"line_items": [{"product_id": "uuid-here", "quantity": 5}]},
        idempotency_key="unique-idempotency-key-123",
    )
    print(order["order_number"])
except ApiError as err:
    print(err.status, err.title, err.detail)

# Webhook 签名验证(适用于 Flask / Django / FastAPI 等)
is_valid = verify_webhook_signature(raw_body, signature_header, webhook_secret)

PHP

require 'esim-atlas-sdk.php';

use EsimAtlas\Client;
use EsimAtlas\ApiError;

$client = new Client('sk_live_abc123def456', 'your_secret_here');

// 查询产品列表
$products = $client->listProducts(['region' => 'Asia', 'limit' => 10]);
foreach ($products['data'] as $product) {
    echo $product['name'], PHP_EOL;
}

// 创建订单(幂等键防止重复下单)
try {
    $order = $client->createOrder(
        ['line_items' => [['product_id' => 'uuid-here', 'quantity' => 5]]],
        'unique-idempotency-key-123'
    );
    echo $order['order_number'], PHP_EOL;
} catch (ApiError $err) {
    echo $err->status, ' ', $err->title, ': ', $err->detail, PHP_EOL;
}

// Webhook 签名验证
$isValid = EsimAtlas\verifyWebhookSignature($rawBody, $signatureHeader, $webhookSecret);

注意:PHP 的 OAuthError 通过 $errorCode 属性暴露 OAuth 错误码 (不是 $code —— 那是内置 Exception 的 int 类型属性)。

Ruby

require_relative 'esim_atlas_sdk'

client = EsimAtlas::Client.new(
  client_id: 'sk_live_abc123def456',
  client_secret: 'your_secret_here'
)

# 查询产品列表
products = client.list_products(region: 'Asia', limit: 10)
products['data'].each { |product| puts product['name'] }

# 创建订单(幂等键防止重复下单)
begin
  order = client.create_order(
    { line_items: [{ product_id: 'uuid-here', quantity: 5 }] },
    'unique-idempotency-key-123'
  )
  puts order['order_number']
rescue EsimAtlas::ApiError => e
  puts "#{e.status} #{e.title}: #{e.detail}"
end

# Webhook 签名验证(适用于 Rails / Sinatra 等)
is_valid = EsimAtlas.verify_webhook_signature(raw_body, signature_header, webhook_secret)