Tài liệu API
Truy cập lập trình đến số ảo, đơn hàng và số dư tài khoản.
Tong Quan
Tất cả trường tiền tệ trên API /v1 đều bằng IDR (Rupiah Indonesia), dưới dạng số nguyên — ví dụ "price": 15000 và "balance": 500000 nghĩa là Rp 15.000 và Rp 500.000. Để có bản chiếu USD-native của cùng một sổ cái, hãy chuyển sang API v2 bằng nút chuyển phiên bản ở trên.
⟩Xác thực
Tất cả yêu cầu API đều yêu cầu Bearer token. Tạo token từ Cài đặt tài khoản trong bảng điều khiển, sau đó đính kèm vào mỗi yêu cầu:
Yêu cầu không có token hợp lệ sẽ nhận phản hồi 401 UNAUTHORIZED.
⟩URL cơ sở
Tất cả đường dẫn endpoint bên dưới tương đối với:
⟩Định dạng phản hồi
Mọi phản hồi trả về JSON với cấu trúc nhất quán. Tất cả phản hồi bao gồm header x-request-id để gỡ lỗi.
{
"success": true,
"data": { ... }
} {
"success": false,
"error": {
"code": "ERROR_CODE",
"message": "Human-readable message"
}
} Tất cả trường tiền tệ trên API /v1 đều bằng IDR (Rupiah Indonesia), dưới dạng số nguyên — ví dụ "price": 15000 và "balance": 500000 nghĩa là Rp 15.000 và Rp 500.000. Để có bản chiếu USD-native của cùng một sổ cái, hãy chuyển sang API v2 bằng nút chuyển phiên bản ở trên.
/catalog/countries Trả về danh sách tất cả quốc gia có sẵn.
Tham số
Không có
Ví dụ yêu cầu
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()Ví dụ phản hồi
{
"success": true,
"data": [
{
"id": 6,
"code": "ID",
"name": "Indonesia",
"dial_code": "+62",
"emoji": "🇮🇩",
"active": true
}
]
} /catalog/services Trả về danh sách dịch vụ (nền tảng) có sẵn. Có thể lọc theo quốc gia.
Tham số truy vấn
| Tên | Kiểu | Bắt buộc | Mô tả |
|---|---|---|---|
country_id | integer | Không | Lọc dịch vụ có sẵn cho quốc gia này |
Ví dụ yêu cầu
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()Ví dụ phản hồi
{
"success": true,
"data": [
{
"id": 3,
"code": "wa",
"name": "WhatsApp",
"active": true
}
]
} /catalog/products Trả về danh sách sản phẩm có sẵn (phân trang). Lọc theo quốc gia và/hoặc nền tảng.
Tham số truy vấn
| Tên | Kiểu | Bắt buộc | Mô tả |
|---|---|---|---|
country_id | integer | Không | Lọc theo ID quốc gia |
platform_id | integer | Không | Lọc theo ID nền tảng/dịch vụ |
sort | string | Không | Thứ tự sắp xếp: price_asc (mặc định), price_desc, available_asc, available_desc, name_asc, name_desc |
limit | integer | Không | Số kết quả mỗi trang (1-10.000, mặc định 1.000) |
page | integer | Không | Số trang (tối thiểu 1, mặc định 1) |
Ví dụ yêu cầu
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()Ví dụ phản hồi
{
"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 Trả về tỷ giá hối đoái USD/IDR hiện tại được dùng để chuyển đổi tiền tệ.
Tham số truy vấn
| Tên | Kiểu | Bắt buộc | Mô tả |
|---|---|---|---|
pair | string | Không | Cặp tiền tệ (mặc định: USD/IDR) |
Ví dụ yêu cầu
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()Ví dụ phản hồi
{
"success": true,
"data": {
"pair": "USD/IDR",
"base_currency": "USD",
"quote_currency": "IDR",
"rate": 16250
}
} /balance Trả về số dư tài khoản của người dùng đã xác thực.
Tham số
Không có
Ví dụ yêu cầu
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()Ví dụ phản hồi
{
"success": true,
"data": {
"currency": "IDR",
"balance": 500000
}
} /orders Trả về danh sách đơn hàng của người dùng đã xác thực, sắp xếp theo mới nhất. Hỗ trợ lọc theo trạng thái và phân trang qua offset.
Tham số truy vấn
| Tên | Kiểu | Bắt buộc | Mô tả |
|---|---|---|---|
limit | integer | Không | Số kết quả tối đa (1-100, mặc định 20) |
offset | integer | Không | Số kết quả bỏ qua (mặc định 0) |
status | string | Không | Lọc theo trạng thái: ACTIVE, OTP_RECEIVED, COMPLETED, CANCELED, EXPIRED (không phân biệt hoa thường) |
Ví dụ yêu cầu
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()Ví dụ phản hồi
{
"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} Trả về một đơn hàng theo ID. Chỉ trả về đơn hàng của người dùng đã xác thực.
Tham số đường dẫn
| Tên | Kiểu | Bắt buộc | Mô tả |
|---|---|---|---|
id | integer | Có | Mã đơn hàng (tham số đường dẫn) |
Ví dụ yêu cầu
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()Ví dụ phản hồi
{
"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 Liệt kê tất cả đơn hàng đang hoạt động (ACTIVE + OTP_RECEIVED). Sử dụng để theo dõi cập nhật trạng thái OTP.
Tham số
Không có
Ví dụ yêu cầu
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()Ví dụ phản hồi
{
"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 Tạo đơn hàng số ảo mới. Tự động trừ số dư. Hỗ trợ header Idempotency-Key tùy chọn để tránh tạo đơn trùng lặp khi thử lại do lỗi mạng.
Nội dung yêu cầu
| Tên | Kiểu | Bắt buộc | Mô tả |
|---|---|---|---|
product_id | integer | Không | ID sản phẩm cần đặt hàng. Chỉ cung cấp MỘT trong hai: product_id HOẶC catalog_product_id, không dùng cả hai. |
catalog_product_id | integer | Không | ID sản phẩm ổn định — máy chủ tự chọn ưu đãi tốt nhất hiện tại (rẻ nhất, có cân nhắc độ tin cậy). Cung cấp trường này hoặc product_id. |
max_price | integer · string | Không | Giới hạn giá tùy chọn. v1: số nguyên IDR. v2: chuỗi thập phân USD (vd. "0.50"). |
prefer_provider | string | Không | Mã nhà cung cấp tùy chọn được ưu tiên khi các ưu đãi ngang nhau. |
policy | string | Không | Chính sách định tuyến tùy chọn, chỉ hợp lệ khi dùng catalog_product_id. Giá trị: cheapest (mặc định) chọn ưu đãi tốt có giá thấp nhất; best_success xếp hạng các ưu đãi theo tỷ lệ giao thành công gần đây trước. best_success chấm điểm mỗi nhà cung cấp theo tỷ lệ đơn nhận được OTP trong 30 ngày trọn vẹn gần nhất, theo dải 10%, và chỉ tính một nhà cung cấp khi họ có ít nhất 20 đơn trong khoảng đó — nhà cung cấp dưới ngưỡng này hoặc chưa có lịch sử được coi là trung tính, nên các ưu đãi mới không bao giờ bị bỏ rơi (tùy chọn; tín hiệu khởi đầu ở mức trung tính). Khi prefer_provider cũng được đặt, nhà cung cấp ưu tiên vẫn được xếp trước. |
quantity | integer | Không | Số lượng (1-100, mặc định 1) |
Truyền header Idempotency-Key để thử lại an toàn mà không tạo đơn trùng lặp. Key có thể chứa chữ cái, chữ số, dấu gạch ngang và gạch dưới (A-Z a-z 0-9 _ -), tối đa 128 ký tự; key không hợp lệ sẽ bị từ chối với 422 VALIDATION_ERROR. Thử lại với cùng key và cùng body sẽ phát lại kết quả ban đầu (bao gồm failed_count khi thành công một phần). Lần thử lại đã đến nhà cung cấp nhưng thất bại sẽ được ghi lại và phát lại đúng lỗi đó — hãy dùng key MỚI để thử lại. Lỗi không có tác dụng phụ (không đủ số dư, không có ưu đãi khả dụng) sẽ giải phóng key, nên bạn có thể nạp tiền và thử lại với cùng key. Dùng lại một key với body khác sẽ trả về 422 IDEMPOTENCY_KEY_REUSED, và yêu cầu vẫn đang xử lý với key đó sẽ trả về 409 REQUEST_IN_PROGRESS. Trường failed_reason trong phản hồi create luôn là null — nó chỉ được điền khi poll/liệt kê đơn hàng.
Ví dụ yêu cầu
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()Ví dụ phản hồi
{
"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 Hủy đơn hàng đang hoạt động. Phí thuê sẽ được hoàn vào số dư tài khoản.
Nội dung yêu cầu
| Tên | Kiểu | Bắt buộc | Mô tả |
|---|---|---|---|
id | integer | Có | Mã đơn hàng cần hủy |
Ví dụ yêu cầu
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()Ví dụ phản hồi
{
"success": true,
"data": {
"order_id": 1001,
"status": "CANCELED",
"refund_amount": 15000,
"new_balance": 515000
}
} /orders/finish Đánh dấu đơn hàng đã hoàn thành sau khi nhận OTP. Giải phóng số ngay lập tức thay vì đợi hết hạn.
Nội dung yêu cầu
| Tên | Kiểu | Bắt buộc | Mô tả |
|---|---|---|---|
id | integer | Có | Mã đơn hàng cần hoàn thành |
Ví dụ yêu cầu
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()Ví dụ phản hồi
{
"success": true,
"data": {
"order_id": 1001,
"status": "COMPLETED"
}
} /orders/resend Yêu cầu nền tảng gửi lại SMS đến số đã thuê. Không phải tất cả nền tảng đều hỗ trợ gửi lại — kiểm tra trường resent trong phản hồi.
Nội dung yêu cầu
| Tên | Kiểu | Bắt buộc | Mô tả |
|---|---|---|---|
id | integer | Có | Mã đơn hàng cần gửi lại SMS |
Ví dụ yêu cầu
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()Ví dụ phản hồi
{
"success": true,
"data": {
"order_id": 1001,
"status": "ACTIVE",
"resent": true
}
} /webhook Trả về cấu hình webhook notification hiện tại của bạn.
Tham số
Không có
Ví dụ yêu cầu
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()Ví dụ phản hồi
{
"success": true,
"data": {
"webhook_url": "https://example.com/webhook",
"webhook_secret": "a1b2c3d4e5f6..."
}
} /webhook Cập nhật URL webhook và/hoặc secret. Secret được tự động tạo khi bạn đặt URL lần đầu. Gửi chuỗi rỗng để xóa. URL phải sử dụng HTTPS.
Nội dung yêu cầu
| Tên | Kiểu | Bắt buộc | Mô tả |
|---|---|---|---|
webhook_url | string | Không | URL HTTPS để nhận sự kiện webhook (chuỗi rỗng để xóa) |
webhook_secret | string | Không | Secret chia sẻ cho chữ ký HMAC-SHA256 (tự động tạo nếu bỏ qua lần đặt đầu tiên) |
Cần ít nhất một trường.
Ví dụ yêu cầu
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()Ví dụ phản hồi
{
"success": true,
"data": {
"webhook_url": "https://example.com/webhook",
"webhook_secret": "a1b2c3d4e5f6..."
}
} /webhook/test Gửi sự kiện thử nghiệm đến URL webhook đã cấu hình. Trả về mã trạng thái HTTP từ máy chủ của bạn. Hữu ích để kiểm tra endpoint hoạt động trước khi vận hành chính thức.
Tham số
Không có
Ví dụ yêu cầu
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()Ví dụ phản hồi
{
"success": true,
"data": {
"status_code": 200
}
} ⟩Webhook Notifications
Cấu hình URL webhook để nhận thông báo đẩy theo thời gian thực cho các sự kiện đơn hàng thay vì polling. Đây là phương pháp được khuyến nghị cho bot script.
Sự kiện
| Sự kiện | Kích hoạt |
|---|---|
order.otp_received | Mã OTP đã được gửi đến số thuê |
order.completed | Đơn hàng đã hoàn thành (thủ công hoặc khi hết hạn) |
order.expired | Đơn hàng hết hạn mà không có OTP (đã hoàn tiền) |
order.canceled | Đơn hàng bị hủy bởi người dùng (đã hoàn tiền) |
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"
}
} Xác minh chữ ký
Mỗi yêu cầu webhook bao gồm header X-Webhook-Signature chứa chữ ký HMAC-SHA256 của nội dung yêu cầu, sử dụng webhook_secret làm khóa:
Xác minh chữ ký này trên máy chủ để đảm bảo yêu cầu là xác thực. Gửi theo kiểu fire-and-forget với timeout 3 giây và không thử lại.
⟩Rate Limits
Yêu cầu API được giới hạn tốc độ theo nhóm endpoint. Vượt quá giới hạn trả về 429 Too Many Requests với header Retry-After cho biết số giây cần đợi.
| Nhóm endpoint | Giới hạn | Cửa sổ |
|---|---|---|
| Danh mục (quốc gia, dịch vụ, sản phẩm, tỷ giá) | 5.000 yêu cầu | 60 giây |
| Số dư | 600 yêu cầu | 60 giây |
| Đọc đơn hàng (danh sách, chi tiết, hoạt động) | 5.000 yêu cầu | 60 giây |
| Tạo đơn hàng | 3.000 yêu cầu | 60 giây |
| Hủy đơn hàng | 1.000 yêu cầu | 60 giây |
| Thao tác đơn hàng (hoàn thành, gửi lại) | 1.000 yêu cầu | 60 giây |
| Cấu hình webhook (xem, cập nhật) | 600 yêu cầu | 60 giây |
| Thử webhook | 10 yêu cầu | 60 giây |
⟩Mã lỗi
Phản hồi lỗi bao gồm một trong các mã sau trong error.code:
| Mã | HTTP | Mô tả |
|---|---|---|
UNAUTHORIZED | 401 | Thiếu hoặc API token không hợp lệ |
FORBIDDEN | 403 | Truy cập bị từ chối |
NOT_FOUND | 404 | Không tìm thấy tài nguyên (đơn hàng, tỷ giá, v.v.) |
CONFLICT | 409 | Yêu cầu trùng lặp hoặc xung đột tài nguyên |
INSUFFICIENT_BALANCE | 409 | Số dư không đủ để tạo đơn hàng |
VALIDATION_ERROR | 422 | Tham số yêu cầu không hợp lệ |
RATE_LIMIT_EXCEEDED | 429 | Quá nhiều yêu cầu (kiểm tra header Retry-After) |
INTERNAL_ERROR | 500 | Lỗi máy chủ nội bộ |
PROVIDER_ERROR | 422 | Nhà cung cấp SMS thượng nguồn từ chối yêu cầu. Khi tạo đơn thất bại, lỗi có thể kèm <code>details</code>: <code>cause_counts</code> (đơn dùng <code>product_id</code> cũ — bảng đếm nhóm theo nguyên nhân) hoặc <code>attempts</code> (đơn dùng <code>catalog_product_id</code> — kết quả từng lần thử), với các giá trị <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 | Không có ưu đãi đang hoạt động nào khớp với sản phẩm và chính sách được yêu cầu (giới hạn giá, tình trạng còn hàng). |
CANCEL_TOO_EARLY | 409 | Đơn hàng quá mới để hủy — hãy đợi 2 phút |
REQUEST_IN_PROGRESS | 409 | Một yêu cầu tạo đơn với idempotency key này vẫn đang được xử lý |
IDEMPOTENCY_KEY_REUSED | 422 | Idempotency key này đã được dùng với một body yêu cầu khác |
SERVICE_UNAVAILABLE | 503 | Dịch vụ tạm thời không khả dụng (bảo trì) |
Tong Quan
Tất cả trường tiền tệ trên API /v2 đều bằng USD, được trả về dưới dạng đối tượng tiền tệ — { "amount": "0.92", "currency": "USD", "canonical_amount": 15000, "canonical_currency": "IDR" }. amount là một chuỗi thập phân; canonical_amount là giá trị sổ cái IDR chính xác (dùng nó để đối soát). Tỷ giá USD/IDR rate được áp dụng được công bố một lần cho mỗi phản hồi trong meta.fx. v2 là bản chiếu USD tại thời điểm hiển thị trên cùng một sổ cái IDR như v1 — nó không bao giờ lưu trữ hay giao dịch USD.
⟩Xác thực
Tất cả yêu cầu API đều yêu cầu Bearer token. Tạo token từ Cài đặt tài khoản trong bảng điều khiển, sau đó đính kèm vào mỗi yêu cầu:
Yêu cầu không có token hợp lệ sẽ nhận phản hồi 401 UNAUTHORIZED.
⟩URL cơ sở
Tất cả đường dẫn endpoint bên dưới tương đối với:
⟩Định dạng phản hồi
Mọi phản hồi trả về JSON với cấu trúc nhất quán. Tất cả phản hồi bao gồm header x-request-id để gỡ lỗi.
{
"success": true,
"data": { ... }
} {
"success": false,
"error": {
"code": "ERROR_CODE",
"message": "Human-readable message"
}
} Lỗi v2: không có tỷ giá khả dụng (503)
{
"success": false,
"error": { "code": "FX_RATE_UNAVAILABLE", "message": "USD/IDR exchange rate is unavailable" }
} Tất cả trường tiền tệ trên API /v2 đều bằng USD, được trả về dưới dạng đối tượng tiền tệ — { "amount": "0.92", "currency": "USD", "canonical_amount": 15000, "canonical_currency": "IDR" }. amount là một chuỗi thập phân; canonical_amount là giá trị sổ cái IDR chính xác (dùng nó để đối soát). Tỷ giá USD/IDR rate được áp dụng được công bố một lần cho mỗi phản hồi trong meta.fx. v2 là bản chiếu USD tại thời điểm hiển thị trên cùng một sổ cái IDR như v1 — nó không bao giờ lưu trữ hay giao dịch USD.
/catalog/countries Trả về danh sách tất cả quốc gia có sẵn.
Tham số
Không có
Ví dụ yêu cầu
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()Ví dụ phản hồi
{
"success": true,
"data": [
{
"id": 6,
"code": "ID",
"name": "Indonesia",
"dial_code": "+62",
"emoji": "🇮🇩",
"active": true
}
]
} Giống hệt v1 — chỉ đường dẫn gốc thay đổi (/v1 → /v2).
/catalog/services Trả về danh sách dịch vụ (nền tảng) có sẵn. Có thể lọc theo quốc gia.
Tham số truy vấn
| Tên | Kiểu | Bắt buộc | Mô tả |
|---|---|---|---|
country_id | integer | Không | Lọc dịch vụ có sẵn cho quốc gia này |
Ví dụ yêu cầu
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()Ví dụ phản hồi
{
"success": true,
"data": [
{
"id": 3,
"code": "wa",
"name": "WhatsApp",
"active": true
}
]
} Giống hệt v1 — chỉ đường dẫn gốc thay đổi (/v1 → /v2).
/catalog/products Trả về danh sách sản phẩm có sẵn (phân trang). Lọc theo quốc gia và/hoặc nền tảng.
Tham số truy vấn
| Tên | Kiểu | Bắt buộc | Mô tả |
|---|---|---|---|
country_id | integer | Không | Lọc theo ID quốc gia |
platform_id | integer | Không | Lọc theo ID nền tảng/dịch vụ |
sort | string | Không | Thứ tự sắp xếp: price_asc (mặc định), price_desc, available_asc, available_desc, name_asc, name_desc |
limit | integer | Không | Số kết quả mỗi trang (1-10.000, mặc định 1.000) |
page | integer | Không | Số trang (tối thiểu 1, mặc định 1) |
Ví dụ yêu cầu
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()Ví dụ phản hồi
{
"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: các trường tiền tệ là đối tượng tiền tệ USD và phản hồi mang theo một meta.fx { pair, rate, rate_as_of } duy nhất. rate là số IDR nguyên trên 1 USD, nên USD = canonical_amount / rate. Tổng dùng 2 chữ số thập phân; giá/hoàn tiền theo từng mục dùng 4. Một số tiền dương thực sự không bao giờ được làm tròn thành 0.00. rate_as_of là dấu thời gian RFC3339 của tỷ giá (dạng +00:00) hoặc null khi không có dấu thời gian được ghi lại.
Chỉ v2: nếu không có tỷ giá USD/IDR khả dụng, các endpoint tiền tệ trả về 503 FX_RATE_UNAVAILABLE kèm header Retry-After thay vì phần thân tiền tệ. v1 không bao giờ trả về điều này.
/catalog/exchange-rate Trả về tỷ giá hối đoái USD/IDR hiện tại được dùng để chuyển đổi tiền tệ.
Tham số
Không có — v2 luôn trả về USD/IDR; tham số ?pair của v1 bị bỏ qua.
Ví dụ yêu cầu
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()Ví dụ phản hồi
{
"success": true,
"data": { "pair": "USD/IDR", "rate": 16250, "rate_as_of": "2026-05-27T08:00:00+00:00" }
} v2: trả về { pair, rate, rate_as_of } (không có base_currency/quote_currency, không có lớp bọc meta — tỷ giá chính là dữ liệu). ?pair bị bỏ qua — v2 luôn trả về USD/IDR (v1 tôn trọng ?pair). Trả về 503 FX_RATE_UNAVAILABLE nếu không có tỷ giá khả dụng.
/balance Trả về số dư tài khoản của người dùng đã xác thực.
Tham số
Không có
Ví dụ yêu cầu
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()Ví dụ phản hồi
{
"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: các trường tiền tệ là đối tượng tiền tệ USD và phản hồi mang theo một meta.fx { pair, rate, rate_as_of } duy nhất. rate là số IDR nguyên trên 1 USD, nên USD = canonical_amount / rate. Tổng dùng 2 chữ số thập phân; giá/hoàn tiền theo từng mục dùng 4. Một số tiền dương thực sự không bao giờ được làm tròn thành 0.00. rate_as_of là dấu thời gian RFC3339 của tỷ giá (dạng +00:00) hoặc null khi không có dấu thời gian được ghi lại.
Chỉ v2: nếu không có tỷ giá USD/IDR khả dụng, các endpoint tiền tệ trả về 503 FX_RATE_UNAVAILABLE kèm header Retry-After thay vì phần thân tiền tệ. v1 không bao giờ trả về điều này.
/orders Trả về danh sách đơn hàng của người dùng đã xác thực, sắp xếp theo mới nhất. Hỗ trợ lọc theo trạng thái và phân trang qua offset.
Tham số truy vấn
| Tên | Kiểu | Bắt buộc | Mô tả |
|---|---|---|---|
limit | integer | Không | Số kết quả tối đa (1-100, mặc định 20) |
offset | integer | Không | Số kết quả bỏ qua (mặc định 0) |
status | string | Không | Lọc theo trạng thái: ACTIVE, OTP_RECEIVED, COMPLETED, CANCELED, EXPIRED (không phân biệt hoa thường) |
Ví dụ yêu cầu
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()Ví dụ phản hồi
{
"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: các trường tiền tệ là đối tượng tiền tệ USD và phản hồi mang theo một meta.fx { pair, rate, rate_as_of } duy nhất. rate là số IDR nguyên trên 1 USD, nên USD = canonical_amount / rate. Tổng dùng 2 chữ số thập phân; giá/hoàn tiền theo từng mục dùng 4. Một số tiền dương thực sự không bao giờ được làm tròn thành 0.00. rate_as_of là dấu thời gian RFC3339 của tỷ giá (dạng +00:00) hoặc null khi không có dấu thời gian được ghi lại.
Chỉ v2: nếu không có tỷ giá USD/IDR khả dụng, các endpoint tiền tệ trả về 503 FX_RATE_UNAVAILABLE kèm header Retry-After thay vì phần thân tiền tệ. v1 không bao giờ trả về điều này.
/orders/{id} Trả về một đơn hàng theo ID. Chỉ trả về đơn hàng của người dùng đã xác thực.
Tham số đường dẫn
| Tên | Kiểu | Bắt buộc | Mô tả |
|---|---|---|---|
id | integer | Có | Mã đơn hàng (tham số đường dẫn) |
Ví dụ yêu cầu
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()Ví dụ phản hồi
{
"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: các trường tiền tệ là đối tượng tiền tệ USD và phản hồi mang theo một meta.fx { pair, rate, rate_as_of } duy nhất. rate là số IDR nguyên trên 1 USD, nên USD = canonical_amount / rate. Tổng dùng 2 chữ số thập phân; giá/hoàn tiền theo từng mục dùng 4. Một số tiền dương thực sự không bao giờ được làm tròn thành 0.00. rate_as_of là dấu thời gian RFC3339 của tỷ giá (dạng +00:00) hoặc null khi không có dấu thời gian được ghi lại.
Chỉ v2: nếu không có tỷ giá USD/IDR khả dụng, các endpoint tiền tệ trả về 503 FX_RATE_UNAVAILABLE kèm header Retry-After thay vì phần thân tiền tệ. v1 không bao giờ trả về điều này.
/orders/active Liệt kê tất cả đơn hàng đang hoạt động (ACTIVE + OTP_RECEIVED). Sử dụng để theo dõi cập nhật trạng thái OTP.
Tham số
Không có
Ví dụ yêu cầu
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()Ví dụ phản hồi
{
"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 này không mang tiền tệ — nó không trả về amount cũng không trả về meta.fx (cùng cấu trúc như v1, dưới /v2).
/orders/create Tạo đơn hàng số ảo mới. Tự động trừ số dư. Hỗ trợ header Idempotency-Key tùy chọn để tránh tạo đơn trùng lặp khi thử lại do lỗi mạng.
Nội dung yêu cầu
| Tên | Kiểu | Bắt buộc | Mô tả |
|---|---|---|---|
product_id | integer | Không | ID sản phẩm cần đặt hàng. Chỉ cung cấp MỘT trong hai: product_id HOẶC catalog_product_id, không dùng cả hai. |
catalog_product_id | integer | Không | ID sản phẩm ổn định — máy chủ tự chọn ưu đãi tốt nhất hiện tại (rẻ nhất, có cân nhắc độ tin cậy). Cung cấp trường này hoặc product_id. |
max_price | integer · string | Không | Giới hạn giá tùy chọn. v1: số nguyên IDR. v2: chuỗi thập phân USD (vd. "0.50"). |
prefer_provider | string | Không | Mã nhà cung cấp tùy chọn được ưu tiên khi các ưu đãi ngang nhau. |
policy | string | Không | Chính sách định tuyến tùy chọn, chỉ hợp lệ khi dùng catalog_product_id. Giá trị: cheapest (mặc định) chọn ưu đãi tốt có giá thấp nhất; best_success xếp hạng các ưu đãi theo tỷ lệ giao thành công gần đây trước. best_success chấm điểm mỗi nhà cung cấp theo tỷ lệ đơn nhận được OTP trong 30 ngày trọn vẹn gần nhất, theo dải 10%, và chỉ tính một nhà cung cấp khi họ có ít nhất 20 đơn trong khoảng đó — nhà cung cấp dưới ngưỡng này hoặc chưa có lịch sử được coi là trung tính, nên các ưu đãi mới không bao giờ bị bỏ rơi (tùy chọn; tín hiệu khởi đầu ở mức trung tính). Khi prefer_provider cũng được đặt, nhà cung cấp ưu tiên vẫn được xếp trước. |
quantity | integer | Không | Số lượng (1-100, mặc định 1) |
Truyền header Idempotency-Key để thử lại an toàn mà không tạo đơn trùng lặp. Key có thể chứa chữ cái, chữ số, dấu gạch ngang và gạch dưới (A-Z a-z 0-9 _ -), tối đa 128 ký tự; key không hợp lệ sẽ bị từ chối với 422 VALIDATION_ERROR. Thử lại với cùng key và cùng body sẽ phát lại kết quả ban đầu (bao gồm failed_count khi thành công một phần). Lần thử lại đã đến nhà cung cấp nhưng thất bại sẽ được ghi lại và phát lại đúng lỗi đó — hãy dùng key MỚI để thử lại. Lỗi không có tác dụng phụ (không đủ số dư, không có ưu đãi khả dụng) sẽ giải phóng key, nên bạn có thể nạp tiền và thử lại với cùng key. Dùng lại một key với body khác sẽ trả về 422 IDEMPOTENCY_KEY_REUSED, và yêu cầu vẫn đang xử lý với key đó sẽ trả về 409 REQUEST_IN_PROGRESS. Trường failed_reason trong phản hồi create luôn là null — nó chỉ được điền khi poll/liệt kê đơn hàng.
Ví dụ yêu cầu
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()Ví dụ phản hồi
{
"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: các trường tiền tệ là đối tượng tiền tệ USD và phản hồi mang theo một meta.fx { pair, rate, rate_as_of } duy nhất. rate là số IDR nguyên trên 1 USD, nên USD = canonical_amount / rate. Tổng dùng 2 chữ số thập phân; giá/hoàn tiền theo từng mục dùng 4. Một số tiền dương thực sự không bao giờ được làm tròn thành 0.00. rate_as_of là dấu thời gian RFC3339 của tỷ giá (dạng +00:00) hoặc null khi không có dấu thời gian được ghi lại.
Chỉ v2: nếu không có tỷ giá USD/IDR khả dụng, các endpoint tiền tệ trả về 503 FX_RATE_UNAVAILABLE kèm header Retry-After thay vì phần thân tiền tệ. v1 không bao giờ trả về điều này.
/orders/cancel Hủy đơn hàng đang hoạt động. Phí thuê sẽ được hoàn vào số dư tài khoản.
Nội dung yêu cầu
| Tên | Kiểu | Bắt buộc | Mô tả |
|---|---|---|---|
id | integer | Có | Mã đơn hàng cần hủy |
Ví dụ yêu cầu
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()Ví dụ phản hồi
{
"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: các trường tiền tệ là đối tượng tiền tệ USD và phản hồi mang theo một meta.fx { pair, rate, rate_as_of } duy nhất. rate là số IDR nguyên trên 1 USD, nên USD = canonical_amount / rate. Tổng dùng 2 chữ số thập phân; giá/hoàn tiền theo từng mục dùng 4. Một số tiền dương thực sự không bao giờ được làm tròn thành 0.00. rate_as_of là dấu thời gian RFC3339 của tỷ giá (dạng +00:00) hoặc null khi không có dấu thời gian được ghi lại.
Chỉ v2: nếu không có tỷ giá USD/IDR khả dụng, các endpoint tiền tệ trả về 503 FX_RATE_UNAVAILABLE kèm header Retry-After thay vì phần thân tiền tệ. v1 không bao giờ trả về điều này.
/orders/finish Đánh dấu đơn hàng đã hoàn thành sau khi nhận OTP. Giải phóng số ngay lập tức thay vì đợi hết hạn.
Nội dung yêu cầu
| Tên | Kiểu | Bắt buộc | Mô tả |
|---|---|---|---|
id | integer | Có | Mã đơn hàng cần hoàn thành |
Ví dụ yêu cầu
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()Ví dụ phản hồi
{
"success": true,
"data": {
"order_id": 1001,
"status": "COMPLETED"
}
} Giống hệt v1 — chỉ đường dẫn gốc thay đổi (/v1 → /v2).
/orders/resend Yêu cầu nền tảng gửi lại SMS đến số đã thuê. Không phải tất cả nền tảng đều hỗ trợ gửi lại — kiểm tra trường resent trong phản hồi.
Nội dung yêu cầu
| Tên | Kiểu | Bắt buộc | Mô tả |
|---|---|---|---|
id | integer | Có | Mã đơn hàng cần gửi lại SMS |
Ví dụ yêu cầu
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()Ví dụ phản hồi
{
"success": true,
"data": {
"order_id": 1001,
"status": "ACTIVE",
"resent": true
}
} Giống hệt v1 — chỉ đường dẫn gốc thay đổi (/v1 → /v2).
/webhook Trả về cấu hình webhook notification hiện tại của bạn.
Tham số
Không có
Ví dụ yêu cầu
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()Ví dụ phản hồi
{
"success": true,
"data": {
"webhook_url": "https://example.com/webhook",
"webhook_secret": "a1b2c3d4e5f6..."
}
} Giống hệt v1 — chỉ đường dẫn gốc thay đổi (/v1 → /v2).
/webhook Cập nhật URL webhook và/hoặc secret. Secret được tự động tạo khi bạn đặt URL lần đầu. Gửi chuỗi rỗng để xóa. URL phải sử dụng HTTPS.
Nội dung yêu cầu
| Tên | Kiểu | Bắt buộc | Mô tả |
|---|---|---|---|
webhook_url | string | Không | URL HTTPS để nhận sự kiện webhook (chuỗi rỗng để xóa) |
webhook_secret | string | Không | Secret chia sẻ cho chữ ký HMAC-SHA256 (tự động tạo nếu bỏ qua lần đặt đầu tiên) |
Cần ít nhất một trường.
Ví dụ yêu cầu
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()Ví dụ phản hồi
{
"success": true,
"data": {
"webhook_url": "https://example.com/webhook",
"webhook_secret": "a1b2c3d4e5f6..."
}
} Giống hệt v1 — chỉ đường dẫn gốc thay đổi (/v1 → /v2).
/webhook/test Gửi sự kiện thử nghiệm đến URL webhook đã cấu hình. Trả về mã trạng thái HTTP từ máy chủ của bạn. Hữu ích để kiểm tra endpoint hoạt động trước khi vận hành chính thức.
Tham số
Không có
Ví dụ yêu cầu
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()Ví dụ phản hồi
{
"success": true,
"data": {
"status_code": 200
}
} Giống hệt v1 — chỉ đường dẫn gốc thay đổi (/v1 → /v2).
⟩Webhook Notifications
Cấu hình URL webhook để nhận thông báo đẩy theo thời gian thực cho các sự kiện đơn hàng thay vì polling. Đây là phương pháp được khuyến nghị cho bot script.
Sự kiện
| Sự kiện | Kích hoạt |
|---|---|
order.otp_received | Mã OTP đã được gửi đến số thuê |
order.completed | Đơn hàng đã hoàn thành (thủ công hoặc khi hết hạn) |
order.expired | Đơn hàng hết hạn mà không có OTP (đã hoàn tiền) |
order.canceled | Đơn hàng bị hủy bởi người dùng (đã hoàn tiền) |
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"
}
} Xác minh chữ ký
Mỗi yêu cầu webhook bao gồm header X-Webhook-Signature chứa chữ ký HMAC-SHA256 của nội dung yêu cầu, sử dụng webhook_secret làm khóa:
Xác minh chữ ký này trên máy chủ để đảm bảo yêu cầu là xác thực. Gửi theo kiểu fire-and-forget với timeout 3 giây và không thử lại.
⟩Rate Limits
Yêu cầu API được giới hạn tốc độ theo nhóm endpoint. Vượt quá giới hạn trả về 429 Too Many Requests với header Retry-After cho biết số giây cần đợi.
| Nhóm endpoint | Giới hạn | Cửa sổ |
|---|---|---|
| Danh mục (quốc gia, dịch vụ, sản phẩm, tỷ giá) | 5.000 yêu cầu | 60 giây |
| Số dư | 600 yêu cầu | 60 giây |
| Đọc đơn hàng (danh sách, chi tiết, hoạt động) | 5.000 yêu cầu | 60 giây |
| Tạo đơn hàng | 3.000 yêu cầu | 60 giây |
| Hủy đơn hàng | 1.000 yêu cầu | 60 giây |
| Thao tác đơn hàng (hoàn thành, gửi lại) | 1.000 yêu cầu | 60 giây |
| Cấu hình webhook (xem, cập nhật) | 600 yêu cầu | 60 giây |
| Thử webhook | 10 yêu cầu | 60 giây |
⟩Mã lỗi
Phản hồi lỗi bao gồm một trong các mã sau trong error.code:
| Mã | HTTP | Mô tả |
|---|---|---|
UNAUTHORIZED | 401 | Thiếu hoặc API token không hợp lệ |
FORBIDDEN | 403 | Truy cập bị từ chối |
NOT_FOUND | 404 | Không tìm thấy tài nguyên (đơn hàng, tỷ giá, v.v.) |
CONFLICT | 409 | Yêu cầu trùng lặp hoặc xung đột tài nguyên |
INSUFFICIENT_BALANCE | 409 | Số dư không đủ để tạo đơn hàng |
VALIDATION_ERROR | 422 | Tham số yêu cầu không hợp lệ |
RATE_LIMIT_EXCEEDED | 429 | Quá nhiều yêu cầu (kiểm tra header Retry-After) |
INTERNAL_ERROR | 500 | Lỗi máy chủ nội bộ |
PROVIDER_ERROR | 422 | Nhà cung cấp SMS thượng nguồn từ chối yêu cầu. Khi tạo đơn thất bại, lỗi có thể kèm <code>details</code>: <code>cause_counts</code> (đơn dùng <code>product_id</code> cũ — bảng đếm nhóm theo nguyên nhân) hoặc <code>attempts</code> (đơn dùng <code>catalog_product_id</code> — kết quả từng lần thử), với các giá trị <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 | Không có ưu đãi đang hoạt động nào khớp với sản phẩm và chính sách được yêu cầu (giới hạn giá, tình trạng còn hàng). |
CANCEL_TOO_EARLY | 409 | Đơn hàng quá mới để hủy — hãy đợi 2 phút |
REQUEST_IN_PROGRESS | 409 | Một yêu cầu tạo đơn với idempotency key này vẫn đang được xử lý |
IDEMPOTENCY_KEY_REUSED | 422 | Idempotency key này đã được dùng với một body yêu cầu khác |
SERVICE_UNAVAILABLE | 503 | Dịch vụ tạm thời không khả dụng (bảo trì) |
FX_RATE_UNAVAILABLE | 503 | Tỷ giá hối đoái USD/IDR không khả dụng (các endpoint tiền tệ v2) — trả về 503 kèm header Retry-After. |
⟩Chuyển từ v1 sang v2
v1 phục vụ IDR; v2 phục vụ USD. Cả hai phiên bản cùng tồn tại vĩnh viễn — không có việc ngừng hỗ trợ. Chọn một phiên bản cho mỗi tích hợp; đừng trộn lẫn đường dẫn gốc. v2 giống hệt v1 ngoại trừ cách biểu diễn tiền tệ.
| Khía cạnh | v1 · IDR | v2 · USD |
|---|---|---|
| Trường tiền tệ | IDR nguyên, vd. 15000 | Đối tượng tiền tệ { amount, currency, canonical_amount, canonical_currency } |
meta.fx | Không có | Bắt buộc trên mọi phản hồi mang tiền tệ |
| Tiền tệ | IDR | USD (cố định trong mã) |
FX_RATE_UNAVAILABLE | — | 503 + Retry-After mới khi không có tỷ giá khả dụng |
| Độ chính xác | — | Tổng 2 chữ số, giá/hoàn tiền 4 chữ số, làm tròn lên với số dương |
GET /catalog/exchange-rate | {pair, base_currency, quote_currency, rate}; tôn trọng ?pair | {pair, rate, rate_as_of}; ?pair bị bỏ qua (chỉ USD/IDR) |
Ví dụ song song
{
"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 là định danh sản phẩm ỔN ĐỊNH (mỗi quốc gia + nền tảng có một) — an toàn để lưu trong hệ thống của bạn VÀ có thể đặt hàng trực tiếp: đặt hàng bằng catalog_product_id (kèm max_price và prefer_provider tùy chọn) và máy chủ sẽ chọn ưu đãi đang hoạt động cho bạn. Đường dẫn qua product_id theo từng ưu đãi vẫn được hỗ trợ và tương thích ngược — id đó không ổn định và thay đổi mỗi khi bậc giá của nhà cung cấp dịch chuyển, nên khuyến nghị tải lại danh mục trước khi đặt hàng chỉ áp dụng cho đường dẫn này.
{
"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" } }
}Danh sách kiểm tra khi chuyển đổi
- Đổi đường dẫn gốc
/v1→/v2. - Phân tích các trường tiền tệ dưới dạng đối tượng — đọc
amountnhư một chuỗi thập phân;currencylà"USD". - Để đối soát sổ cái, dùng
canonical_amount(IDR chính xác); số tiền USDamountlà bản chiếu tại thời điểm hiển thị vàrateđược công bố một lần trongmeta.fx. - Xử lý
FX_RATE_UNAVAILABLE(503) mới — thử lại sauRetry-After. v1 không bao giờ trả về điều này.