eSIM Atlas Partner API SDK
Lightweight, zero-dependency, single-file SDKs for the eSIM Atlas Partner API, available in four languages:
| Language | File | Requirements |
|---|---|---|
| TypeScript / JavaScript | esim-atlas-sdk.ts | Node.js 18+ or modern browsers (native fetch) |
| Python | esim_atlas_sdk.py | Python 3.8+ (standard library only) |
| PHP | esim-atlas-sdk.php | PHP 8.0+ with cURL and JSON extensions |
| Ruby | esim_atlas_sdk.rb | Ruby 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:
- On the first API call, the client exchanges your credentials for an access token.
- The token is cached in memory and reused for subsequent calls.
- 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
| Method | Description |
|---|---|
listProducts(params?) | List products with filtering (region, type, status) and cursor pagination |
getProduct(id) | Get product details including features and compatible devices |
Orders
| Method | Description |
|---|---|
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)
| Method | Description |
|---|---|
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
| Method | Description |
|---|---|
getEsimStatus(iccid) | Check eSIM status, data usage, and location |
Inventory
| Method | Description |
|---|---|
getInventoryStatus(params?) | Check product stock and availability |
Webhooks
| Method | Description |
|---|---|
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
| Method | Description |
|---|---|
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
| Event | Description |
|---|---|
order.created | New order placed |
order.fulfilled | Order fully fulfilled |
order.cancelled | Order cancelled |
esim.activated | eSIM activated on network |
esim.expired | eSIM validity expired |
payment.completed | Payment successfully processed |
payment.failed | Payment failed |
activation.submitted | Physical SIM activation submitted |
activation.confirmed | Physical SIM activation confirmed by carrier |
activation.activated | Physical SIM activated on network |
activation.failed | Physical 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 (listProducts → list_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 / JavaScript | esim-atlas-sdk.ts | Node.js 18+ 或现代浏览器(原生 fetch) |
| Python | esim_atlas_sdk.py | Python 3.8+(仅标准库) |
| PHP | esim-atlas-sdk.php | PHP 8.0+(需 cURL 和 JSON 扩展) |
| Ruby | esim_atlas_sdk.rb | Ruby 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 令牌:
- 首次 API 调用时,客户端使用凭据换取访问令牌
- 令牌缓存在内存中,后续调用自动复用
- 令牌到期前 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 前缀自动识别运营商。当前支持的运营商:Lycamobile 和
Optus——其他运营商的 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)