เอกสาร API

การเข้าถึงแบบโปรแกรมสำหรับเบอร์เสมือน คำสั่งซื้อ และยอดเงินในบัญชี

แนะนำ

เริ่มด้วย SDK อย่างเป็นทางการ

สำหรับการเชื่อมต่อใหม่ ให้ใช้ TypeScript/JavaScript หรือ Python SDK ทั้งสอง SDK ใช้ API สาธารณะ /v2 เป็นค่าเริ่มต้น เก็บ idempotency key ไว้เมื่อ retry อย่างปลอดภัย แสดง typed errors และทำให้ lifecycle ของ OTP สอดคล้องกัน

01

สร้างอย่างปลอดภัย

สร้างคำสั่งซื้อด้วย product_id สำหรับสล็อตระดับราคาที่เฉพาะเจาะจงและเสถียร หรือใช้ catalog_product_id พร้อม operator_id แบบไม่บังคับ, min_price/max_price และคีย์ idempotency สำหรับคำขอแบบเสียเงินที่มีการ route และ retry ได้อย่างปลอดภัย

catalog_product_idmax_priceIdempotency-Key
import { SmscodeClient, OtpTimeoutError } from "@smscode/sdk";

const client = new SmscodeClient({ token: process.env.SMSCODE_TOKEN! });

let orderId: number | undefined;

try {
  const created = await client.orders.create({
    catalog_product_id: Number(process.env.SMSCODE_CATALOG_PRODUCT_ID),
    max_price: "0.50",
    quantity: 1,
  });

  const order = created.orders[0]!;
  orderId = order.id;
  const first = await client.orders.waitForOtp(orderId, { timeoutMs: 120_000 });

  console.log(first.otpCode); // ส่งรหัสนี้ในแอปเป้าหมาย
  await client.orders.finish(orderId);
} catch (err) {
  if (err instanceof OtpTimeoutError && orderId !== undefined) {
    const current = await client.orders.get(orderId);
    if (current.can_cancel) await client.orders.cancel(orderId);
  }
  throw err;
}
import os

from smscode import OtpTimeoutError, SmscodeClient

with SmscodeClient(token=os.environ["SMSCODE_TOKEN"]) as client:
    created = client.orders.create(
        catalog_product_id=int(os.environ["SMSCODE_CATALOG_PRODUCT_ID"]),
        max_price="0.50",
        quantity=1,
    )

    order = created.orders[0]
    order_id = int(order["id"])

    try:
        first = client.orders.wait_for_otp(order_id, timeout_ms=120_000)
        print(first.otp_code)  # ส่งรหัสนี้ในแอปเป้าหมาย
        client.orders.finish(order_id)
    except OtpTimeoutError:
        current = client.orders.get(order_id)
        if current["can_cancel"]:
            client.orders.cancel(order_id)
        raise

ใช้ can_resend และ resend_available_at สำหรับ timing ของ resend ค่า timestamp ระดับล่างของ resend เป็น internal และไม่ใช่ field response สาธารณะ

ภาพรวม

ฟิลด์เงินทั้งหมดบน API /v1 เป็น IDR (รูเปียห์อินโดนีเซีย) ในรูปแบบจำนวนเต็ม — ตัวอย่างเช่น "price": 15000 และ "balance": 500000 หมายถึง Rp 15,000 และ Rp 500,000 สำหรับการฉายภาพแบบ USD-native ของบัญชีแยกประเภทเดียวกัน ให้สลับไปใช้ API v2 ด้วยปุ่มสลับเวอร์ชันด้านบน

การยืนยันตัวตน

คำขอ API ทั้งหมดต้องใช้ Bearer token สร้าง token จากตั้งค่าบัญชีในแดชบอร์ด แล้วใส่ในทุกคำขอ:

Authorization:Bearer YOUR_API_TOKEN

คำขอที่ไม่มี token ที่ถูกต้องจะได้รับการตอบกลับ 401 UNAUTHORIZED

URL ฐาน

เส้นทาง endpoint ทั้งหมดด้านล่างเป็น relative กับ:

https://api.smscode.gg/v1

รูปแบบการตอบกลับ

ทุกการตอบกลับเป็น JSON ในรูปแบบ envelope ที่สอดคล้องกัน ทุกการตอบกลับมี header x-request-id สำหรับการดีบัก

สำเร็จ
{
  "success": true,
  "data": { ... }
}
ข้อผิดพลาด
{
  "success": false,
  "error": {
    "code": "ERROR_CODE",
    "message": "Human-readable message"
  }
}

ฟิลด์เงินทั้งหมดบน API /v1 เป็น IDR (รูเปียห์อินโดนีเซีย) ในรูปแบบจำนวนเต็ม — ตัวอย่างเช่น "price": 15000 และ "balance": 500000 หมายถึง Rp 15,000 และ Rp 500,000 สำหรับการฉายภาพแบบ USD-native ของบัญชีแยกประเภทเดียวกัน ให้สลับไปใช้ API v2 ด้วยปุ่มสลับเวอร์ชันด้านบน

GET/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()

ตัวอย่างการตอบกลับ

200 OK
{
  "success": true,
  "data": [
    {
      "id": 6,
      "code": "ID",
      "name": "Indonesia",
      "dial_code": "+62",
      "emoji": "🇮🇩",
      "active": true
    }
  ]
}
GET/catalog/services

ส่งคืนรายการบริการ (แพลตฟอร์ม) ที่มี สามารถกรองตามประเทศได้

พารามิเตอร์คิวรี

ชื่อประเภทจำเป็นรายละเอียด
country_idintegerไม่กรองบริการที่มีสำหรับประเทศนี้

ตัวอย่างคำขอ

curl -s "https://api.smscode.gg/v1/catalog/services?country_id=7" \
  -H "Authorization: Bearer YOUR_API_TOKEN"
