API 文档
通过编程方式访问虚拟号码、订单和账户余额。
概述
/v1 API 上的所有金额字段均为 IDR(印尼盾),以整数单位表示——例如 "price": 15000 和 "balance": 500000 表示 Rp 15,000 和 Rp 500,000。如需同一账本的 USD 原生投影,请使用上方的版本切换开关切换到 v2 API。
⟩认证
所有 API 请求均需 Bearer token。在控制台的账户设置中生成令牌,然后在每个请求中包含它:
缺少有效令牌的请求将收到 401 UNAUTHORIZED 响应。
⟩Base URL
以下所有端点路径均相对于:
⟩响应格式
每个响应返回结构一致的 JSON。所有响应都包含 x-request-id 响应头,用于调试。
{
"success": true,
"data": { ... }
} {
"success": false,
"error": {
"code": "ERROR_CODE",
"message": "Human-readable message"
}
} /v1 API 上的所有金额字段均为 IDR(印尼盾),以整数单位表示——例如 "price": 15000 和 "balance": 500000 表示 Rp 15,000 和 Rp 500,000。如需同一账本的 USD 原生投影,请使用上方的版本切换开关切换到 v2 API。
/catalog/countries 返回所有可用国家列表。
参数
无
请求示例
curl -s https://api.smscode.gg/v1/catalog/countries \
-H "Authorization: Bearer YOUR_API_TOKEN"const res = await fetch("https://api.smscode.gg/v1/catalog/countries", {
headers: { Authorization: "Bearer YOUR_API_TOKEN" },
});
const data = await res.json();import requests
res = requests.get("https://api.smscode.gg/v1/catalog/countries",
headers={"Authorization": "Bearer YOUR_API_TOKEN"})
data = res.json()响应示例
{
"success": true,
"data": [
{
"id": 6,
"code": "ID",
"name": "Indonesia",
"dial_code": "+62",
"emoji": "🇮🇩",
"active": true
}
]
} /catalog/services 返回可用服务(平台)列表。可按国家筛选。
查询参数
| 名称 | 类型 | 必填 | 说明 |
|---|---|---|---|
country_id | integer | 否 | 筛选该国家可用的服务 |
请求示例
curl -s "https://api.smscode.gg/v1/catalog/services?country_id=6" \
-H "Authorization: Bearer YOUR_API_TOKEN"const res = await fetch("https://api.smscode.gg/v1/catalog/services?country_id=6", {
headers: { Authorization: "Bearer YOUR_API_TOKEN" },
});
const data = await res.json();import requests
res = requests.get("https://api.smscode.gg/v1/catalog/services",
params={"country_id": 6},
headers={"Authorization": "Bearer YOUR_API_TOKEN"})
data = res.json()响应示例
{
"success": true,
"data": [
{
"id": 3,
"code": "wa",
"name": "WhatsApp",
"active": true
}
]
} /catalog/products 返回分页的可用产品列表。可按国家和/或平台筛选。
查询参数
| 名称 | 类型 | 必填 | 说明 |
|---|---|---|---|
country_id | integer | 否 | 按国家 ID 筛选 |
platform_id | integer | 否 | 按平台/服务 ID 筛选 |
sort | string | 否 | 排序方式:price_asc(默认)、price_desc、available_asc、available_desc、name_asc、name_desc |
limit | integer | 否 | 每页结果数(1-10,000,默认 1,000) |
page | integer | 否 | 页码(最小 1,默认 1) |
请求示例
curl -s "https://api.smscode.gg/v1/catalog/products?country_id=6&platform_id=3&limit=10&page=1" \
-H "Authorization: Bearer YOUR_API_TOKEN"const params = new URLSearchParams({
country_id: "6", platform_id: "3", limit: "10", page: "1",
});
const res = await fetch(`https://api.smscode.gg/v1/catalog/products?${params}`, {
headers: { Authorization: "Bearer YOUR_API_TOKEN" },
});
const data = await res.json();import requests
res = requests.get("https://api.smscode.gg/v1/catalog/products",
params={"country_id": 6, "platform_id": 3, "limit": 10, "page": 1},
headers={"Authorization": "Bearer YOUR_API_TOKEN"})
data = res.json()响应示例
{
"success": true,
"data": [
{
"id": 142,
"name": "WhatsApp Indonesia",
"country_id": 6,
"platform_id": 3,
"catalog_product_id": 87,
"available": 42,
"price": 15000,
"active": true
}
],
"meta": { "page": 1, "limit": 10, "count": 1 }
} /catalog/exchange-rate 返回用于货币换算的当前 USD/IDR 汇率。
查询参数
| 名称 | 类型 | 必填 | 说明 |
|---|---|---|---|
pair | string | 否 | 货币对(默认:USD/IDR) |
请求示例
curl -s "https://api.smscode.gg/v1/catalog/exchange-rate?pair=USD/IDR" \
-H "Authorization: Bearer YOUR_API_TOKEN"const res = await fetch("https://api.smscode.gg/v1/catalog/exchange-rate?pair=USD/IDR", {
headers: { Authorization: "Bearer YOUR_API_TOKEN" },
});
const data = await res.json();import requests
res = requests.get("https://api.smscode.gg/v1/catalog/exchange-rate",
params={"pair": "USD/IDR"},
headers={"Authorization": "Bearer YOUR_API_TOKEN"})
data = res.json()响应示例
{
"success": true,
"data": {
"pair": "USD/IDR",
"base_currency": "USD",
"quote_currency": "IDR",
"rate": 16250
}
} /balance 返回已认证用户的账户余额。
参数
无
请求示例
curl -s https://api.smscode.gg/v1/balance \
-H "Authorization: Bearer YOUR_API_TOKEN"const res = await fetch("https://api.smscode.gg/v1/balance", {
headers: { Authorization: "Bearer YOUR_API_TOKEN" },
});
const data = await res.json();import requests
res = requests.get("https://api.smscode.gg/v1/balance",
headers={"Authorization": "Bearer YOUR_API_TOKEN"})
data = res.json()响应示例
{
"success": true,
"data": {
"currency": "IDR",
"balance": 500000
}
} /orders 返回已认证用户的订单列表,按最近排序。支持按状态筛选和通过 offset 分页。
查询参数
| 名称 | 类型 | 必填 | 说明 |
|---|---|---|---|
limit | integer | 否 | 最大结果数(1-100,默认 20) |
offset | integer | 否 | 跳过的结果数(默认 0) |
status | string | 否 | 按状态筛选:ACTIVE、OTP_RECEIVED、COMPLETED、CANCELED、EXPIRED(不区分大小写) |
请求示例
curl -s "https://api.smscode.gg/v1/orders?limit=5&status=ACTIVE" \
-H "Authorization: Bearer YOUR_API_TOKEN"const params = new URLSearchParams({
limit: "5", status: "ACTIVE", offset: "0",
});
const res = await fetch(`https://api.smscode.gg/v1/orders?${params}`, {
headers: { Authorization: "Bearer YOUR_API_TOKEN" },
});
const data = await res.json();import requests
res = requests.get("https://api.smscode.gg/v1/orders",
params={"limit": 5, "status": "ACTIVE", "offset": 0},
headers={"Authorization": "Bearer YOUR_API_TOKEN"})
data = res.json()响应示例
{
"success": true,
"data": [
{
"id": 1001,
"status": "ACTIVE",
"created_at": "2026-02-25T10:00:00+00:00",
"product_id": 142,
"phone_number": "+6281234567890",
"amount": 15000,
"otp_code": null,
"otp_received_at": null,
"expires_at": "2026-02-25T10:20:00+00:00",
"canceled_at": null,
"failed_reason": null
}
]
} /orders/{id} 根据 ID 返回单个订单。仅返回已认证用户的订单。
路径参数
| 名称 | 类型 | 必填 | 说明 |
|---|---|---|---|
id | integer | 是 | 订单 ID(路径参数) |
请求示例
curl -s https://api.smscode.gg/v1/orders/1001 \
-H "Authorization: Bearer YOUR_API_TOKEN"const res = await fetch("https://api.smscode.gg/v1/orders/1001", {
headers: { Authorization: "Bearer YOUR_API_TOKEN" },
});
const data = await res.json();import requests
res = requests.get("https://api.smscode.gg/v1/orders/1001",
headers={"Authorization": "Bearer YOUR_API_TOKEN"})
data = res.json()响应示例
{
"success": true,
"data": {
"id": 1001,
"status": "OTP_RECEIVED",
"created_at": "2026-02-25T10:00:00+00:00",
"product_id": 142,
"phone_number": "+6281234567890",
"amount": 15000,
"otp_code": "123456",
"otp_received_at": "2026-02-25T10:05:00+00:00",
"expires_at": "2026-02-25T10:20:00+00:00",
"canceled_at": null,
"failed_reason": null
}
} /orders/active 列出所有当前活跃的订单(ACTIVE + OTP_RECEIVED)。用于轮询 OTP 状态更新。
参数
无
请求示例
curl -s https://api.smscode.gg/v1/orders/active \
-H "Authorization: Bearer YOUR_API_TOKEN"const res = await fetch("https://api.smscode.gg/v1/orders/active", {
headers: { Authorization: "Bearer YOUR_API_TOKEN" },
});
const data = await res.json();import requests
res = requests.get("https://api.smscode.gg/v1/orders/active",
headers={"Authorization": "Bearer YOUR_API_TOKEN"})
data = res.json()响应示例
{
"success": true,
"data": [
{
"id": 1001,
"status": "OTP_RECEIVED",
"otp_code": "123456",
"otp_message": "Your verification code is 123456",
"otp_received_at": "2026-02-25T10:05:00+00:00",
"expires_at": "2026-02-25T10:20:00+00:00",
"failed_reason": null
},
{
"id": 1002,
"status": "ACTIVE",
"otp_code": null,
"otp_message": null,
"otp_received_at": null,
"expires_at": "2026-02-25T10:50:00+00:00",
"failed_reason": null
}
]
} /orders/create 创建新的虚拟号码订单。自动扣除余额。支持可选的 Idempotency-Key 请求头,防止网络重试时产生重复订单。
请求体
| 名称 | 类型 | 必填 | 说明 |
|---|---|---|---|
product_id | integer | 否 | 要订购的产品 ID。只能二选一:product_id 或 catalog_product_id,不可同时提供。 |
catalog_product_id | integer | 否 | 稳定的产品 ID — 服务器会挑选当前最优的报价(最便宜、兼顾可靠性)。提供此项或 product_id 其一即可。 |
max_price | integer · string | 否 | 可选的价格上限。v1:IDR 整数。v2:USD 小数字符串(如 "0.50")。 |
prefer_provider | string | 否 | 可选的供应商代码,在报价相同时优先选用。 |
policy | string | 否 | 可选的路由策略,仅在使用 catalog_product_id 时有效。取值:cheapest(默认)选择价格最低的健康报价;best_success 优先按近期发送成功率对报价排序。best_success 以每个供应商在最近 30 个完整自然日内收到 OTP 的订单占比来评分,按 10% 一档划分,且只有当该供应商在此区间内至少有 20 笔订单时才计入——低于该门槛或没有历史记录的供应商视为中性,因此新报价不会被埋没(需主动选用;该信号从中性起步)。若同时设置了 prefer_provider,则优先供应商仍排在最前。 |
quantity | integer | 否 | 数量(1-100,默认 1) |
传递 Idempotency-Key 请求头,可安全重试而不会创建重复订单。Key 可包含字母、数字、连字符和下划线(A-Z a-z 0-9 _ -),最多 128 个字符;无效的 key 会返回 422 VALIDATION_ERROR。使用相同的 key 和相同的请求体重试将重放原始结果(包括部分成功时的 failed_count)。已到达供应商但失败的重试会被记录,重试时返回相同的错误 — 请使用新的 key 重新尝试。无副作用的失败(余额不足、无可用报价)会释放该 key,因此你可以充值后用相同的 key 重试。用不同的请求体重复使用某个 key 会返回 422 IDEMPOTENCY_KEY_REUSED,而该 key 仍在处理中的请求会返回 409 REQUEST_IN_PROGRESS。create 响应中的 failed_reason 字段始终为 null — 它仅在订单轮询/列表时填充。
请求示例
curl -s -X POST https://api.smscode.gg/v1/orders/create \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: unique-request-id-123" \
-d '{"product_id":142,"quantity":1}'
# Or order by stable catalog_product_id — the server picks the best current offer
# (cheapest, reliability-aware). Optional max_price caps the price; on v1 it is an
# IDR integer, on v2 it is a USD decimal string (e.g. "0.50"). prefer_provider
# (provider code) is a soft tie-breaker. Pass EITHER product_id OR catalog_product_id.
curl -s -X POST https://api.smscode.gg/v1/orders/create \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: unique-request-id-124" \
-d '{"catalog_product_id":87,"max_price":20000}'const res = await fetch("https://api.smscode.gg/v1/orders/create", {
method: "POST",
headers: {
Authorization: "Bearer YOUR_API_TOKEN",
"Content-Type": "application/json",
"Idempotency-Key": "unique-request-id-123",
},
body: JSON.stringify({ product_id: 142, quantity: 1 }),
});
const data = await res.json();import requests
res = requests.post("https://api.smscode.gg/v1/orders/create",
json={"product_id": 142, "quantity": 1},
headers={
"Authorization": "Bearer YOUR_API_TOKEN",
"Idempotency-Key": "unique-request-id-123",
})
data = res.json()响应示例
{
"success": true,
"data": {
"orders": [
{
"id": 1002,
"status": "ACTIVE",
"product_id": 142,
"catalog_product_id": 87,
"amount": 15000,
"phone_number": "+6281234567891",
"otp_code": null,
"otp_received_at": null,
"expires_at": "2026-02-25T10:50:00+00:00",
"failed_reason": null
}
],
"failed_count": 0
}
} /orders/cancel 取消活跃订单。租用费用将退回账户余额。
请求体
| 名称 | 类型 | 必填 | 说明 |
|---|---|---|---|
id | integer | 是 | 要取消的订单 ID |
请求示例
curl -s -X POST https://api.smscode.gg/v1/orders/cancel \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"id":1001}'const res = await fetch("https://api.smscode.gg/v1/orders/cancel", {
method: "POST",
headers: {
Authorization: "Bearer YOUR_API_TOKEN",
"Content-Type": "application/json",
},
body: JSON.stringify({ id: 1001 }),
});
const data = await res.json();import requests
res = requests.post("https://api.smscode.gg/v1/orders/cancel",
json={"id": 1001},
headers={"Authorization": "Bearer YOUR_API_TOKEN"})
data = res.json()响应示例
{
"success": true,
"data": {
"order_id": 1001,
"status": "CANCELED",
"refund_amount": 15000,
"new_balance": 515000
}
} /orders/finish 在收到 OTP 后将订单标记为已完成。这会立即释放号码,而非等待过期。
请求体
| 名称 | 类型 | 必填 | 说明 |
|---|---|---|---|
id | integer | 是 | 要完成的订单 ID |
请求示例
curl -s -X POST https://api.smscode.gg/v1/orders/finish \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"id":1001}'const res = await fetch("https://api.smscode.gg/v1/orders/finish", {
method: "POST",
headers: {
Authorization: "Bearer YOUR_API_TOKEN",
"Content-Type": "application/json",
},
body: JSON.stringify({ id: 1001 }),
});
const data = await res.json();import requests
res = requests.post("https://api.smscode.gg/v1/orders/finish",
json={"id": 1001},
headers={"Authorization": "Bearer YOUR_API_TOKEN"})
data = res.json()响应示例
{
"success": true,
"data": {
"order_id": 1001,
"status": "COMPLETED"
}
} /orders/resend 请求平台向租用号码重新发送 SMS。并非所有平台都支持重发 — 请查看响应中的 resent 字段。
请求体
| 名称 | 类型 | 必填 | 说明 |
|---|---|---|---|
id | integer | 是 | 要重发 SMS 的订单 ID |
请求示例
curl -s -X POST https://api.smscode.gg/v1/orders/resend \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"id":1001}'const res = await fetch("https://api.smscode.gg/v1/orders/resend", {
method: "POST",
headers: {
Authorization: "Bearer YOUR_API_TOKEN",
"Content-Type": "application/json",
},
body: JSON.stringify({ id: 1001 }),
});
const data = await res.json();import requests
res = requests.post("https://api.smscode.gg/v1/orders/resend",
json={"id": 1001},
headers={"Authorization": "Bearer YOUR_API_TOKEN"})
data = res.json()响应示例
{
"success": true,
"data": {
"order_id": 1001,
"status": "ACTIVE",
"resent": true
}
} /webhook 返回您当前的 webhook 通知配置。
参数
无
请求示例
curl -s https://api.smscode.gg/v1/webhook \
-H "Authorization: Bearer YOUR_API_TOKEN"const res = await fetch("https://api.smscode.gg/v1/webhook", {
headers: { Authorization: "Bearer YOUR_API_TOKEN" },
});
const data = await res.json();import requests
res = requests.get("https://api.smscode.gg/v1/webhook",
headers={"Authorization": "Bearer YOUR_API_TOKEN"})
data = res.json()响应示例
{
"success": true,
"data": {
"webhook_url": "https://example.com/webhook",
"webhook_secret": "a1b2c3d4e5f6..."
}
} /webhook 更新您的 webhook URL 和/或密钥。首次设置 URL 时会自动生成密钥。发送空字符串可清除。URL 必须使用 HTTPS。
请求体
| 名称 | 类型 | 必填 | 说明 |
|---|---|---|---|
webhook_url | string | 否 | 接收 webhook 事件的 HTTPS URL(空字符串表示清除) |
webhook_secret | string | 否 | 用于 HMAC-SHA256 签名的共享密钥(首次设置时自动生成) |
至少需要一个字段。
请求示例
curl -s -X PATCH https://api.smscode.gg/v1/webhook \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"webhook_url":"https://example.com/webhook"}'const res = await fetch("https://api.smscode.gg/v1/webhook", {
method: "PATCH",
headers: {
Authorization: "Bearer YOUR_API_TOKEN",
"Content-Type": "application/json",
},
body: JSON.stringify({ webhook_url: "https://example.com/webhook" }),
});
const data = await res.json();import requests
res = requests.patch("https://api.smscode.gg/v1/webhook",
json={"webhook_url": "https://example.com/webhook"},
headers={"Authorization": "Bearer YOUR_API_TOKEN"})
data = res.json()响应示例
{
"success": true,
"data": {
"webhook_url": "https://example.com/webhook",
"webhook_secret": "a1b2c3d4e5f6..."
}
} /webhook/test 向已配置的 webhook URL 发送测试事件。返回服务器响应的 HTTP 状态码。可用于在正式使用前验证端点是否正常工作。
参数
无
请求示例
curl -s -X POST https://api.smscode.gg/v1/webhook/test \
-H "Authorization: Bearer YOUR_API_TOKEN"const res = await fetch("https://api.smscode.gg/v1/webhook/test", {
method: "POST",
headers: { Authorization: "Bearer YOUR_API_TOKEN" },
});
const data = await res.json();import requests
res = requests.post("https://api.smscode.gg/v1/webhook/test",
headers={"Authorization": "Bearer YOUR_API_TOKEN"})
data = res.json()响应示例
{
"success": true,
"data": {
"status_code": 200
}
} ⟩Webhook 通知
配置 webhook URL 以接收订单事件的实时推送通知,无需轮询。这是机器人脚本的推荐方式。
事件列表
| 事件 | 触发条件 |
|---|---|
order.otp_received | OTP 验证码已送达租用号码 |
order.completed | 订单已标记为完成(手动或到期) |
order.expired | 订单过期且未收到 OTP(余额已退还) |
order.canceled | 订单被用户取消(余额已退还) |
Payload
{
"event": "order.otp_received",
"timestamp": "2026-02-25T12:00:00+00:00",
"data": {
"order_id": 1001,
"phone_number": "+628123456789",
"otp_code": "1234",
"otp_message": "Your verification code is 1234",
"product_id": 42,
"catalog_product_id": 87,
"country": "Indonesia",
"platform": "WhatsApp"
}
} 签名验证
每个 webhook 请求都包含 X-Webhook-Signature 请求头,其中包含使用您的 webhook_secret 作为密钥对请求体计算的 HMAC-SHA256 签名:
在您的服务器上验证此签名以确保请求的真实性。发送采用即发即忘模式,3 秒超时且不重试。
⟩速率限制
API 请求按端点分组进行速率限制。超出限制将返回 429 Too Many Requests,并附带 Retry-After 响应头,指示需要等待的秒数。
| 端点分组 | 限制 | 时间窗口 |
|---|---|---|
| 目录(国家、服务、产品、汇率) | 5,000 次请求 | 60 秒 |
| 余额 | 600 次请求 | 60 秒 |
| 订单查询(列表、详情、活跃) | 5,000 次请求 | 60 秒 |
| 创建订单 | 3,000 次请求 | 60 秒 |
| 取消订单 | 1,000 次请求 | 60 秒 |
| 订单操作(完成、重发) | 1,000 次请求 | 60 秒 |
| Webhook 配置(查询、更新) | 600 次请求 | 60 秒 |
| Webhook 测试 | 10 次请求 | 60 秒 |
⟩错误代码
错误响应在 error.code 中包含以下代码之一:
| 代码 | HTTP | 说明 |
|---|---|---|
UNAUTHORIZED | 401 | 缺少或无效的 API 令牌 |
FORBIDDEN | 403 | 访问被拒绝 |
NOT_FOUND | 404 | 资源未找到(订单、汇率等) |
CONFLICT | 409 | 重复请求或资源冲突 |
INSUFFICIENT_BALANCE | 409 | 余额不足,无法创建订单 |
VALIDATION_ERROR | 422 | 请求参数验证失败 |
RATE_LIMIT_EXCEEDED | 429 | 请求过于频繁(请查看 Retry-After 响应头) |
INTERNAL_ERROR | 500 | 服务器内部错误 |
PROVIDER_ERROR | 422 | 上游 SMS 提供商拒绝了请求。订单创建失败时,错误可能携带 <code>details</code>:<code>cause_counts</code>(旧版 <code>product_id</code> 订单 — 按原因分类的统计)或 <code>attempts</code>(<code>catalog_product_id</code> 订单 — 每次尝试的结果),取值为 <code>ok</code>、<code>no_numbers</code>、<code>insufficient_balance</code>、<code>price_rejected</code>、<code>provider_unavailable</code>、<code>provider_account_balance</code>、<code>provider_error</code>。 |
NO_OFFER_AVAILABLE | 422 | 没有符合所请求产品与策略(价格上限、可用性)的有效报价。 |
CANCEL_TOO_EARLY | 409 | 订单创建时间过短,无法取消 — 请等待 2 分钟 |
REQUEST_IN_PROGRESS | 409 | 使用该幂等 key 的创建请求仍在处理中 |
IDEMPOTENCY_KEY_REUSED | 422 | 该幂等 key 已用于不同的请求体 |
SERVICE_UNAVAILABLE | 503 | 服务暂时不可用(维护中) |
概述
/v2 API 上的所有金额字段均为 USD,以金额对象返回——{ "amount": "0.92", "currency": "USD", "canonical_amount": 15000, "canonical_currency": "IDR" }。amount 是十进制字符串;canonical_amount 是精确的 IDR 账本值(用于对账)。所应用的 USD/IDR rate 在每个响应中通过 meta.fx 披露一次。v2 是在与 v1 相同的 IDR 账本之上的渲染时 USD 投影——它从不存储或结算 USD。
⟩认证
所有 API 请求均需 Bearer token。在控制台的账户设置中生成令牌,然后在每个请求中包含它:
缺少有效令牌的请求将收到 401 UNAUTHORIZED 响应。
⟩Base URL
以下所有端点路径均相对于:
⟩响应格式
每个响应返回结构一致的 JSON。所有响应都包含 x-request-id 响应头,用于调试。
{
"success": true,
"data": { ... }
} {
"success": false,
"error": {
"code": "ERROR_CODE",
"message": "Human-readable message"
}
} v2 错误:无可用汇率(503)
{
"success": false,
"error": { "code": "FX_RATE_UNAVAILABLE", "message": "USD/IDR exchange rate is unavailable" }
} /v2 API 上的所有金额字段均为 USD,以金额对象返回——{ "amount": "0.92", "currency": "USD", "canonical_amount": 15000, "canonical_currency": "IDR" }。amount 是十进制字符串;canonical_amount 是精确的 IDR 账本值(用于对账)。所应用的 USD/IDR rate 在每个响应中通过 meta.fx 披露一次。v2 是在与 v1 相同的 IDR 账本之上的渲染时 USD 投影——它从不存储或结算 USD。
/catalog/countries 返回所有可用国家列表。
参数
无
请求示例
curl -s https://api.smscode.gg/v2/catalog/countries \
-H "Authorization: Bearer YOUR_API_TOKEN"const res = await fetch("https://api.smscode.gg/v2/catalog/countries", {
headers: { Authorization: "Bearer YOUR_API_TOKEN" },
});
const data = await res.json();import requests
res = requests.get("https://api.smscode.gg/v2/catalog/countries",
headers={"Authorization": "Bearer YOUR_API_TOKEN"})
data = res.json()响应示例
{
"success": true,
"data": [
{
"id": 6,
"code": "ID",
"name": "Indonesia",
"dial_code": "+62",
"emoji": "🇮🇩",
"active": true
}
]
} 与 v1 相同——仅基础路径改变(/v1 → /v2)。
/catalog/services 返回可用服务(平台)列表。可按国家筛选。
查询参数
| 名称 | 类型 | 必填 | 说明 |
|---|---|---|---|
country_id | integer | 否 | 筛选该国家可用的服务 |
请求示例
curl -s "https://api.smscode.gg/v2/catalog/services?country_id=6" \
-H "Authorization: Bearer YOUR_API_TOKEN"const res = await fetch("https://api.smscode.gg/v2/catalog/services?country_id=6", {
headers: { Authorization: "Bearer YOUR_API_TOKEN" },
});
const data = await res.json();import requests
res = requests.get("https://api.smscode.gg/v2/catalog/services",
params={"country_id": 6},
headers={"Authorization": "Bearer YOUR_API_TOKEN"})
data = res.json()响应示例
{
"success": true,
"data": [
{
"id": 3,
"code": "wa",
"name": "WhatsApp",
"active": true
}
]
} 与 v1 相同——仅基础路径改变(/v1 → /v2)。
/catalog/products 返回分页的可用产品列表。可按国家和/或平台筛选。
查询参数
| 名称 | 类型 | 必填 | 说明 |
|---|---|---|---|
country_id | integer | 否 | 按国家 ID 筛选 |
platform_id | integer | 否 | 按平台/服务 ID 筛选 |
sort | string | 否 | 排序方式:price_asc(默认)、price_desc、available_asc、available_desc、name_asc、name_desc |
limit | integer | 否 | 每页结果数(1-10,000,默认 1,000) |
page | integer | 否 | 页码(最小 1,默认 1) |
请求示例
curl -s "https://api.smscode.gg/v2/catalog/products?country_id=6&platform_id=3&limit=10&page=1" \
-H "Authorization: Bearer YOUR_API_TOKEN"const params = new URLSearchParams({
country_id: "6", platform_id: "3", limit: "10", page: "1",
});
const res = await fetch(`https://api.smscode.gg/v2/catalog/products?${params}`, {
headers: { Authorization: "Bearer YOUR_API_TOKEN" },
});
const data = await res.json();import requests
res = requests.get("https://api.smscode.gg/v2/catalog/products",
params={"country_id": 6, "platform_id": 3, "limit": 10, "page": 1},
headers={"Authorization": "Bearer YOUR_API_TOKEN"})
data = res.json()响应示例
{
"success": true,
"data": [
{
"id": 142,
"name": "WhatsApp Indonesia",
"country_id": 6,
"platform_id": 3,
"catalog_product_id": 87,
"available": 42,
"price": { "amount": "0.9231", "currency": "USD", "canonical_amount": 15000, "canonical_currency": "IDR" },
"active": true
}
],
"meta": { "page": 1, "limit": 10, "count": 1, "fx": { "pair": "USD/IDR", "rate": 16250, "rate_as_of": "2026-05-27T08:00:00+00:00" } }
} v2:金额字段为 USD 金额对象,且响应携带单个 meta.fx { pair, rate, rate_as_of }。rate 是每 1 USD 对应的整数 IDR,因此 USD = canonical_amount / rate。总额使用 2 位小数;单项价格/退款使用 4 位。严格为正的金额绝不会舍入为 0.00。rate_as_of 是该汇率的 RFC3339 时间戳(+00:00 形式),若未记录时间戳则为 null。
仅 v2:如果不存在可用的 USD/IDR 汇率,金额类 endpoint 将返回 503 FX_RATE_UNAVAILABLE 并带有 Retry-After 响应头,而不是金额响应体。v1 绝不会返回此情况。
/catalog/exchange-rate 返回用于货币换算的当前 USD/IDR 汇率。
参数
无——v2 始终返回 USD/IDR;v1 的 ?pair 参数被忽略。
请求示例
curl -s "https://api.smscode.gg/v2/catalog/exchange-rate?pair=USD/IDR" \
-H "Authorization: Bearer YOUR_API_TOKEN"const res = await fetch("https://api.smscode.gg/v2/catalog/exchange-rate?pair=USD/IDR", {
headers: { Authorization: "Bearer YOUR_API_TOKEN" },
});
const data = await res.json();import requests
res = requests.get("https://api.smscode.gg/v2/catalog/exchange-rate",
params={"pair": "USD/IDR"},
headers={"Authorization": "Bearer YOUR_API_TOKEN"})
data = res.json()响应示例
{
"success": true,
"data": { "pair": "USD/IDR", "rate": 16250, "rate_as_of": "2026-05-27T08:00:00+00:00" }
} v2:返回 { pair, rate, rate_as_of }(无 base_currency/quote_currency,无 meta 包装——汇率本身就是数据)。?pair 被忽略——v2 始终返回 USD/IDR(v1 会遵循 ?pair)。如果不存在可用汇率,则返回 503 FX_RATE_UNAVAILABLE。
/balance 返回已认证用户的账户余额。
参数
无
请求示例
curl -s https://api.smscode.gg/v2/balance \
-H "Authorization: Bearer YOUR_API_TOKEN"const res = await fetch("https://api.smscode.gg/v2/balance", {
headers: { Authorization: "Bearer YOUR_API_TOKEN" },
});
const data = await res.json();import requests
res = requests.get("https://api.smscode.gg/v2/balance",
headers={"Authorization": "Bearer YOUR_API_TOKEN"})
data = res.json()响应示例
{
"success": true,
"data": {
"balance": { "amount": "30.77", "currency": "USD", "canonical_amount": 500000, "canonical_currency": "IDR" }
},
"meta": { "fx": { "pair": "USD/IDR", "rate": 16250, "rate_as_of": "2026-05-27T08:00:00+00:00" } }
} v2:金额字段为 USD 金额对象,且响应携带单个 meta.fx { pair, rate, rate_as_of }。rate 是每 1 USD 对应的整数 IDR,因此 USD = canonical_amount / rate。总额使用 2 位小数;单项价格/退款使用 4 位。严格为正的金额绝不会舍入为 0.00。rate_as_of 是该汇率的 RFC3339 时间戳(+00:00 形式),若未记录时间戳则为 null。
仅 v2:如果不存在可用的 USD/IDR 汇率,金额类 endpoint 将返回 503 FX_RATE_UNAVAILABLE 并带有 Retry-After 响应头,而不是金额响应体。v1 绝不会返回此情况。
/orders 返回已认证用户的订单列表,按最近排序。支持按状态筛选和通过 offset 分页。
查询参数
| 名称 | 类型 | 必填 | 说明 |
|---|---|---|---|
limit | integer | 否 | 最大结果数(1-100,默认 20) |
offset | integer | 否 | 跳过的结果数(默认 0) |
status | string | 否 | 按状态筛选:ACTIVE、OTP_RECEIVED、COMPLETED、CANCELED、EXPIRED(不区分大小写) |
请求示例
curl -s "https://api.smscode.gg/v2/orders?limit=5&status=ACTIVE" \
-H "Authorization: Bearer YOUR_API_TOKEN"const params = new URLSearchParams({
limit: "5", status: "ACTIVE", offset: "0",
});
const res = await fetch(`https://api.smscode.gg/v2/orders?${params}`, {
headers: { Authorization: "Bearer YOUR_API_TOKEN" },
});
const data = await res.json();import requests
res = requests.get("https://api.smscode.gg/v2/orders",
params={"limit": 5, "status": "ACTIVE", "offset": 0},
headers={"Authorization": "Bearer YOUR_API_TOKEN"})
data = res.json()响应示例
{
"success": true,
"data": [
{
"id": 1001,
"status": "ACTIVE",
"created_at": "2026-02-25T10:00:00+00:00",
"product_id": 142,
"phone_number": "+6281234567890",
"amount": { "amount": "0.9231", "currency": "USD", "canonical_amount": 15000, "canonical_currency": "IDR" },
"otp_code": null,
"otp_received_at": null,
"expires_at": "2026-02-25T10:20:00+00:00",
"canceled_at": null,
"failed_reason": null
}
],
"meta": { "fx": { "pair": "USD/IDR", "rate": 16250, "rate_as_of": "2026-05-27T08:00:00+00:00" } }
} v2:金额字段为 USD 金额对象,且响应携带单个 meta.fx { pair, rate, rate_as_of }。rate 是每 1 USD 对应的整数 IDR,因此 USD = canonical_amount / rate。总额使用 2 位小数;单项价格/退款使用 4 位。严格为正的金额绝不会舍入为 0.00。rate_as_of 是该汇率的 RFC3339 时间戳(+00:00 形式),若未记录时间戳则为 null。
仅 v2:如果不存在可用的 USD/IDR 汇率,金额类 endpoint 将返回 503 FX_RATE_UNAVAILABLE 并带有 Retry-After 响应头,而不是金额响应体。v1 绝不会返回此情况。
/orders/{id} 根据 ID 返回单个订单。仅返回已认证用户的订单。
路径参数
| 名称 | 类型 | 必填 | 说明 |
|---|---|---|---|
id | integer | 是 | 订单 ID(路径参数) |
请求示例
curl -s https://api.smscode.gg/v2/orders/1001 \
-H "Authorization: Bearer YOUR_API_TOKEN"const res = await fetch("https://api.smscode.gg/v2/orders/1001", {
headers: { Authorization: "Bearer YOUR_API_TOKEN" },
});
const data = await res.json();import requests
res = requests.get("https://api.smscode.gg/v2/orders/1001",
headers={"Authorization": "Bearer YOUR_API_TOKEN"})
data = res.json()响应示例
{
"success": true,
"data": {
"id": 1001,
"status": "OTP_RECEIVED",
"created_at": "2026-02-25T10:00:00+00:00",
"product_id": 142,
"phone_number": "+6281234567890",
"amount": { "amount": "0.9231", "currency": "USD", "canonical_amount": 15000, "canonical_currency": "IDR" },
"otp_code": "123456",
"otp_received_at": "2026-02-25T10:05:00+00:00",
"expires_at": "2026-02-25T10:20:00+00:00",
"canceled_at": null,
"failed_reason": null
},
"meta": { "fx": { "pair": "USD/IDR", "rate": 16250, "rate_as_of": "2026-05-27T08:00:00+00:00" } }
} v2:金额字段为 USD 金额对象,且响应携带单个 meta.fx { pair, rate, rate_as_of }。rate 是每 1 USD 对应的整数 IDR,因此 USD = canonical_amount / rate。总额使用 2 位小数;单项价格/退款使用 4 位。严格为正的金额绝不会舍入为 0.00。rate_as_of 是该汇率的 RFC3339 时间戳(+00:00 形式),若未记录时间戳则为 null。
仅 v2:如果不存在可用的 USD/IDR 汇率,金额类 endpoint 将返回 503 FX_RATE_UNAVAILABLE 并带有 Retry-After 响应头,而不是金额响应体。v1 绝不会返回此情况。
/orders/active 列出所有当前活跃的订单(ACTIVE + OTP_RECEIVED)。用于轮询 OTP 状态更新。
参数
无
请求示例
curl -s https://api.smscode.gg/v2/orders/active \
-H "Authorization: Bearer YOUR_API_TOKEN"const res = await fetch("https://api.smscode.gg/v2/orders/active", {
headers: { Authorization: "Bearer YOUR_API_TOKEN" },
});
const data = await res.json();import requests
res = requests.get("https://api.smscode.gg/v2/orders/active",
headers={"Authorization": "Bearer YOUR_API_TOKEN"})
data = res.json()响应示例
{
"success": true,
"data": [
{
"id": 1001,
"status": "OTP_RECEIVED",
"otp_code": "123456",
"otp_message": "Your verification code is 123456",
"otp_received_at": "2026-02-25T10:05:00+00:00",
"expires_at": "2026-02-25T10:20:00+00:00",
"failed_reason": null
},
{
"id": 1002,
"status": "ACTIVE",
"otp_code": null,
"otp_message": null,
"otp_received_at": null,
"expires_at": "2026-02-25T10:50:00+00:00",
"failed_reason": null
}
]
} v2:此 endpoint 不涉及金额——它不返回 amount,也不返回 meta.fx(结构与 v1 相同,位于 /v2 下)。
/orders/create 创建新的虚拟号码订单。自动扣除余额。支持可选的 Idempotency-Key 请求头,防止网络重试时产生重复订单。
请求体
| 名称 | 类型 | 必填 | 说明 |
|---|---|---|---|
product_id | integer | 否 | 要订购的产品 ID。只能二选一:product_id 或 catalog_product_id,不可同时提供。 |
catalog_product_id | integer | 否 | 稳定的产品 ID — 服务器会挑选当前最优的报价(最便宜、兼顾可靠性)。提供此项或 product_id 其一即可。 |
max_price | integer · string | 否 | 可选的价格上限。v1:IDR 整数。v2:USD 小数字符串(如 "0.50")。 |
prefer_provider | string | 否 | 可选的供应商代码,在报价相同时优先选用。 |
policy | string | 否 | 可选的路由策略,仅在使用 catalog_product_id 时有效。取值:cheapest(默认)选择价格最低的健康报价;best_success 优先按近期发送成功率对报价排序。best_success 以每个供应商在最近 30 个完整自然日内收到 OTP 的订单占比来评分,按 10% 一档划分,且只有当该供应商在此区间内至少有 20 笔订单时才计入——低于该门槛或没有历史记录的供应商视为中性,因此新报价不会被埋没(需主动选用;该信号从中性起步)。若同时设置了 prefer_provider,则优先供应商仍排在最前。 |
quantity | integer | 否 | 数量(1-100,默认 1) |
传递 Idempotency-Key 请求头,可安全重试而不会创建重复订单。Key 可包含字母、数字、连字符和下划线(A-Z a-z 0-9 _ -),最多 128 个字符;无效的 key 会返回 422 VALIDATION_ERROR。使用相同的 key 和相同的请求体重试将重放原始结果(包括部分成功时的 failed_count)。已到达供应商但失败的重试会被记录,重试时返回相同的错误 — 请使用新的 key 重新尝试。无副作用的失败(余额不足、无可用报价)会释放该 key,因此你可以充值后用相同的 key 重试。用不同的请求体重复使用某个 key 会返回 422 IDEMPOTENCY_KEY_REUSED,而该 key 仍在处理中的请求会返回 409 REQUEST_IN_PROGRESS。create 响应中的 failed_reason 字段始终为 null — 它仅在订单轮询/列表时填充。
请求示例
curl -s -X POST https://api.smscode.gg/v2/orders/create \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: unique-request-id-123" \
-d '{"product_id":142,"quantity":1}'
# Or order by stable catalog_product_id — the server picks the best current offer
# (cheapest, reliability-aware). Optional max_price caps the price; on v1 it is an
# IDR integer, on v2 it is a USD decimal string (e.g. "0.50"). prefer_provider
# (provider code) is a soft tie-breaker. Pass EITHER product_id OR catalog_product_id.
curl -s -X POST https://api.smscode.gg/v2/orders/create \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: unique-request-id-124" \
-d '{"catalog_product_id":87,"max_price":20000}'const res = await fetch("https://api.smscode.gg/v2/orders/create", {
method: "POST",
headers: {
Authorization: "Bearer YOUR_API_TOKEN",
"Content-Type": "application/json",
"Idempotency-Key": "unique-request-id-123",
},
body: JSON.stringify({ product_id: 142, quantity: 1 }),
});
const data = await res.json();import requests
res = requests.post("https://api.smscode.gg/v2/orders/create",
json={"product_id": 142, "quantity": 1},
headers={
"Authorization": "Bearer YOUR_API_TOKEN",
"Idempotency-Key": "unique-request-id-123",
})
data = res.json()响应示例
{
"success": true,
"data": {
"orders": [
{
"id": 1002,
"status": "ACTIVE",
"product_id": 142,
"catalog_product_id": 87,
"amount": { "amount": "0.9231", "currency": "USD", "canonical_amount": 15000, "canonical_currency": "IDR" },
"phone_number": "+6281234567891",
"otp_code": null,
"otp_received_at": null,
"expires_at": "2026-02-25T10:50:00+00:00",
"failed_reason": null
}
],
"failed_count": 0
},
"meta": { "fx": { "pair": "USD/IDR", "rate": 16250, "rate_as_of": "2026-05-27T08:00:00+00:00" } }
} v2:金额字段为 USD 金额对象,且响应携带单个 meta.fx { pair, rate, rate_as_of }。rate 是每 1 USD 对应的整数 IDR,因此 USD = canonical_amount / rate。总额使用 2 位小数;单项价格/退款使用 4 位。严格为正的金额绝不会舍入为 0.00。rate_as_of 是该汇率的 RFC3339 时间戳(+00:00 形式),若未记录时间戳则为 null。
仅 v2:如果不存在可用的 USD/IDR 汇率,金额类 endpoint 将返回 503 FX_RATE_UNAVAILABLE 并带有 Retry-After 响应头,而不是金额响应体。v1 绝不会返回此情况。
/orders/cancel 取消活跃订单。租用费用将退回账户余额。
请求体
| 名称 | 类型 | 必填 | 说明 |
|---|---|---|---|
id | integer | 是 | 要取消的订单 ID |
请求示例
curl -s -X POST https://api.smscode.gg/v2/orders/cancel \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"id":1001}'const res = await fetch("https://api.smscode.gg/v2/orders/cancel", {
method: "POST",
headers: {
Authorization: "Bearer YOUR_API_TOKEN",
"Content-Type": "application/json",
},
body: JSON.stringify({ id: 1001 }),
});
const data = await res.json();import requests
res = requests.post("https://api.smscode.gg/v2/orders/cancel",
json={"id": 1001},
headers={"Authorization": "Bearer YOUR_API_TOKEN"})
data = res.json()响应示例
{
"success": true,
"data": {
"order_id": 1001,
"status": "CANCELED",
"refund_amount": { "amount": "0.9231", "currency": "USD", "canonical_amount": 15000, "canonical_currency": "IDR" },
"new_balance": { "amount": "31.69", "currency": "USD", "canonical_amount": 515000, "canonical_currency": "IDR" }
},
"meta": { "fx": { "pair": "USD/IDR", "rate": 16250, "rate_as_of": "2026-05-27T08:00:00+00:00" } }
} v2:金额字段为 USD 金额对象,且响应携带单个 meta.fx { pair, rate, rate_as_of }。rate 是每 1 USD 对应的整数 IDR,因此 USD = canonical_amount / rate。总额使用 2 位小数;单项价格/退款使用 4 位。严格为正的金额绝不会舍入为 0.00。rate_as_of 是该汇率的 RFC3339 时间戳(+00:00 形式),若未记录时间戳则为 null。
仅 v2:如果不存在可用的 USD/IDR 汇率,金额类 endpoint 将返回 503 FX_RATE_UNAVAILABLE 并带有 Retry-After 响应头,而不是金额响应体。v1 绝不会返回此情况。
/orders/finish 在收到 OTP 后将订单标记为已完成。这会立即释放号码,而非等待过期。
请求体
| 名称 | 类型 | 必填 | 说明 |
|---|---|---|---|
id | integer | 是 | 要完成的订单 ID |
请求示例
curl -s -X POST https://api.smscode.gg/v2/orders/finish \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"id":1001}'const res = await fetch("https://api.smscode.gg/v2/orders/finish", {
method: "POST",
headers: {
Authorization: "Bearer YOUR_API_TOKEN",
"Content-Type": "application/json",
},
body: JSON.stringify({ id: 1001 }),
});
const data = await res.json();import requests
res = requests.post("https://api.smscode.gg/v2/orders/finish",
json={"id": 1001},
headers={"Authorization": "Bearer YOUR_API_TOKEN"})
data = res.json()响应示例
{
"success": true,
"data": {
"order_id": 1001,
"status": "COMPLETED"
}
} 与 v1 相同——仅基础路径改变(/v1 → /v2)。
/orders/resend 请求平台向租用号码重新发送 SMS。并非所有平台都支持重发 — 请查看响应中的 resent 字段。
请求体
| 名称 | 类型 | 必填 | 说明 |
|---|---|---|---|
id | integer | 是 | 要重发 SMS 的订单 ID |
请求示例
curl -s -X POST https://api.smscode.gg/v2/orders/resend \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"id":1001}'const res = await fetch("https://api.smscode.gg/v2/orders/resend", {
method: "POST",
headers: {
Authorization: "Bearer YOUR_API_TOKEN",
"Content-Type": "application/json",
},
body: JSON.stringify({ id: 1001 }),
});
const data = await res.json();import requests
res = requests.post("https://api.smscode.gg/v2/orders/resend",
json={"id": 1001},
headers={"Authorization": "Bearer YOUR_API_TOKEN"})
data = res.json()响应示例
{
"success": true,
"data": {
"order_id": 1001,
"status": "ACTIVE",
"resent": true
}
} 与 v1 相同——仅基础路径改变(/v1 → /v2)。
/webhook 返回您当前的 webhook 通知配置。
参数
无
请求示例
curl -s https://api.smscode.gg/v2/webhook \
-H "Authorization: Bearer YOUR_API_TOKEN"const res = await fetch("https://api.smscode.gg/v2/webhook", {
headers: { Authorization: "Bearer YOUR_API_TOKEN" },
});
const data = await res.json();import requests
res = requests.get("https://api.smscode.gg/v2/webhook",
headers={"Authorization": "Bearer YOUR_API_TOKEN"})
data = res.json()响应示例
{
"success": true,
"data": {
"webhook_url": "https://example.com/webhook",
"webhook_secret": "a1b2c3d4e5f6..."
}
} 与 v1 相同——仅基础路径改变(/v1 → /v2)。
/webhook 更新您的 webhook URL 和/或密钥。首次设置 URL 时会自动生成密钥。发送空字符串可清除。URL 必须使用 HTTPS。
请求体
| 名称 | 类型 | 必填 | 说明 |
|---|---|---|---|
webhook_url | string | 否 | 接收 webhook 事件的 HTTPS URL(空字符串表示清除) |
webhook_secret | string | 否 | 用于 HMAC-SHA256 签名的共享密钥(首次设置时自动生成) |
至少需要一个字段。
请求示例
curl -s -X PATCH https://api.smscode.gg/v2/webhook \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"webhook_url":"https://example.com/webhook"}'const res = await fetch("https://api.smscode.gg/v2/webhook", {
method: "PATCH",
headers: {
Authorization: "Bearer YOUR_API_TOKEN",
"Content-Type": "application/json",
},
body: JSON.stringify({ webhook_url: "https://example.com/webhook" }),
});
const data = await res.json();import requests
res = requests.patch("https://api.smscode.gg/v2/webhook",
json={"webhook_url": "https://example.com/webhook"},
headers={"Authorization": "Bearer YOUR_API_TOKEN"})
data = res.json()响应示例
{
"success": true,
"data": {
"webhook_url": "https://example.com/webhook",
"webhook_secret": "a1b2c3d4e5f6..."
}
} 与 v1 相同——仅基础路径改变(/v1 → /v2)。
/webhook/test 向已配置的 webhook URL 发送测试事件。返回服务器响应的 HTTP 状态码。可用于在正式使用前验证端点是否正常工作。
参数
无
请求示例
curl -s -X POST https://api.smscode.gg/v2/webhook/test \
-H "Authorization: Bearer YOUR_API_TOKEN"const res = await fetch("https://api.smscode.gg/v2/webhook/test", {
method: "POST",
headers: { Authorization: "Bearer YOUR_API_TOKEN" },
});
const data = await res.json();import requests
res = requests.post("https://api.smscode.gg/v2/webhook/test",
headers={"Authorization": "Bearer YOUR_API_TOKEN"})
data = res.json()响应示例
{
"success": true,
"data": {
"status_code": 200
}
} 与 v1 相同——仅基础路径改变(/v1 → /v2)。
⟩Webhook 通知
配置 webhook URL 以接收订单事件的实时推送通知,无需轮询。这是机器人脚本的推荐方式。
事件列表
| 事件 | 触发条件 |
|---|---|
order.otp_received | OTP 验证码已送达租用号码 |
order.completed | 订单已标记为完成(手动或到期) |
order.expired | 订单过期且未收到 OTP(余额已退还) |
order.canceled | 订单被用户取消(余额已退还) |
Payload
{
"event": "order.otp_received",
"timestamp": "2026-02-25T12:00:00+00:00",
"data": {
"order_id": 1001,
"phone_number": "+628123456789",
"otp_code": "1234",
"otp_message": "Your verification code is 1234",
"product_id": 42,
"catalog_product_id": 87,
"country": "Indonesia",
"platform": "WhatsApp"
}
} 签名验证
每个 webhook 请求都包含 X-Webhook-Signature 请求头,其中包含使用您的 webhook_secret 作为密钥对请求体计算的 HMAC-SHA256 签名:
在您的服务器上验证此签名以确保请求的真实性。发送采用即发即忘模式,3 秒超时且不重试。
⟩速率限制
API 请求按端点分组进行速率限制。超出限制将返回 429 Too Many Requests,并附带 Retry-After 响应头,指示需要等待的秒数。
| 端点分组 | 限制 | 时间窗口 |
|---|---|---|
| 目录(国家、服务、产品、汇率) | 5,000 次请求 | 60 秒 |
| 余额 | 600 次请求 | 60 秒 |
| 订单查询(列表、详情、活跃) | 5,000 次请求 | 60 秒 |
| 创建订单 | 3,000 次请求 | 60 秒 |
| 取消订单 | 1,000 次请求 | 60 秒 |
| 订单操作(完成、重发) | 1,000 次请求 | 60 秒 |
| Webhook 配置(查询、更新) | 600 次请求 | 60 秒 |
| Webhook 测试 | 10 次请求 | 60 秒 |
⟩错误代码
错误响应在 error.code 中包含以下代码之一:
| 代码 | HTTP | 说明 |
|---|---|---|
UNAUTHORIZED | 401 | 缺少或无效的 API 令牌 |
FORBIDDEN | 403 | 访问被拒绝 |
NOT_FOUND | 404 | 资源未找到(订单、汇率等) |
CONFLICT | 409 | 重复请求或资源冲突 |
INSUFFICIENT_BALANCE | 409 | 余额不足,无法创建订单 |
VALIDATION_ERROR | 422 | 请求参数验证失败 |
RATE_LIMIT_EXCEEDED | 429 | 请求过于频繁(请查看 Retry-After 响应头) |
INTERNAL_ERROR | 500 | 服务器内部错误 |
PROVIDER_ERROR | 422 | 上游 SMS 提供商拒绝了请求。订单创建失败时,错误可能携带 <code>details</code>:<code>cause_counts</code>(旧版 <code>product_id</code> 订单 — 按原因分类的统计)或 <code>attempts</code>(<code>catalog_product_id</code> 订单 — 每次尝试的结果),取值为 <code>ok</code>、<code>no_numbers</code>、<code>insufficient_balance</code>、<code>price_rejected</code>、<code>provider_unavailable</code>、<code>provider_account_balance</code>、<code>provider_error</code>。 |
NO_OFFER_AVAILABLE | 422 | 没有符合所请求产品与策略(价格上限、可用性)的有效报价。 |
CANCEL_TOO_EARLY | 409 | 订单创建时间过短,无法取消 — 请等待 2 分钟 |
REQUEST_IN_PROGRESS | 409 | 使用该幂等 key 的创建请求仍在处理中 |
IDEMPOTENCY_KEY_REUSED | 422 | 该幂等 key 已用于不同的请求体 |
SERVICE_UNAVAILABLE | 503 | 服务暂时不可用(维护中) |
FX_RATE_UNAVAILABLE | 503 | USD/IDR 汇率不可用(v2 金额类 endpoint)——返回 503 并带有 Retry-After 响应头。 |
⟩从 v1 迁移到 v2
v1 提供 IDR;v2 提供 USD。两个版本将永久共存——不会下线。每个集成只选择一个版本;请勿混用基础路径。除金额表示方式外,v2 与 v1 完全相同。
| 方面 | v1 · IDR | v2 · USD |
|---|---|---|
| 金额字段 | 整数 IDR,例如 15000 | 金额对象 { amount, currency, canonical_amount, canonical_currency } |
meta.fx | 无 | 每个涉及金额的响应均必需 |
| 货币 | IDR | USD(硬编码) |
FX_RATE_UNAVAILABLE | — | 无可用汇率时新增 503 + Retry-After |
| 精度 | — | 总额 2 位小数,价格/退款 4 位小数,正数向上取整 |
GET /catalog/exchange-rate | {pair, base_currency, quote_currency, rate};遵循 ?pair | {pair, rate, rate_as_of};忽略 ?pair(仅 USD/IDR) |
并排示例
{
"success": true,
"data": {
"currency": "IDR",
"balance": 500000
}
}{
"success": true,
"data": {
"balance": { "amount": "30.77", "currency": "USD", "canonical_amount": 500000, "canonical_currency": "IDR" }
},
"meta": { "fx": { "pair": "USD/IDR", "rate": 16250, "rate_as_of": "2026-05-27T08:00:00+00:00" } }
}{
"success": true,
"data": [
{
"id": 142,
"name": "WhatsApp Indonesia",
"country_id": 6,
"platform_id": 3,
"catalog_product_id": 87,
"available": 42,
"price": 15000,
"active": true
}
],
"meta": { "page": 1, "limit": 10, "count": 1 }
}{
"success": true,
"data": [
{
"id": 142,
"name": "WhatsApp Indonesia",
"country_id": 6,
"platform_id": 3,
"catalog_product_id": 87,
"available": 42,
"price": { "amount": "0.9231", "currency": "USD", "canonical_amount": 15000, "canonical_currency": "IDR" },
"active": true
}
],
"meta": { "page": 1, "limit": 10, "count": 1, "fx": { "pair": "USD/IDR", "rate": 16250, "rate_as_of": "2026-05-27T08:00:00+00:00" } }
}catalog_product_id 是稳定的产品标识(每个国家 + 平台对应一个),可以安全地存储在您的系统中,并且可直接用于下单:使用 catalog_product_id(加上可选的 max_price 与 prefer_provider)下单,服务器会为您选定实时报价。基于单个 product_id 的方式仍受支持且向后兼容 —— 该 id 是易变的,每当供应商的价格档位变动时都会改变,因此“下单前重新获取目录”的建议仅适用于该方式。
{
"success": true,
"data": {
"order_id": 1001,
"status": "CANCELED",
"refund_amount": 15000,
"new_balance": 515000
}
}{
"success": true,
"data": {
"order_id": 1001,
"status": "CANCELED",
"refund_amount": { "amount": "0.9231", "currency": "USD", "canonical_amount": 15000, "canonical_currency": "IDR" },
"new_balance": { "amount": "31.69", "currency": "USD", "canonical_amount": 515000, "canonical_currency": "IDR" }
},
"meta": { "fx": { "pair": "USD/IDR", "rate": 16250, "rate_as_of": "2026-05-27T08:00:00+00:00" } }
}迁移清单
- 将基础路径
/v1→/v2。 - 将金额字段解析为对象——把
amount读取为十进制字符串;currency为"USD"。 - 对账时请使用
canonical_amount(精确 IDR);USDamount是渲染时投影,且rate在meta.fx中披露一次。 - 处理新增的
FX_RATE_UNAVAILABLE(503)——在Retry-After后重试。v1 绝不会返回此情况。