const res = await fetch("https://api.smscode.gg/v1/catalog/services?country_id=7", {
  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": 7},
    headers={"Authorization": "Bearer YOUR_API_TOKEN"})
data = res.json()

ตัวอย่างการตอบกลับ

200 OK
{
  "success": true,
  "data": [
    {
      "id": 3,
      "code": "wa",
      "name": "WhatsApp",
      "active": true
    }
  ]
}
GET/catalog/operators

ส่งคืนผู้ให้บริการที่เลือกได้สำหรับประเทศ + บริการ หากมีทั้งผู้ให้บริการจริงและสต็อก Any response จะมีแถว Any ที่ operator_id เป็น null; หากไม่มีสินค้าเฉพาะผู้ให้บริการ รายการจะว่าง

พารามิเตอร์คิวรี

ชื่อประเภทจำเป็นรายละเอียด
country_idintegerใช่ID ประเทศ
platform_idintegerใช่ID แพลตฟอร์ม/บริการ

ตัวอย่างคำขอ

curl -s "https://api.smscode.gg/v1/catalog/operators?country_id=7&platform_id=3" \
  -H "Authorization: Bearer YOUR_API_TOKEN"
const params = new URLSearchParams({
  country_id: "7", platform_id: "3",
});
const res = await fetch(`https://api.smscode.gg/v1/catalog/operators?${params}`, {
  headers: { Authorization: "Bearer YOUR_API_TOKEN" },
});
const data = await res.json();
import requests

res = requests.get("https://api.smscode.gg/v1/catalog/operators",
    params={"country_id": 7, "platform_id": 3},
    headers={"Authorization": "Bearer YOUR_API_TOKEN"})
data = res.json()

ตัวอย่างการตอบกลับ

200 OK
{
  "success": true,
  "data": [
    {
      "operator_id": null,
      "code": "any",
      "name": "Any",
      "local_name": null
    },
    {
      "operator_id": 433,
      "code": "axis",
      "name": "AXIS (XL Axiata)",
      "local_name": "AXIS (XL Axiata)"
    }
  ]
}
GET/catalog/products

ส่งคืนรายการสินค้าที่พร้อมใช้งานแบบแบ่งหน้า กรองตามประเทศ แพลตฟอร์ม และผู้ให้บริการแบบไม่บังคับ

พารามิเตอร์คิวรี

ชื่อประเภทจำเป็นรายละเอียด
country_idintegerไม่กรองตามรหัสประเทศ
platform_idintegerไม่กรองตามรหัสแพลตฟอร์ม/บริการ
operator_idintegerไม่ID ผู้ให้บริการแบบไม่บังคับจาก /catalog/operators เว้นว่างสำหรับสินค้า Any
sortstringไม่การเรียงลำดับ: price_asc (ค่าเริ่มต้น), price_desc, available_asc, available_desc, name_asc, name_desc
limitintegerไม่จำนวนผลลัพธ์ต่อหน้า (1-10,000 ค่าเริ่มต้น 1,000)
pageintegerไม่หมายเลขหน้า (ขั้นต่ำ 1 ค่าเริ่มต้น 1)

ตัวอย่างคำขอ

curl -s "https://api.smscode.gg/v1/catalog/products?country_id=7&platform_id=3&limit=10&page=1" \
  -H "Authorization: Bearer YOUR_API_TOKEN"
const params = new URLSearchParams({
  country_id: "7", 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": 7, "platform_id": 3, "limit": 10, "page": 1},
    headers={"Authorization": "Bearer YOUR_API_TOKEN"})
data = res.json()

ตัวอย่างการตอบกลับ

200 OK
{
  "success": true,
  "data": [
    {
      "id": 142,
      "name": "WhatsApp Indonesia",
      "country_id": 7,
      "platform_id": 3,
      "catalog_product_id": 87,
      "operator_id": null,
      "operator_name": null,
      "available": 42,
      "price": 15000,
      "active": true
    }
  ],
  "meta": { "page": 1, "limit": 10, "count": 1 }
}
GET/catalog/exchange-rate

ส่งคืนอัตราแลกเปลี่ยน USD/IDR ปัจจุบันที่ใช้สำหรับการแปลงสกุลเงิน

พารามิเตอร์คิวรี

ชื่อประเภทจำเป็นรายละเอียด
pairstringไม่คู่สกุลเงิน (ค่าเริ่มต้น: 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()

ตัวอย่างการตอบกลับ

200 OK
{
  "success": true,
  "data": {
    "pair": "USD/IDR",
    "base_currency": "USD",
    "quote_currency": "IDR",
    "rate": 16250
  }
}
GET/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()

ตัวอย่างการตอบกลับ

200 OK
{
  "success": true,
  "data": {
    "currency": "IDR",
    "balance": 500000
  }
}
GET/orders

ส่งคืนรายการคำสั่งซื้อของผู้ใช้ที่ผ่านการยืนยันตัวตน เรียงตามล่าสุด รองรับการกรองตามสถานะและแบ่งหน้าด้วย offset

พารามิเตอร์คิวรี

ชื่อประเภทจำเป็นรายละเอียด
limitintegerไม่จำนวนผลลัพธ์สูงสุด (1-100 ค่าเริ่มต้น 20)
offsetintegerไม่จำนวนผลลัพธ์ที่ข้าม (ค่าเริ่มต้น 0)
statusstringไม่กรองตามสถานะ: 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()

ตัวอย่างการตอบกลับ

200 OK
{
  "success": true,
  "data": [
    {
      "id": 1001,
      "status": "ACTIVE",
      "created_at": "2026-02-25T10:00:00+00:00",
      "product_id": 142,
      "catalog_product_id": 87,
      "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,
      "operator_id": null,
      "operator_name": null
    }
  ]
}
GET/orders/{id}

ส่งคืนคำสั่งซื้อเดี่ยวตามรหัส ส่งคืนเฉพาะคำสั่งซื้อที่เป็นของผู้ใช้ที่ผ่านการยืนยันตัวตน

พารามิเตอร์เส้นทาง

ชื่อประเภทจำเป็นรายละเอียด
idintegerใช่รหัสคำสั่งซื้อ (path parameter)

ตัวอย่างคำขอ

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()

ตัวอย่างการตอบกลับ

200 OK
{
  "success": true,
  "data": {
    "id": 1001,
    "status": "OTP_RECEIVED",
    "created_at": "2026-02-25T10:00:00+00:00",
    "product_id": 142,
    "catalog_product_id": 87,
    "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,
    "operator_id": null,
    "operator_name": null
  }
}
GET/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()

ตัวอย่างการตอบกลับ

200 OK
{
  "success": true,
  "data": [
    {
      "id": 1001,
      "status": "OTP_RECEIVED",
      "otp_code": null,
      "otp_message": "Confirm your login: https://example.com/confirm",
      "sms_revision": 1,
      "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,
      "sms_revision": 0,
      "otp_received_at": null,
      "expires_at": "2026-02-25T10:50:00+00:00",
      "failed_reason": null
    }
  ]
}
POST/orders/create

สร้างคำสั่งซื้อเบอร์เสมือนใหม่ หักยอดเงินอัตโนมัติ รองรับ header Idempotency-Key เพื่อป้องกันคำสั่งซื้อซ้ำเมื่อลองส่งคำขอใหม่

เนื้อหาคำขอ

ชื่อประเภทจำเป็นรายละเอียด
product_idintegerไม่ID สินค้าของสล็อตระดับราคาที่เฉพาะเจาะจงและเสถียรสำหรับสั่งซื้อโดยตรง ส่งค่านี้หรือ catalog_product_id อย่างใดอย่างหนึ่งเท่านั้น ห้ามส่งทั้งคู่
catalog_product_idintegerไม่ID umbrella สำหรับประเทศ+แพลตฟอร์มแบบ routed เซิร์ฟเวอร์จะเลือกระดับราคาปัจจุบันที่ตรงเงื่อนไข ส่งค่านี้หรือ product_id อย่างใดอย่างหนึ่ง
operator_idintegerไม่ID ผู้ให้บริการแบบไม่บังคับจาก /catalog/operators ใช้ได้เฉพาะกับ catalog_product_id; เว้นว่างสำหรับ Any
min_priceintegerไม่ราคาขั้นต่ำแบบไม่บังคับ จำนวนเต็ม IDR ใช้ได้เฉพาะกับ catalog_product_id
max_priceintegerไม่เพดานราคาแบบไม่บังคับ จำนวนเต็ม IDR ใช้ได้เฉพาะกับ catalog_product_id
prefer_providerstringไม่รหัสผู้ให้บริการแบบไม่บังคับ ใช้เป็นตัวตัดสินเมื่อข้อเสนอเท่ากัน
policystringไม่นโยบายการจัดเส้นทางแบบไม่บังคับ ใช้ได้เฉพาะกับ catalog_product_id เท่านั้น ค่าที่ใช้ได้: cheapest (ค่าเริ่มต้น) เลือกข้อเสนอที่พร้อมใช้งานและราคาต่ำสุด; best_success จัดอันดับข้อเสนอโดยให้ความสำเร็จในการส่งล่าสุดมาก่อน โดย best_success ให้คะแนนผู้ให้บริการแต่ละรายจากสัดส่วนคำสั่งซื้อที่ได้รับ OTP ในช่วง 30 วันเต็มล่าสุด แบ่งเป็นช่วงละ 10% และจะนับผู้ให้บริการรายนั้นก็ต่อเมื่อมีคำสั่งซื้ออย่างน้อย 20 รายการในช่วงเวลาดังกล่าว — ผู้ให้บริการที่ต่ำกว่าเกณฑ์นี้หรือยังไม่มีประวัติจะถือว่าเป็นกลาง จึงทำให้ข้อเสนอใหม่ไม่ถูกมองข้าม (ต้องเลือกใช้เอง; สัญญาณเริ่มต้นที่ค่ากลาง) หากตั้งค่า prefer_provider ไว้ด้วย ผู้ให้บริการที่เลือกไว้ก็ยังคงอยู่อันดับแรก
quantityintegerไม่จำนวน (1-100 ค่าเริ่มต้น 1)

ส่ง header Idempotency-Key เพื่อส่งคำขอซ้ำได้อย่างปลอดภัยโดยไม่สร้างคำสั่งซื้อซ้ำ key ประกอบด้วยตัวอักษร ตัวเลข ขีดกลาง และขีดล่าง (A-Z a-z 0-9 _ -) ได้ ยาวไม่เกิน 128 อักขระ key ที่ไม่ถูกต้องจะถูกปฏิเสธด้วย 422 VALIDATION_ERROR การส่งซ้ำด้วย key เดิมและ body เดิมจะเล่นผลลัพธ์เดิมซ้ำ (รวมถึง failed_count ของกรณีสำเร็จบางส่วน) การส่งซ้ำที่ไปถึงผู้ให้บริการแล้วแต่ล้มเหลวจะถูกบันทึกไว้และเล่นข้อผิดพลาดเดิมซ้ำ — ให้ใช้ key ใหม่เพื่อลองอีกครั้ง ความล้มเหลวที่ไม่มีผลข้างเคียง (ยอดเงินไม่พอ ไม่มีข้อเสนอที่ใช้ได้) จะปล่อย key คืน คุณจึงสามารถเติมเงินแล้วลองซ้ำด้วย key เดิมได้ การใช้ key เดิมซ้ำกับ body ที่ต่างออกไปจะคืนค่า 422 IDEMPOTENCY_KEY_REUSED และคำขอที่ยังทำงานอยู่ด้วย key นั้นจะคืนค่า 409 REQUEST_IN_PROGRESS ฟิลด์ failed_reason ในการตอบกลับของ create จะเป็น null เสมอ — จะมีค่าก็ต่อเมื่อ poll/list คำสั่งซื้อเท่านั้น

ตัวอย่างคำขอ

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 route by catalog_product_id — the server picks a current tier.
# product_id is the stable exact tier-slot id. catalog_product_id is the
# country+platform umbrella for routed ordering. Optional min_price/max_price
# bound the tier; operator_id scopes to a carrier from /catalog/operators.
# 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,"min_price":12000,"max_price":20000,"operator_id":433}'
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()

ตัวอย่างการตอบกลับ

200 OK
{
  "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,
        "operator_id": null,
        "operator_name": null
      }
    ],
    "failed_count": 0
  }
}
POST/orders/cancel

ยกเลิกคำสั่งซื้อที่กำลังดำเนินการ ค่าเช่าจะถูกคืนเข้ายอดบัญชีของคุณ

เนื้อหาคำขอ

ชื่อประเภทจำเป็นรายละเอียด
idintegerใช่รหัสคำสั่งซื้อที่จะยกเลิก

ตัวอย่างคำขอ

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()

ตัวอย่างการตอบกลับ

200 OK
{
  "success": true,
  "data": {
    "order_id": 1001,
    "status": "CANCELED",
    "refund_amount": 15000,
    "new_balance": 515000
  }
}
POST/orders/finish

ทำเครื่องหมายคำสั่งซื้อว่าเสร็จสิ้นหลังจากได้รับ OTP การดำเนินการนี้จะปล่อยเบอร์ทันทีแทนที่จะรอให้หมดอายุ

เนื้อหาคำขอ

ชื่อประเภทจำเป็นรายละเอียด
idintegerใช่รหัสคำสั่งซื้อที่จะเสร็จสิ้น

ตัวอย่างคำขอ

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()

ตัวอย่างการตอบกลับ

200 OK
{
  "success": true,
  "data": {
    "order_id": 1001,
    "status": "COMPLETED"
  }
}
POST/orders/resend

ขอให้แพลตฟอร์มส่ง SMS ซ้ำไปยังเบอร์ที่เช่า แพลตฟอร์มบางแห่งไม่รองรับการส่งซ้ำ — ตรวจสอบฟิลด์ resent ในการตอบกลับ

เนื้อหาคำขอ

ชื่อประเภทจำเป็นรายละเอียด
idintegerใช่รหัสคำสั่งซื้อที่จะส่ง SMS ซ้ำ

ตัวอย่างคำขอ

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()

ตัวอย่างการตอบกลับ

200 OK
{
  "success": true,
  "data": {
    "order_id": 1001,
    "status": "ACTIVE",
    "resent": true
  }
}
POST/orders/reactivate

เปิดใช้งานเบอร์ที่เสร็จสมบูรณ์อีกครั้ง — สั่งซื้อเบอร์เดิมซ้ำเพื่อรับรหัสยืนยันอีกครั้งโดยไม่ต้องเช่าเบอร์ใหม่ เฉพาะคำสั่งซื้อที่เสร็จสมบูรณ์ซึ่งเบอร์รองรับการเปิดใช้งานอีกครั้งเท่านั้นที่ทำได้ (ตรวจสอบ can_reactivate ที่คำสั่งซื้อ หรือดูตัวอย่างด้วย reactivate-options) คำสั่งซื้อลูกที่เปิดใช้งานใหม่เป็นคำสั่งซื้อใหม่ ซึ่งคืนค่าในรูปแบบเดียวกับ create ยอดเงินจะถูกหักโดยอัตโนมัติ

เนื้อหาคำขอ

ชื่อประเภทจำเป็นรายละเอียด
idintegerใช่คำสั่งซื้อที่เสร็จสมบูรณ์ที่จะเปิดใช้งานอีกครั้ง
max_priceintegerไม่เพดานค่าใช้จ่ายแบบไม่บังคับ จำนวนเต็ม IDR การเปิดใช้งานอีกครั้งจะถูกปฏิเสธด้วย 422 VALIDATION_ERROR หากค่าใช้จ่ายปัจจุบันเกินเพดานนี้

เช่นเดียวกับ create นี่คือการเปลี่ยนแปลงที่เกี่ยวกับเงิน — ให้ส่ง header Idempotency-Key เพื่อลองซ้ำได้อย่างปลอดภัย (create และ reactivate ไม่มีทางชนกันบน key เดียวกัน) การใช้ key เดิมซ้ำกับ body ที่ต่างออกไปจะคืนค่า 422 IDEMPOTENCY_KEY_REUSED และคำขอที่ยังดำเนินการอยู่ด้วย key นั้นจะคืนค่า 409 REQUEST_IN_PROGRESS เบอร์ที่ไม่สามารถเปิดใช้งานอีกครั้งได้จะคืนค่า 409 CONFLICT ยอดเงินที่น้อยเกินไปจะคืนค่า 409 INSUFFICIENT_BALANCE

ตัวอย่างคำขอ

curl -s -X POST https://api.smscode.gg/v1/orders/reactivate \
  -H "Authorization: Bearer YOUR_API_TOKEN" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: unique-request-id-901" \
  -d '{"id":1001,"max_price":20000}'
const res = await fetch("https://api.smscode.gg/v1/orders/reactivate", {
  method: "POST",
  headers: {
    Authorization: "Bearer YOUR_API_TOKEN",
    "Content-Type": "application/json",
    "Idempotency-Key": "unique-request-id-901",
  },
  body: JSON.stringify({ id: 1001, max_price: 20000 }),
});
const data = await res.json();
import requests

res = requests.post("https://api.smscode.gg/v1/orders/reactivate",
    json={"id": 1001, "max_price": 20000},
    headers={
        "Authorization": "Bearer YOUR_API_TOKEN",
        "Idempotency-Key": "unique-request-id-901",
    })
data = res.json()

ตัวอย่างการตอบกลับ

200 OK
{
  "success": true,
  "data": {
    "orders": [
      {
        "id": 1044,
        "status": "ACTIVE",
        "product_id": 142,
        "catalog_product_id": 87,
        "amount": 15000,
        "phone_number": "+6281234567890",
        "otp_code": null,
        "otp_received_at": null,
        "expires_at": "2026-02-25T11:20:00+00:00",
        "failed_reason": null,
        "operator_id": null,
        "operator_name": null
      }
    ],
    "failed_count": 0
  }
}
GET/orders/{id}/reactivate-options

ดูตัวอย่างว่าการเปิดใช้งานอีกครั้งจะคิดค่าใช้จ่ายเท่าไรในตอนนี้ อ่านอย่างเดียว — ไม่ใช้ Idempotency-Key และไม่สร้างสิ่งใด คืนค่าใช้จ่ายเป็นจำนวนเต็ม IDR ใช้ได้เฉพาะคำสั่งซื้อที่เสร็จสมบูรณ์ซึ่งเบอร์รองรับการเปิดใช้งานอีกครั้งเท่านั้น

พารามิเตอร์เส้นทาง

ชื่อประเภทจำเป็นรายละเอียด
idintegerใช่รหัสคำสั่งซื้อที่จะดูตัวอย่างค่าใช้จ่ายในการเปิดใช้งานอีกครั้ง (path parameter)

ตัวอย่างคำขอ

curl -s https://api.smscode.gg/v1/orders/1001/reactivate-options \
  -H "Authorization: Bearer YOUR_API_TOKEN"
const res = await fetch("https://api.smscode.gg/v1/orders/1001/reactivate-options", {
  headers: { Authorization: "Bearer YOUR_API_TOKEN" },
});
const data = await res.json();
import requests

res = requests.get("https://api.smscode.gg/v1/orders/1001/reactivate-options",
    headers={"Authorization": "Bearer YOUR_API_TOKEN"})
data = res.json()

ตัวอย่างการตอบกลับ

200 OK
{
  "success": true,
  "data": {
    "cost": 15000
  }
}
GET/webhook

ส่งคืนการตั้งค่า webhook notification ปัจจุบันของคุณ

พารามิเตอร์

ไม่มี

ตัวอย่างคำขอ

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()

ตัวอย่างการตอบกลับ

200 OK
{
  "success": true,
  "data": {
    "webhook_url": "https://example.com/webhook",
    "webhook_secret": "a1b2c3d4e5f6..."
  }
}
PATCH/webhook

อัปเดต URL และ/หรือ secret ของ webhook secret จะถูกสร้างอัตโนมัติเมื่อคุณตั้ง URL ครั้งแรก ส่งสตริงว่างเพื่อล้าง URL ต้องใช้ HTTPS

เนื้อหาคำขอ

ชื่อประเภทจำเป็นรายละเอียด
webhook_urlstringไม่HTTPS URL สำหรับรับเหตุการณ์ webhook (สตริงว่างเพื่อล้าง)
webhook_secretstringไม่Shared secret สำหรับ HMAC-SHA256 signature (สร้างอัตโนมัติหากไม่ระบุในครั้งแรก)

ต้องระบุอย่างน้อยหนึ่งฟิลด์

ตัวอย่างคำขอ

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()

ตัวอย่างการตอบกลับ

200 OK
{
  "success": true,
  "data": {
    "webhook_url": "https://example.com/webhook",
    "webhook_secret": "a1b2c3d4e5f6..."
  }
}
POST/webhook/test

ส่งเหตุการณ์ทดสอบไปยัง URL webhook ที่ตั้งค่าไว้ ส่งคืนรหัสสถานะ HTTP จากเซิร์ฟเวอร์ของคุณ มีประโยชน์สำหรับตรวจสอบว่า endpoint ทำงานก่อนเริ่มใช้งานจริง

พารามิเตอร์

ไม่มี

ตัวอย่างคำขอ

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()

ตัวอย่างการตอบกลับ

200 OK
{
  "success": true,
  "data": {
    "status_code": 200
  }
}

การแจ้งเตือน Webhook

ตั้งค่า URL webhook เพื่อรับการแจ้งเตือนแบบ push แบบเรียลไทม์สำหรับเหตุการณ์คำสั่งซื้อแทนการ polling นี่คือวิธีที่แนะนำสำหรับ bot script

เหตุการณ์

เหตุการณ์ทริกเกอร์
order.otp_receivedได้รับ SMS ใหม่ โดยรหัสที่ตรวจพบอาจเป็น null
order.completedคำสั่งซื้อถูกทำเครื่องหมายว่าเสร็จสิ้น (ด้วยตนเองหรือเมื่อหมดอายุ)
order.expiredคำสั่งซื้อหมดอายุก่อนรับ SMS (คืนเงินแล้ว)
order.canceledคำสั่งซื้อถูกยกเลิกโดยผู้ใช้ (คืนเงินแล้ว)

SMS ใหม่แต่ละข้อความจะส่ง event นี้ otp_code อาจเป็น null ขณะที่มี otp_message ได้ event SMS หลายรายการอาจมาถึงไม่ตามลำดับ ใช้ sms_revision เพื่อไม่รับคู่ข้อมูลรวมที่เก่ากว่า

เพย์โหลด

เนื้อหา Webhook POST
{
  "event": "order.otp_received",
  "timestamp": "2026-02-25T12:00:00+00:00",
  "data": {
    "order_id": 1001,
    "phone_number": "+628123456789",
    "otp_code": null,
    "otp_message": "Confirm your login: https://example.com/confirm",
    "sms_revision": 1,
    "product_id": 142,
    "catalog_product_id": 87,
    "country": "Indonesia",
    "platform": "WhatsApp"
  }
}

การตรวจสอบ Signature

ทุกคำขอ webhook มี header X-Webhook-Signature พร้อม HMAC-SHA256 signature ของ request body โดยใช้ webhook_secret ของคุณเป็น key:

X-Webhook-Signature:sha256={HMAC-SHA256(body, webhook_secret)}

ตรวจสอบ signature นี้บนเซิร์ฟเวอร์ของคุณเพื่อยืนยันว่าคำขอเป็นของจริง การส่งเป็นแบบ fire-and-forget พร้อม timeout 3 วินาทีและไม่มีการลองใหม่

ขีดจำกัดอัตรา

คำขอ API ถูกจำกัดอัตราตามกลุ่ม endpoint การเกินขีดจำกัดจะได้รับ 429 Too Many Requests พร้อม header Retry-After ที่ระบุจำนวนวินาทีที่ต้องรอ

กลุ่ม Endpointขีดจำกัดช่วงเวลา
แคตตาล็อก (ประเทศ บริการ ผลิตภัณฑ์ อัตราแลกเปลี่ยน)5,000 คำขอ60 วินาที
ยอดเงิน600 คำขอ60 วินาที
การอ่านคำสั่งซื้อ (รายการ, ดู, กำลังดำเนินการ)5,000 คำขอ60 วินาที
สร้างคำสั่งซื้อ3,000 คำขอ60 วินาที
ยกเลิกคำสั่งซื้อ1,000 คำขอ60 วินาที
การดำเนินการคำสั่งซื้อ (finish, resend)1,000 คำขอ60 วินาที
การตั้งค่า Webhook (ดู, อัปเดต)600 คำขอ60 วินาที
ทดสอบ Webhook10 คำขอ60 วินาที

รหัสข้อผิดพลาด

การตอบกลับข้อผิดพลาดจะมีรหัสเหล่านี้ใน error.code:

รหัสHTTPรายละเอียด
UNAUTHORIZED401ไม่มีหรือ API token ไม่ถูกต้อง
FORBIDDEN403การเข้าถึงถูกปฏิเสธ
NOT_FOUND404ไม่พบทรัพยากร (คำสั่งซื้อ อัตราแลกเปลี่ยน ฯลฯ)
CONFLICT409คำขอซ้ำหรือทรัพยากรขัดแย้ง
INSUFFICIENT_BALANCE409ยอดเงินไม่เพียงพอสำหรับสร้างคำสั่งซื้อ
VALIDATION_ERROR422พารามิเตอร์คำขอไม่ผ่านการตรวจสอบ
RATE_LIMIT_EXCEEDED429คำขอมากเกินไป (ตรวจสอบ header Retry-After)
INTERNAL_ERROR500ข้อผิดพลาดภายในเซิร์ฟเวอร์
PROVIDER_ERROR422ผู้ให้บริการ SMS ต้นทางปฏิเสธคำขอ เมื่อการสร้างคำสั่งซื้อล้มเหลว error อาจมี details: cause_counts (คำสั่งซื้อแบบ product_id เดิม — การนับจัดกลุ่มตามสาเหตุ) หรือ attempts (คำสั่งซื้อแบบ catalog_product_id — ผลลัพธ์ต่อความพยายามแต่ละครั้ง) โดยใช้ค่า ok, no_numbers, insufficient_balance, price_rejected, provider_unavailable, provider_account_balance, provider_error
NO_OFFER_AVAILABLE422ไม่มีข้อเสนอที่ใช้งานอยู่ตรงกับผลิตภัณฑ์และนโยบายที่ร้องขอ (เพดานราคา ความพร้อมจำหน่าย)
CANCEL_TOO_EARLY409คำสั่งซื้อใหม่เกินไปที่จะยกเลิก — รอ 2 นาที
REQUEST_IN_PROGRESS409คำขอสร้างคำสั่งซื้อด้วย idempotency key นี้ยังทำงานอยู่
IDEMPOTENCY_KEY_REUSED422idempotency key นี้ถูกใช้กับ body คำขอที่ต่างออกไปแล้ว
SERVICE_UNAVAILABLE503บริการไม่พร้อมให้บริการชั่วคราว (บำรุงรักษา)

ภาพรวม

ฟิลด์เงินทั้งหมดบน API /v2 เป็น USD ส่งคืนเป็นออบเจ็กต์เงิน — { "amount": "0.92", "currency": "USD", "canonical_amount": 15000, "canonical_currency": "IDR" } amount เป็นสตริงทศนิยม ส่วน canonical_amount คือค่าบัญชีแยกประเภท IDR ที่แม่นยำ (ใช้สำหรับการกระทบยอด) อัตรา rate USD/IDR ที่ใช้จะถูกเปิดเผยหนึ่งครั้งต่อการตอบกลับใน meta.fx v2 คือการฉายภาพ USD ขณะเรนเดอร์บนบัญชีแยกประเภท IDR เดียวกันกับ v1 — ไม่เคยจัดเก็บหรือทำธุรกรรมเป็น USD

การย้ายจาก v1 ไป v2

การยืนยันตัวตน

คำขอ API ทั้งหมดต้องใช้ Bearer token สร้าง token จากตั้งค่าบัญชีในแดชบอร์ด แล้วใส่ในทุกคำขอ:

Authorization:Bearer YOUR_API_TOKEN

คำขอที่ไม่มี token ที่ถูกต้องจะได้รับการตอบกลับ 401 UNAUTHORIZED

URL ฐาน

เส้นทาง endpoint ทั้งหมดด้านล่างเป็น relative กับ:

https://api.smscode.gg/v2

รูปแบบการตอบกลับ

ทุกการตอบกลับเป็น JSON ในรูปแบบ envelope ที่สอดคล้องกัน ทุกการตอบกลับมี header x-request-id สำหรับการดีบัก

สำเร็จ
{
  "success": true,
  "data": { ... }
}
ข้อผิดพลาด
{
  "success": false,
  "error": {
    "code": "ERROR_CODE",
    "message": "Human-readable message"
  }
}

ข้อผิดพลาด v2: ไม่มีอัตราแลกเปลี่ยนที่ใช้ได้ (503)

503 Service Unavailable · Retry-After: 60
{
  "success": false,
  "error": { "code": "FX_RATE_UNAVAILABLE", "message": "USD/IDR exchange rate is unavailable" }
}

ฟิลด์เงินทั้งหมดบน API /v2 เป็น USD ส่งคืนเป็นออบเจ็กต์เงิน — { "amount": "0.92", "currency": "USD", "canonical_amount": 15000, "canonical_currency": "IDR" } amount เป็นสตริงทศนิยม ส่วน canonical_amount คือค่าบัญชีแยกประเภท IDR ที่แม่นยำ (ใช้สำหรับการกระทบยอด) อัตรา rate USD/IDR ที่ใช้จะถูกเปิดเผยหนึ่งครั้งต่อการตอบกลับใน meta.fx v2 คือการฉายภาพ USD ขณะเรนเดอร์บนบัญชีแยกประเภท IDR เดียวกันกับ v1 — ไม่เคยจัดเก็บหรือทำธุรกรรมเป็น USD

GET/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()

ตัวอย่างการตอบกลับ

200 OK
{
  "success": true,
  "data": [
    {
      "id": 6,
      "code": "ID",
      "name": "Indonesia",
      "dial_code": "+62",
      "emoji": "🇮🇩",
      "active": true
    }
  ]
}

เหมือนกับ v1 — เปลี่ยนเฉพาะเส้นทางฐาน (/v1/v2)

GET/catalog/services

ส่งคืนรายการบริการ (แพลตฟอร์ม) ที่มี สามารถกรองตามประเทศได้

พารามิเตอร์คิวรี

ชื่อประเภทจำเป็นรายละเอียด
country_idintegerไม่กรองบริการที่มีสำหรับประเทศนี้

ตัวอย่างคำขอ

curl -s "https://api.smscode.gg/v2/catalog/services?country_id=7" \
  -H "Authorization: Bearer YOUR_API_TOKEN"
const res = await fetch("https://api.smscode.gg/v2/catalog/services?country_id=7", {
  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": 7},
    headers={"Authorization": "Bearer YOUR_API_TOKEN"})
data = res.json()

ตัวอย่างการตอบกลับ

200 OK
{
  "success": true,
  "data": [
    {
      "id": 3,
      "code": "wa",
      "name": "WhatsApp",
      "active": true
    }
  ]
}

เหมือนกับ v1 — เปลี่ยนเฉพาะเส้นทางฐาน (/v1/v2)

GET/catalog/operators

ส่งคืนผู้ให้บริการที่เลือกได้สำหรับประเทศ + บริการ หากมีทั้งผู้ให้บริการจริงและสต็อก Any response จะมีแถว Any ที่ operator_id เป็น null; หากไม่มีสินค้าเฉพาะผู้ให้บริการ รายการจะว่าง

พารามิเตอร์คิวรี

ชื่อประเภทจำเป็นรายละเอียด
country_idintegerใช่ID ประเทศ
platform_idintegerใช่ID แพลตฟอร์ม/บริการ

ตัวอย่างคำขอ

curl -s "https://api.smscode.gg/v2/catalog/operators?country_id=7&platform_id=3" \
  -H "Authorization: Bearer YOUR_API_TOKEN"
const params = new URLSearchParams({
  country_id: "7", platform_id: "3",
});
const res = await fetch(`https://api.smscode.gg/v2/catalog/operators?${params}`, {
  headers: { Authorization: "Bearer YOUR_API_TOKEN" },
});
const data = await res.json();
import requests

res = requests.get("https://api.smscode.gg/v2/catalog/operators",
    params={"country_id": 7, "platform_id": 3},
    headers={"Authorization": "Bearer YOUR_API_TOKEN"})
data = res.json()

ตัวอย่างการตอบกลับ

200 OK
{
  "success": true,
  "data": [
    {
      "operator_id": null,
      "code": "any",
      "name": "Any",
      "local_name": null
    },
    {
      "operator_id": 433,
      "code": "axis",
      "name": "AXIS (XL Axiata)",
      "local_name": "AXIS (XL Axiata)"
    }
  ]
}

เหมือนกับ v1 — เปลี่ยนเฉพาะเส้นทางฐาน (/v1/v2)

GET/catalog/products

ส่งคืนรายการสินค้าที่พร้อมใช้งานแบบแบ่งหน้า กรองตามประเทศ แพลตฟอร์ม และผู้ให้บริการแบบไม่บังคับ

พารามิเตอร์คิวรี

ชื่อประเภทจำเป็นรายละเอียด
country_idintegerไม่กรองตามรหัสประเทศ
platform_idintegerไม่กรองตามรหัสแพลตฟอร์ม/บริการ
operator_idintegerไม่ID ผู้ให้บริการแบบไม่บังคับจาก /catalog/operators เว้นว่างสำหรับสินค้า Any
sortstringไม่การเรียงลำดับ: price_asc (ค่าเริ่มต้น), price_desc, available_asc, available_desc, name_asc, name_desc
limitintegerไม่จำนวนผลลัพธ์ต่อหน้า (1-10,000 ค่าเริ่มต้น 1,000)
pageintegerไม่หมายเลขหน้า (ขั้นต่ำ 1 ค่าเริ่มต้น 1)

ตัวอย่างคำขอ

curl -s "https://api.smscode.gg/v2/catalog/products?country_id=7&platform_id=3&limit=10&page=1" \
  -H "Authorization: Bearer YOUR_API_TOKEN"
const params = new URLSearchParams({
  country_id: "7", 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": 7, "platform_id": 3, "limit": 10, "page": 1},
    headers={"Authorization": "Bearer YOUR_API_TOKEN"})
data = res.json()

ตัวอย่างการตอบกลับ

200 OK
{
  "success": true,
  "data": [
    {
      "id": 142,
      "name": "WhatsApp Indonesia",
      "country_id": 7,
      "platform_id": 3,
      "catalog_product_id": 87,
      "operator_id": null,
      "operator_name": null,
      "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 คือ IDR จำนวนเต็มต่อ 1 USD ดังนั้น USD = canonical_amount / rate ยอดรวมใช้ทศนิยม 2 ตำแหน่ง ราคา/การคืนเงินต่อรายการใช้ 4 ตำแหน่ง จำนวนเงินที่เป็นบวกอย่างแท้จริงจะไม่ถูกปัดเป็น 0.00 rate_as_of คือเวลาประทับ RFC3339 ของอัตรา (รูปแบบ +00:00) หรือ null เมื่อไม่มีการบันทึกเวลาประทับ

เฉพาะ v2: หากไม่มีอัตรา USD/IDR ที่ใช้ได้ เอนด์พอยต์เงินจะส่งคืน 503 FX_RATE_UNAVAILABLE พร้อมเฮดเดอร์ Retry-After แทนที่จะเป็นเนื้อหาเงิน v1 ไม่เคยส่งคืนค่านี้

GET/catalog/exchange-rate

ส่งคืนอัตราแลกเปลี่ยน USD/IDR ปัจจุบันที่ใช้สำหรับการแปลงสกุลเงิน

พารามิเตอร์

ไม่มี — v2 ส่งคืน USD/IDR เสมอ; พารามิเตอร์ ?pair ของ v1 จะถูกละเว้น

ตัวอย่างคำขอ

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()

ตัวอย่างการตอบกลับ

200 OK
{
  "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 หากไม่มีอัตราที่ใช้ได้

GET/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()

ตัวอย่างการตอบกลับ

200 OK
{
  "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 คือ IDR จำนวนเต็มต่อ 1 USD ดังนั้น USD = canonical_amount / rate ยอดรวมใช้ทศนิยม 2 ตำแหน่ง ราคา/การคืนเงินต่อรายการใช้ 4 ตำแหน่ง จำนวนเงินที่เป็นบวกอย่างแท้จริงจะไม่ถูกปัดเป็น 0.00 rate_as_of คือเวลาประทับ RFC3339 ของอัตรา (รูปแบบ +00:00) หรือ null เมื่อไม่มีการบันทึกเวลาประทับ

เฉพาะ v2: หากไม่มีอัตรา USD/IDR ที่ใช้ได้ เอนด์พอยต์เงินจะส่งคืน 503 FX_RATE_UNAVAILABLE พร้อมเฮดเดอร์ Retry-After แทนที่จะเป็นเนื้อหาเงิน v1 ไม่เคยส่งคืนค่านี้

GET/orders

ส่งคืนรายการคำสั่งซื้อของผู้ใช้ที่ผ่านการยืนยันตัวตน เรียงตามล่าสุด รองรับการกรองตามสถานะและแบ่งหน้าด้วย offset

พารามิเตอร์คิวรี

ชื่อประเภทจำเป็นรายละเอียด
limitintegerไม่จำนวนผลลัพธ์สูงสุด (1-100 ค่าเริ่มต้น 20)
offsetintegerไม่จำนวนผลลัพธ์ที่ข้าม (ค่าเริ่มต้น 0)
statusstringไม่กรองตามสถานะ: 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()

ตัวอย่างการตอบกลับ

200 OK
{
  "success": true,
  "data": [
    {
      "id": 1001,
      "status": "ACTIVE",
      "created_at": "2026-02-25T10:00:00+00:00",
      "product_id": 142,
      "catalog_product_id": 87,
      "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,
      "operator_id": null,
      "operator_name": 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 คือ IDR จำนวนเต็มต่อ 1 USD ดังนั้น USD = canonical_amount / rate ยอดรวมใช้ทศนิยม 2 ตำแหน่ง ราคา/การคืนเงินต่อรายการใช้ 4 ตำแหน่ง จำนวนเงินที่เป็นบวกอย่างแท้จริงจะไม่ถูกปัดเป็น 0.00 rate_as_of คือเวลาประทับ RFC3339 ของอัตรา (รูปแบบ +00:00) หรือ null เมื่อไม่มีการบันทึกเวลาประทับ

เฉพาะ v2: หากไม่มีอัตรา USD/IDR ที่ใช้ได้ เอนด์พอยต์เงินจะส่งคืน 503 FX_RATE_UNAVAILABLE พร้อมเฮดเดอร์ Retry-After แทนที่จะเป็นเนื้อหาเงิน v1 ไม่เคยส่งคืนค่านี้

GET/orders/{id}

ส่งคืนคำสั่งซื้อเดี่ยวตามรหัส ส่งคืนเฉพาะคำสั่งซื้อที่เป็นของผู้ใช้ที่ผ่านการยืนยันตัวตน

พารามิเตอร์เส้นทาง

ชื่อประเภทจำเป็นรายละเอียด
idintegerใช่รหัสคำสั่งซื้อ (path parameter)

ตัวอย่างคำขอ

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()

ตัวอย่างการตอบกลับ

200 OK
{
  "success": true,
  "data": {
    "id": 1001,
    "status": "OTP_RECEIVED",
    "created_at": "2026-02-25T10:00:00+00:00",
    "product_id": 142,
    "catalog_product_id": 87,
    "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,
    "operator_id": null,
    "operator_name": 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 คือ IDR จำนวนเต็มต่อ 1 USD ดังนั้น USD = canonical_amount / rate ยอดรวมใช้ทศนิยม 2 ตำแหน่ง ราคา/การคืนเงินต่อรายการใช้ 4 ตำแหน่ง จำนวนเงินที่เป็นบวกอย่างแท้จริงจะไม่ถูกปัดเป็น 0.00 rate_as_of คือเวลาประทับ RFC3339 ของอัตรา (รูปแบบ +00:00) หรือ null เมื่อไม่มีการบันทึกเวลาประทับ

เฉพาะ v2: หากไม่มีอัตรา USD/IDR ที่ใช้ได้ เอนด์พอยต์เงินจะส่งคืน 503 FX_RATE_UNAVAILABLE พร้อมเฮดเดอร์ Retry-After แทนที่จะเป็นเนื้อหาเงิน v1 ไม่เคยส่งคืนค่านี้

GET/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()

ตัวอย่างการตอบกลับ

200 OK
{
  "success": true,
  "data": [
    {
      "id": 1001,
      "status": "OTP_RECEIVED",
      "otp_code": null,
      "otp_message": "Confirm your login: https://example.com/confirm",
      "sms_revision": 1,
      "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,
      "sms_revision": 0,
      "otp_received_at": null,
      "expires_at": "2026-02-25T10:50:00+00:00",
      "failed_reason": null
    }
  ]
}

v2: เอนด์พอยต์นี้ไม่เกี่ยวข้องกับเงิน — ไม่ส่งคืน amount และไม่ส่งคืน meta.fx (โครงสร้างเดียวกับ v1 ภายใต้ /v2)

POST/orders/create

สร้างคำสั่งซื้อเบอร์เสมือนใหม่ หักยอดเงินอัตโนมัติ รองรับ header Idempotency-Key เพื่อป้องกันคำสั่งซื้อซ้ำเมื่อลองส่งคำขอใหม่

เนื้อหาคำขอ

ชื่อประเภทจำเป็นรายละเอียด
product_idintegerไม่ID สินค้าของสล็อตระดับราคาที่เฉพาะเจาะจงและเสถียรสำหรับสั่งซื้อโดยตรง ส่งค่านี้หรือ catalog_product_id อย่างใดอย่างหนึ่งเท่านั้น ห้ามส่งทั้งคู่
catalog_product_idintegerไม่ID umbrella สำหรับประเทศ+แพลตฟอร์มแบบ routed เซิร์ฟเวอร์จะเลือกระดับราคาปัจจุบันที่ตรงเงื่อนไข ส่งค่านี้หรือ product_id อย่างใดอย่างหนึ่ง
operator_idintegerไม่ID ผู้ให้บริการแบบไม่บังคับจาก /catalog/operators ใช้ได้เฉพาะกับ catalog_product_id; เว้นว่างสำหรับ Any
min_pricestringไม่ราคาขั้นต่ำแบบไม่บังคับ สตริงทศนิยม USD (เช่น "0.30") ใช้ได้เฉพาะกับ catalog_product_id
max_pricestringไม่เพดานราคาแบบไม่บังคับ สตริงทศนิยม USD (เช่น "0.50") ใช้ได้เฉพาะกับ catalog_product_id
prefer_providerstringไม่รหัสผู้ให้บริการแบบไม่บังคับ ใช้เป็นตัวตัดสินเมื่อข้อเสนอเท่ากัน
policystringไม่นโยบายการจัดเส้นทางแบบไม่บังคับ ใช้ได้เฉพาะกับ catalog_product_id เท่านั้น ค่าที่ใช้ได้: cheapest (ค่าเริ่มต้น) เลือกข้อเสนอที่พร้อมใช้งานและราคาต่ำสุด; best_success จัดอันดับข้อเสนอโดยให้ความสำเร็จในการส่งล่าสุดมาก่อน โดย best_success ให้คะแนนผู้ให้บริการแต่ละรายจากสัดส่วนคำสั่งซื้อที่ได้รับ OTP ในช่วง 30 วันเต็มล่าสุด แบ่งเป็นช่วงละ 10% และจะนับผู้ให้บริการรายนั้นก็ต่อเมื่อมีคำสั่งซื้ออย่างน้อย 20 รายการในช่วงเวลาดังกล่าว — ผู้ให้บริการที่ต่ำกว่าเกณฑ์นี้หรือยังไม่มีประวัติจะถือว่าเป็นกลาง จึงทำให้ข้อเสนอใหม่ไม่ถูกมองข้าม (ต้องเลือกใช้เอง; สัญญาณเริ่มต้นที่ค่ากลาง) หากตั้งค่า prefer_provider ไว้ด้วย ผู้ให้บริการที่เลือกไว้ก็ยังคงอยู่อันดับแรก
quantityintegerไม่จำนวน (1-100 ค่าเริ่มต้น 1)

ส่ง header Idempotency-Key เพื่อส่งคำขอซ้ำได้อย่างปลอดภัยโดยไม่สร้างคำสั่งซื้อซ้ำ key ประกอบด้วยตัวอักษร ตัวเลข ขีดกลาง และขีดล่าง (A-Z a-z 0-9 _ -) ได้ ยาวไม่เกิน 128 อักขระ key ที่ไม่ถูกต้องจะถูกปฏิเสธด้วย 422 VALIDATION_ERROR การส่งซ้ำด้วย key เดิมและ body เดิมจะเล่นผลลัพธ์เดิมซ้ำ (รวมถึง failed_count ของกรณีสำเร็จบางส่วน) การส่งซ้ำที่ไปถึงผู้ให้บริการแล้วแต่ล้มเหลวจะถูกบันทึกไว้และเล่นข้อผิดพลาดเดิมซ้ำ — ให้ใช้ key ใหม่เพื่อลองอีกครั้ง ความล้มเหลวที่ไม่มีผลข้างเคียง (ยอดเงินไม่พอ ไม่มีข้อเสนอที่ใช้ได้) จะปล่อย key คืน คุณจึงสามารถเติมเงินแล้วลองซ้ำด้วย key เดิมได้ การใช้ key เดิมซ้ำกับ body ที่ต่างออกไปจะคืนค่า 422 IDEMPOTENCY_KEY_REUSED และคำขอที่ยังทำงานอยู่ด้วย key นั้นจะคืนค่า 409 REQUEST_IN_PROGRESS ฟิลด์ failed_reason ในการตอบกลับของ create จะเป็น null เสมอ — จะมีค่าก็ต่อเมื่อ poll/list คำสั่งซื้อเท่านั้น

ตัวอย่างคำขอ

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 '{"catalog_product_id":87,"min_price":"0.30","max_price":"0.50","operator_id":433,"quantity":1}'
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({
    catalog_product_id: 87,
    min_price: "0.30",
    max_price: "0.50",
    operator_id: 433,
    quantity: 1,
  }),
});
const data = await res.json();
import requests

res = requests.post("https://api.smscode.gg/v2/orders/create",
    json={
        "catalog_product_id": 87,
        "min_price": "0.30",
        "max_price": "0.50",
        "operator_id": 433,
        "quantity": 1,
    },
    headers={
        "Authorization": "Bearer YOUR_API_TOKEN",
        "Idempotency-Key": "unique-request-id-123",
    })
data = res.json()

ตัวอย่างการตอบกลับ

200 OK
{
  "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,
        "operator_id": null,
        "operator_name": 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 คือ IDR จำนวนเต็มต่อ 1 USD ดังนั้น USD = canonical_amount / rate ยอดรวมใช้ทศนิยม 2 ตำแหน่ง ราคา/การคืนเงินต่อรายการใช้ 4 ตำแหน่ง จำนวนเงินที่เป็นบวกอย่างแท้จริงจะไม่ถูกปัดเป็น 0.00 rate_as_of คือเวลาประทับ RFC3339 ของอัตรา (รูปแบบ +00:00) หรือ null เมื่อไม่มีการบันทึกเวลาประทับ

เฉพาะ v2: หากไม่มีอัตรา USD/IDR ที่ใช้ได้ เอนด์พอยต์เงินจะส่งคืน 503 FX_RATE_UNAVAILABLE พร้อมเฮดเดอร์ Retry-After แทนที่จะเป็นเนื้อหาเงิน v1 ไม่เคยส่งคืนค่านี้

POST/orders/cancel

ยกเลิกคำสั่งซื้อที่กำลังดำเนินการ ค่าเช่าจะถูกคืนเข้ายอดบัญชีของคุณ

เนื้อหาคำขอ

ชื่อประเภทจำเป็นรายละเอียด
idintegerใช่รหัสคำสั่งซื้อที่จะยกเลิก

ตัวอย่างคำขอ

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()

ตัวอย่างการตอบกลับ

200 OK
{
  "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 คือ IDR จำนวนเต็มต่อ 1 USD ดังนั้น USD = canonical_amount / rate ยอดรวมใช้ทศนิยม 2 ตำแหน่ง ราคา/การคืนเงินต่อรายการใช้ 4 ตำแหน่ง จำนวนเงินที่เป็นบวกอย่างแท้จริงจะไม่ถูกปัดเป็น 0.00 rate_as_of คือเวลาประทับ RFC3339 ของอัตรา (รูปแบบ +00:00) หรือ null เมื่อไม่มีการบันทึกเวลาประทับ

เฉพาะ v2: หากไม่มีอัตรา USD/IDR ที่ใช้ได้ เอนด์พอยต์เงินจะส่งคืน 503 FX_RATE_UNAVAILABLE พร้อมเฮดเดอร์ Retry-After แทนที่จะเป็นเนื้อหาเงิน v1 ไม่เคยส่งคืนค่านี้

POST/orders/finish

ทำเครื่องหมายคำสั่งซื้อว่าเสร็จสิ้นหลังจากได้รับ OTP การดำเนินการนี้จะปล่อยเบอร์ทันทีแทนที่จะรอให้หมดอายุ

เนื้อหาคำขอ

ชื่อประเภทจำเป็นรายละเอียด
idintegerใช่รหัสคำสั่งซื้อที่จะเสร็จสิ้น

ตัวอย่างคำขอ

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()

ตัวอย่างการตอบกลับ

200 OK
{
  "success": true,
  "data": {
    "order_id": 1001,
    "status": "COMPLETED"
  }
}

เหมือนกับ v1 — เปลี่ยนเฉพาะเส้นทางฐาน (/v1/v2)

POST/orders/resend

ขอให้แพลตฟอร์มส่ง SMS ซ้ำไปยังเบอร์ที่เช่า แพลตฟอร์มบางแห่งไม่รองรับการส่งซ้ำ — ตรวจสอบฟิลด์ resent ในการตอบกลับ

เนื้อหาคำขอ

ชื่อประเภทจำเป็นรายละเอียด
idintegerใช่รหัสคำสั่งซื้อที่จะส่ง SMS ซ้ำ

ตัวอย่างคำขอ

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()

ตัวอย่างการตอบกลับ

200 OK
{
  "success": true,
  "data": {
    "order_id": 1001,
    "status": "ACTIVE",
    "resent": true
  }
}

เหมือนกับ v1 — เปลี่ยนเฉพาะเส้นทางฐาน (/v1/v2)

POST/orders/reactivate

เปิดใช้งานเบอร์ที่เสร็จสมบูรณ์อีกครั้ง — สั่งซื้อเบอร์เดิมซ้ำเพื่อรับรหัสยืนยันอีกครั้งโดยไม่ต้องเช่าเบอร์ใหม่ เฉพาะคำสั่งซื้อที่เสร็จสมบูรณ์ซึ่งเบอร์รองรับการเปิดใช้งานอีกครั้งเท่านั้นที่ทำได้ (ตรวจสอบ can_reactivate ที่คำสั่งซื้อ หรือดูตัวอย่างด้วย reactivate-options) คำสั่งซื้อลูกที่เปิดใช้งานใหม่เป็นคำสั่งซื้อใหม่ ซึ่งคืนค่าในรูปแบบเดียวกับ create ยอดเงินจะถูกหักโดยอัตโนมัติ

เนื้อหาคำขอ

ชื่อประเภทจำเป็นรายละเอียด
idintegerใช่คำสั่งซื้อที่เสร็จสมบูรณ์ที่จะเปิดใช้งานอีกครั้ง
max_pricestringไม่เพดานค่าใช้จ่ายแบบไม่บังคับ สตริงทศนิยม USD (เช่น "0.50") การเปิดใช้งานอีกครั้งจะถูกปฏิเสธด้วย 422 VALIDATION_ERROR หากค่าใช้จ่ายปัจจุบันเกินเพดานนี้

เช่นเดียวกับ create นี่คือการเปลี่ยนแปลงที่เกี่ยวกับเงิน — ให้ส่ง header Idempotency-Key เพื่อลองซ้ำได้อย่างปลอดภัย (create และ reactivate ไม่มีทางชนกันบน key เดียวกัน) การใช้ key เดิมซ้ำกับ body ที่ต่างออกไปจะคืนค่า 422 IDEMPOTENCY_KEY_REUSED และคำขอที่ยังดำเนินการอยู่ด้วย key นั้นจะคืนค่า 409 REQUEST_IN_PROGRESS เบอร์ที่ไม่สามารถเปิดใช้งานอีกครั้งได้จะคืนค่า 409 CONFLICT ยอดเงินที่น้อยเกินไปจะคืนค่า 409 INSUFFICIENT_BALANCE

ตัวอย่างคำขอ

curl -s -X POST https://api.smscode.gg/v2/orders/reactivate \
  -H "Authorization: Bearer YOUR_API_TOKEN" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: unique-request-id-901" \
  -d '{"id":1001,"max_price":"0.50"}'
const res = await fetch("https://api.smscode.gg/v2/orders/reactivate", {
  method: "POST",
  headers: {
    Authorization: "Bearer YOUR_API_TOKEN",
    "Content-Type": "application/json",
    "Idempotency-Key": "unique-request-id-901",
  },
  body: JSON.stringify({ id: 1001, max_price: "0.50" }),
});
const data = await res.json();
import requests

res = requests.post("https://api.smscode.gg/v2/orders/reactivate",
    json={"id": 1001, "max_price": "0.50"},
    headers={
        "Authorization": "Bearer YOUR_API_TOKEN",
        "Idempotency-Key": "unique-request-id-901",
    })
data = res.json()

ตัวอย่างการตอบกลับ

200 OK
{
  "success": true,
  "data": {
    "orders": [
      {
        "id": 1044,
        "status": "ACTIVE",
        "product_id": 142,
        "catalog_product_id": 87,
        "amount": { "amount": "0.9231", "currency": "USD", "canonical_amount": 15000, "canonical_currency": "IDR" },
        "phone_number": "+6281234567890",
        "otp_code": null,
        "otp_received_at": null,
        "expires_at": "2026-02-25T11:20:00+00:00",
        "failed_reason": null,
        "operator_id": null,
        "operator_name": 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 คือ IDR จำนวนเต็มต่อ 1 USD ดังนั้น USD = canonical_amount / rate ยอดรวมใช้ทศนิยม 2 ตำแหน่ง ราคา/การคืนเงินต่อรายการใช้ 4 ตำแหน่ง จำนวนเงินที่เป็นบวกอย่างแท้จริงจะไม่ถูกปัดเป็น 0.00 rate_as_of คือเวลาประทับ RFC3339 ของอัตรา (รูปแบบ +00:00) หรือ null เมื่อไม่มีการบันทึกเวลาประทับ

เฉพาะ v2: หากไม่มีอัตรา USD/IDR ที่ใช้ได้ เอนด์พอยต์เงินจะส่งคืน 503 FX_RATE_UNAVAILABLE พร้อมเฮดเดอร์ Retry-After แทนที่จะเป็นเนื้อหาเงิน v1 ไม่เคยส่งคืนค่านี้

GET/orders/{id}/reactivate-options

ดูตัวอย่างว่าการเปิดใช้งานอีกครั้งจะคิดค่าใช้จ่ายเท่าไรในตอนนี้ อ่านอย่างเดียว — ไม่ใช้ Idempotency-Key และไม่สร้างสิ่งใด คืนค่าใช้จ่ายเป็นออบเจ็กต์เงิน USD พร้อมใบเสร็จ FX ใช้ได้เฉพาะคำสั่งซื้อที่เสร็จสมบูรณ์ซึ่งเบอร์รองรับการเปิดใช้งานอีกครั้งเท่านั้น

พารามิเตอร์เส้นทาง

ชื่อประเภทจำเป็นรายละเอียด
idintegerใช่รหัสคำสั่งซื้อที่จะดูตัวอย่างค่าใช้จ่ายในการเปิดใช้งานอีกครั้ง (path parameter)

ตัวอย่างคำขอ

curl -s https://api.smscode.gg/v2/orders/1001/reactivate-options \
  -H "Authorization: Bearer YOUR_API_TOKEN"
const res = await fetch("https://api.smscode.gg/v2/orders/1001/reactivate-options", {
  headers: { Authorization: "Bearer YOUR_API_TOKEN" },
});
const data = await res.json();
import requests

res = requests.get("https://api.smscode.gg/v2/orders/1001/reactivate-options",
    headers={"Authorization": "Bearer YOUR_API_TOKEN"})
data = res.json()

ตัวอย่างการตอบกลับ

200 OK
{
  "success": true,
  "data": {
    "cost": { "amount": "0.9231", "currency": "USD", "canonical_amount": 15000, "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 คือ IDR จำนวนเต็มต่อ 1 USD ดังนั้น USD = canonical_amount / rate ยอดรวมใช้ทศนิยม 2 ตำแหน่ง ราคา/การคืนเงินต่อรายการใช้ 4 ตำแหน่ง จำนวนเงินที่เป็นบวกอย่างแท้จริงจะไม่ถูกปัดเป็น 0.00 rate_as_of คือเวลาประทับ RFC3339 ของอัตรา (รูปแบบ +00:00) หรือ null เมื่อไม่มีการบันทึกเวลาประทับ

เฉพาะ v2: หากไม่มีอัตรา USD/IDR ที่ใช้ได้ เอนด์พอยต์เงินจะส่งคืน 503 FX_RATE_UNAVAILABLE พร้อมเฮดเดอร์ Retry-After แทนที่จะเป็นเนื้อหาเงิน v1 ไม่เคยส่งคืนค่านี้

GET/webhook

ส่งคืนการตั้งค่า webhook notification ปัจจุบันของคุณ

พารามิเตอร์

ไม่มี

ตัวอย่างคำขอ

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()

ตัวอย่างการตอบกลับ

200 OK
{
  "success": true,
  "data": {
    "webhook_url": "https://example.com/webhook",
    "webhook_secret": "a1b2c3d4e5f6..."
  }
}

เหมือนกับ v1 — เปลี่ยนเฉพาะเส้นทางฐาน (/v1/v2)

PATCH/webhook

อัปเดต URL และ/หรือ secret ของ webhook secret จะถูกสร้างอัตโนมัติเมื่อคุณตั้ง URL ครั้งแรก ส่งสตริงว่างเพื่อล้าง URL ต้องใช้ HTTPS

เนื้อหาคำขอ

ชื่อประเภทจำเป็นรายละเอียด
webhook_urlstringไม่HTTPS URL สำหรับรับเหตุการณ์ webhook (สตริงว่างเพื่อล้าง)
webhook_secretstringไม่Shared secret สำหรับ HMAC-SHA256 signature (สร้างอัตโนมัติหากไม่ระบุในครั้งแรก)

ต้องระบุอย่างน้อยหนึ่งฟิลด์

ตัวอย่างคำขอ

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()

ตัวอย่างการตอบกลับ

200 OK
{
  "success": true,
  "data": {
    "webhook_url": "https://example.com/webhook",
    "webhook_secret": "a1b2c3d4e5f6..."
  }
}

เหมือนกับ v1 — เปลี่ยนเฉพาะเส้นทางฐาน (/v1/v2)

POST/webhook/test

ส่งเหตุการณ์ทดสอบไปยัง URL webhook ที่ตั้งค่าไว้ ส่งคืนรหัสสถานะ HTTP จากเซิร์ฟเวอร์ของคุณ มีประโยชน์สำหรับตรวจสอบว่า endpoint ทำงานก่อนเริ่มใช้งานจริง

พารามิเตอร์

ไม่มี

ตัวอย่างคำขอ

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()

ตัวอย่างการตอบกลับ

200 OK
{
  "success": true,
  "data": {
    "status_code": 200
  }
}

เหมือนกับ v1 — เปลี่ยนเฉพาะเส้นทางฐาน (/v1/v2)

การแจ้งเตือน Webhook

ตั้งค่า URL webhook เพื่อรับการแจ้งเตือนแบบ push แบบเรียลไทม์สำหรับเหตุการณ์คำสั่งซื้อแทนการ polling นี่คือวิธีที่แนะนำสำหรับ bot script

เหตุการณ์

เหตุการณ์ทริกเกอร์
order.otp_receivedได้รับ SMS ใหม่ โดยรหัสที่ตรวจพบอาจเป็น null
order.completedคำสั่งซื้อถูกทำเครื่องหมายว่าเสร็จสิ้น (ด้วยตนเองหรือเมื่อหมดอายุ)
order.expiredคำสั่งซื้อหมดอายุก่อนรับ SMS (คืนเงินแล้ว)
order.canceledคำสั่งซื้อถูกยกเลิกโดยผู้ใช้ (คืนเงินแล้ว)

SMS ใหม่แต่ละข้อความจะส่ง event นี้ otp_code อาจเป็น null ขณะที่มี otp_message ได้ event SMS หลายรายการอาจมาถึงไม่ตามลำดับ ใช้ sms_revision เพื่อไม่รับคู่ข้อมูลรวมที่เก่ากว่า

เพย์โหลด

เนื้อหา Webhook POST
{
  "event": "order.otp_received",
  "timestamp": "2026-02-25T12:00:00+00:00",
  "data": {
    "order_id": 1001,
    "phone_number": "+628123456789",
    "otp_code": null,
    "otp_message": "Confirm your login: https://example.com/confirm",
    "sms_revision": 1,
    "product_id": 142,
    "catalog_product_id": 87,
    "country": "Indonesia",
    "platform": "WhatsApp"
  }
}

การตรวจสอบ Signature

ทุกคำขอ webhook มี header X-Webhook-Signature พร้อม HMAC-SHA256 signature ของ request body โดยใช้ webhook_secret ของคุณเป็น key:

X-Webhook-Signature:sha256={HMAC-SHA256(body, webhook_secret)}

ตรวจสอบ signature นี้บนเซิร์ฟเวอร์ของคุณเพื่อยืนยันว่าคำขอเป็นของจริง การส่งเป็นแบบ fire-and-forget พร้อม timeout 3 วินาทีและไม่มีการลองใหม่

ขีดจำกัดอัตรา

คำขอ API ถูกจำกัดอัตราตามกลุ่ม endpoint การเกินขีดจำกัดจะได้รับ 429 Too Many Requests พร้อม header Retry-After ที่ระบุจำนวนวินาทีที่ต้องรอ

กลุ่ม Endpointขีดจำกัดช่วงเวลา
แคตตาล็อก (ประเทศ บริการ ผลิตภัณฑ์ อัตราแลกเปลี่ยน)5,000 คำขอ60 วินาที
ยอดเงิน600 คำขอ60 วินาที
การอ่านคำสั่งซื้อ (รายการ, ดู, กำลังดำเนินการ)5,000 คำขอ60 วินาที
สร้างคำสั่งซื้อ3,000 คำขอ60 วินาที
ยกเลิกคำสั่งซื้อ1,000 คำขอ60 วินาที
การดำเนินการคำสั่งซื้อ (finish, resend)1,000 คำขอ60 วินาที
การตั้งค่า Webhook (ดู, อัปเดต)600 คำขอ60 วินาที
ทดสอบ Webhook10 คำขอ60 วินาที

รหัสข้อผิดพลาด

การตอบกลับข้อผิดพลาดจะมีรหัสเหล่านี้ใน error.code:

รหัสHTTPรายละเอียด
UNAUTHORIZED401ไม่มีหรือ API token ไม่ถูกต้อง
FORBIDDEN403การเข้าถึงถูกปฏิเสธ
NOT_FOUND404ไม่พบทรัพยากร (คำสั่งซื้อ อัตราแลกเปลี่ยน ฯลฯ)
CONFLICT409คำขอซ้ำหรือทรัพยากรขัดแย้ง
INSUFFICIENT_BALANCE409ยอดเงินไม่เพียงพอสำหรับสร้างคำสั่งซื้อ
VALIDATION_ERROR422พารามิเตอร์คำขอไม่ผ่านการตรวจสอบ
RATE_LIMIT_EXCEEDED429คำขอมากเกินไป (ตรวจสอบ header Retry-After)
INTERNAL_ERROR500ข้อผิดพลาดภายในเซิร์ฟเวอร์
PROVIDER_ERROR422ผู้ให้บริการ SMS ต้นทางปฏิเสธคำขอ เมื่อการสร้างคำสั่งซื้อล้มเหลว error อาจมี details: cause_counts (คำสั่งซื้อแบบ product_id เดิม — การนับจัดกลุ่มตามสาเหตุ) หรือ attempts (คำสั่งซื้อแบบ catalog_product_id — ผลลัพธ์ต่อความพยายามแต่ละครั้ง) โดยใช้ค่า ok, no_numbers, insufficient_balance, price_rejected, provider_unavailable, provider_account_balance, provider_error
NO_OFFER_AVAILABLE422ไม่มีข้อเสนอที่ใช้งานอยู่ตรงกับผลิตภัณฑ์และนโยบายที่ร้องขอ (เพดานราคา ความพร้อมจำหน่าย)
CANCEL_TOO_EARLY409คำสั่งซื้อใหม่เกินไปที่จะยกเลิก — รอ 2 นาที
REQUEST_IN_PROGRESS409คำขอสร้างคำสั่งซื้อด้วย idempotency key นี้ยังทำงานอยู่
IDEMPOTENCY_KEY_REUSED422idempotency key นี้ถูกใช้กับ body คำขอที่ต่างออกไปแล้ว
SERVICE_UNAVAILABLE503บริการไม่พร้อมให้บริการชั่วคราว (บำรุงรักษา)
FX_RATE_UNAVAILABLE503อัตราแลกเปลี่ยน USD/IDR ไม่พร้อมใช้งาน (เอนด์พอยต์เงิน v2) — ส่งคืน 503 พร้อมเฮดเดอร์ Retry-After
v1 → v2

การย้ายจาก v1 ไป v2

v1 ให้บริการ IDR; v2 ให้บริการ USD ทั้งสองเวอร์ชันอยู่ร่วมกันถาวร — ไม่มีการยกเลิก เลือกหนึ่งเวอร์ชันต่อหนึ่งการเชื่อมต่อ อย่าปนเส้นทางฐาน v2 เหมือนกับ v1 ทุกประการ ยกเว้นวิธีการแสดงค่าเงิน

ด้านv1 · IDRv2 · USD
ฟิลด์เงินIDR จำนวนเต็ม เช่น 15000ออบเจ็กต์เงิน { amount, currency, canonical_amount, canonical_currency }
meta.fxไม่มีจำเป็นในทุกการตอบกลับที่เกี่ยวข้องกับเงิน
สกุลเงินIDRUSD (ฮาร์ดโค้ด)
FX_RATE_UNAVAILABLE503 + Retry-After ใหม่ เมื่อไม่มีอัตราที่ใช้ได้
ความแม่นยำยอดรวม 2 ตำแหน่ง ราคา/คืนเงิน 4 ตำแหน่ง ปัดขึ้นสำหรับค่าบวก
GET /catalog/exchange-rate{pair, base_currency, quote_currency, rate}; เคารพ ?pair{pair, rate, rate_as_of}; ละเว้น ?pair (USD/IDR เท่านั้น)

ตัวอย่างเทียบเคียง

GET/balance
v1
{
  "success": true,
  "data": {
    "currency": "IDR",
    "balance": 500000
  }
}
v2
{
  "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" } }
}
GET/catalog/products
v1
{
  "success": true,
  "data": [
    {
      "id": 142,
      "name": "WhatsApp Indonesia",
      "country_id": 7,
      "platform_id": 3,
      "catalog_product_id": 87,
      "operator_id": null,
      "operator_name": null,
      "available": 42,
      "price": 15000,
      "active": true
    }
  ],
  "meta": { "page": 1, "limit": 10, "count": 1 }
}
v2
{
  "success": true,
  "data": [
    {
      "id": 142,
      "name": "WhatsApp Indonesia",
      "country_id": 7,
      "platform_id": 3,
      "catalog_product_id": 87,
      "operator_id": null,
      "operator_name": null,
      "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" } }
}

product_id คือ ID สล็อตระดับราคาของ SMSCode ที่เสถียร เก็บค่านี้ไว้เมื่อคุณต้องการสั่งซื้อระดับราคานั้นแบบเจาะจง ราคาและสถานะพร้อมใช้งานอาจเปลี่ยนในแถวเดิมได้ ส่วน catalog_product_id คือ umbrella ของประเทศ+แพลตฟอร์มที่เสถียรสำหรับการสั่งซื้อแบบ routed; ใช้ร่วมกับ operator_id, min_price, max_price, prefer_provider และ policy แบบไม่บังคับเมื่อคุณต้องการให้เซิร์ฟเวอร์เลือกระดับราคาปัจจุบันที่ตรงเงื่อนไข

POST/orders/cancel
v1
{
  "success": true,
  "data": {
    "order_id": 1001,
    "status": "CANCELED",
    "refund_amount": 15000,
    "new_balance": 515000
  }
}
v2
{
  "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" } }
}

เช็กลิสต์การย้าย

  1. เปลี่ยนเส้นทางฐาน /v1/v2
  2. แยกวิเคราะห์ฟิลด์เงินเป็นออบเจ็กต์ — อ่าน amount เป็นสตริงทศนิยม; currency คือ "USD"
  3. สำหรับการกระทบยอดบัญชีแยกประเภท ให้ใช้ canonical_amount (IDR ที่แม่นยำ) ส่วน amount USD เป็นการฉายภาพขณะเรนเดอร์ และ rate จะถูกเปิดเผยหนึ่งครั้งใน meta.fx
  4. จัดการ FX_RATE_UNAVAILABLE (503) ใหม่ — ลองใหม่หลัง Retry-After v1 ไม่เคยส่งคืนค่านี้