core

API Reference

Public REST API สำหรับ integrator — ตรวจสลิป, อ่านโควตา, รูปแบบ error + status ครบทุก endpoint

⏱ REST
The documentation is currently written in Thai only — an English translation is on the way. The navigation and page chrome are already in English.

Public REST API สำหรับเชื่อมต่อระบบของคุณ (POS / ERP / บอท) เข้ากับ SlipBolt ยืนยันตัวตนด้วย API key (sk_live_… / sk_test_…) — ดู Authentication

Base URL

https://api.slip-bolt.com/api/v1

ทุก request ต้องมี header:

Authorization: Bearer sk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

เช็คก่อนยิงงานชุดใหญ่GET /api/v1/me (ร้าน · แพ็ก · โควตา live+test · บัญชีรับเงิน) และ GET /api/v1/quota (โควตาอย่างเดียว) ไม่หักสลิป · ต้องมี scope read ช่วง Trial ไม่มี API key — ดูกระเบื้องโควตาบน Dashboard

Test mode — ใช้ key sk_test_… เพื่อรันทั้ง pipeline จริงโดย ไม่กินโควตาของร้าน เหมาะกับตอน dev/CI · แต่มีเพดานแยกของตัวเอง 200 ครั้ง/ร้าน/เดือน (ตรวจกับธนาคารจริงทุกครั้ง ไม่ใช่ของจำลอง) เกินแล้วได้ 402 TEST_QUOTA_EXCEEDED เหมือนกัน — ดูรายละเอียดที่ Authentication


POST /api/v1/verify

ตรวจสลิปโอนเงิน 1 ใบ ส่งรูปสลิปเป็น raw bytes (ไม่ใช่ JSON / ไม่ใช่ base64)

Request

MethodPOST
Content-Typeapplication/octet-stream
Bodyไฟล์รูปสลิป (JPEG/PNG) เป็น binary ดิบ
Scope ที่ต้องมีverify
curl -X POST https://api.slip-bolt.com/api/v1/verify \
  -H "Authorization: Bearer sk_live_..." \
  -H "Content-Type: application/octet-stream" \
  --data-binary "@slip.jpg"

Node.js

import { readFile } from 'node:fs/promises';

const image = await readFile('slip.jpg');
const res = await fetch('https://api.slip-bolt.com/api/v1/verify', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.SLIPBOLT_API_KEY}`,
    'Content-Type': 'application/octet-stream',
  },
  body: image,
});

if (res.status === 402) throw new Error('โควตาหมด — อัปเกรดแพ็ก');
const data = await res.json();
console.log(data.verified, data.amount, res.headers.get('X-Slipbolt-Quota-Remaining'));

Python

import requests

with open("slip.jpg", "rb") as f:
    res = requests.post(
        "https://api.slip-bolt.com/api/v1/verify",
        headers={
            "Authorization": f"Bearer {API_KEY}",
            "Content-Type": "application/octet-stream",
        },
        data=f.read(),
    )

if res.status_code == 402:
    raise RuntimeError("quota exhausted")
print(res.json()["verified"], res.headers["X-Slipbolt-Quota-Remaining"])

Response 200 OK

{
  "success": true,
  "data": {
    "id": "8f3c…",
    "status": "SUCCESS",
    "verified": true,
    "transRef": "015170106978META…",
    "amount": 250.0,
    "bankCode": "002",
    "bankName": "ธนาคารกรุงเทพ",
    "senderName": "นาย ก. …",
    "senderAccount": "xxx-x-x1234-x",
    "receiverName": "ร้าน ข.",
    "receiverAccount": "xxx-x-x5678-x",
    "transDate": "2026-06-17T03:21:05.000Z",
    "errorCode": null,
    "errorMessage": null,
    "createdAt": "2026-06-17T03:21:06.412Z",
    "duplicate": false,
    "cached": false,
    "latencyMs": 480,
    "environment": "live",
    "quota": { "used": 132, "limit": 500, "remaining": 368 }
  },
  "meta": { "requestId": "req_…", "timestamp": "2026-06-17T10:21:06+07:00" }
}

verified เป็น true เฉพาะเมื่อ status === "SUCCESS" — ดูตาราง status ด้านล่าง


Quota — pool เดียวกับสลิป LINE

การเรียก API ใช้โควตารายเดือนเดียวกับสลิปจาก LINE ของร้าน (pool เดียว นับรวมกัน) ไม่ใช่โควตาแยก — ใช้ผ่านช่องไหนก็หักจากก้อนเดียวกัน

ทุก response (รวม GET /api/v1/quota) แนบ header บอกยอดคงเหลือ:

Headerความหมาย
X-Slipbolt-Quota-Limitเพดานสลิป/เดือนของแพ็กปัจจุบัน
X-Slipbolt-Quota-Usedใช้ไปแล้วในรอบเดือนนี้
X-Slipbolt-Quota-Remainingคงเหลือ
X-Slipbolt-Quota-Reset-Daysกี่วันก่อนรีเซ็ตรอบใหม่

โควตาหมด → 402 Payment Required (เฉพาะ key live) — ไม่ใช่ 200 เงียบ ๆ ให้ integrator เช็คจาก HTTP status ได้เลย

{ "success": false, "statusCode": 402, "error": "Payment Required",
  "message": "โควตาสลิปรายเดือนของร้านถูกใช้หมดแล้ว — อัปเกรดแพ็กหรือรอรอบบิลถัดไป",
  "code": "QUOTA_EXCEEDED", "quota": { "used": 500, "limit": 500, "remaining": 0 } }

key sk_test_ ไม่กินโควตาก้อนนี้ (ของร้าน) จึงไม่ได้ 402 ตัวนี้ — แต่มีเพดานแยกของตัวเอง 200 ครั้ง/เดือน เกินแล้วได้ 402 TEST_QUOTA_EXCEEDED เหมือนกัน (ดูหัวข้อ Idempotency/Test mode ด้านบน)


GET /api/v1/quota

อ่านโควตาคงเหลือ โดยไม่เปลือง verify — เหมาะให้ระบบ poll ก่อนยิงงานชุดใหญ่

MethodGET
Scope ที่ต้องมีread
curl https://api.slip-bolt.com/api/v1/quota \
  -H "Authorization: Bearer sk_live_..."
{
  "success": true,
  "data": {
    "used": 132,
    "limit": 500,
    "remaining": 368,
    "percentUsed": 26,
    "allowOverage": false,
    "planCode": "STARTER",
    "periodMonth": "2026-06",
    "daysUntilReset": 13
  }
}

GET /api/v1/me

ข้อมูลร้าน · คีย์ · แพ็ก · โควตาคงเหลือ — ไม่เปลืองโควตา เหมาะเรียกตอนบูตระบบของคุณ เพื่อเช็คว่าคีย์ถูกร้าน scope ครบ เหลือสลิปเท่าไหร่ และบัญชีรับเงินที่ระบบใช้เทียบผู้รับคือบัญชีไหน (สาเหตุอันดับหนึ่งของ RECEIVER_MISMATCH คือร้านยังไม่ได้เพิ่มบัญชีที่ลูกค้าโอนเข้า)

ช่วงทดลอง 7 วัน plan.code จะเป็น "TRIAL" และ quota.limit = 50 เสมอ แม้ร้านจะเคยเลือกการ์ด Pro/Business ตอนสมัคร — สิทธิ์แพ็กจ่ายเริ่มหลังชำระแล้วเท่านั้น

MethodGET
Scope ที่ต้องมีread
curl https://api.slip-bolt.com/api/v1/me \
  -H "Authorization: Bearer sk_live_..."
{
  "success": true,
  "data": {
    "shop": { "id": "8f3c…", "name": "ร้าน ข." },
    "apiKey": {
      "name": "POS หน้าร้าน",
      "prefix": "sk_live_a1b2c3",
      "environment": "live",
      "scopes": ["verify", "read"],
      "lastUsedAt": "2026-06-17T03:21:06.000Z",
      "createdAt": "2026-05-01T00:00:00.000Z"
    },
    "plan": { "code": "STARTER", "name": "Starter" },
    "quota": { "used": 132, "limit": 500, "remaining": 368, "percentUsed": 26,
      "overage": 0, "allowOverage": false, "planCode": "STARTER",
      "periodMonth": "2026-06", "daysUntilReset": 13 },
    "testQuota": { "used": 12, "limit": 200, "remaining": 188 },
    "rateLimit": { "perKeyPerMinute": 120, "verifyPerMinute": 240 },
    "payeeAccounts": [
      { "bankCode": "kbank", "kind": "BANK", "holder": "ร้าน ข.", "accountLast4": "5678", "isPrimary": true, "status": "ACTIVE" }
    ]
  }
}

payeeAccounts ส่งแค่ 4 ตัวท้าย ของเลขบัญชี — คีย์ API รั่วได้ง่ายกว่ารหัสผ่านหน้าเว็บ ไม่จำเป็นต้องให้เลขเต็มเพื่อรู้ว่า "บัญชีไหน" · testQuota.used: null = วัดไม่ได้ชั่วคราว (คีย์ sk_test_ จะตกไปกินโควตาปกติของร้านแทน ไม่ใช่ error)


GET /api/v1/transactions

ลิสต์ผลตรวจสลิปของร้านย้อนหลัง — ใหม่สุดก่อน แบ่งหน้าแบบ cursor รวมทุกช่องทาง (API / LINE / Dashboard) เหมาะกับตอน POST /verify หลุดกลางทาง (timeout/เน็ตตัด) แล้วอยากรู้ผลจริงโดยไม่ต้องส่งสลิปซ้ำ

MethodGET
Scope ที่ต้องมีread
Querystatus · source · transRef · from/to (ISO-8601) · cursor · limit (1-100, default 50)
curl "https://api.slip-bolt.com/api/v1/transactions?status=SUCCESS&limit=20" \
  -H "Authorization: Bearer sk_live_..."
{
  "success": true,
  "data": {
    "items": [ { "id": "8f3c…", "status": "SUCCESS", "verified": true, "…": "…" } ],
    "nextCursor": "7ab1…"
  }
}

ส่ง nextCursor กลับมาเป็น cursor เพื่อดึงหน้าถัดไป · null = หมดแล้ว

GET /api/v1/transactions/:id

ดึงผลตรวจสลิป 1 รายการ — ใช้คู่กับ webhook (รับ data.id จาก event แล้วมาดึงรายละเอียดเต็ม) รายการของร้านอื่น หรือ id ที่ไม่มีอยู่ → 404 เหมือนกัน (ไม่ยืนยันว่า id นั้นมีอยู่จริงในระบบ)

curl https://api.slip-bolt.com/api/v1/transactions/8f3c2a1e-… \
  -H "Authorization: Bearer sk_live_..."

Scopes

แต่ละ key ถือ scope ได้หลายตัว — endpoint ต้องการ scope ครบทุกตัวที่ระบุ (ตั้งค่าได้ที่ Settings → API Keys)

Scopeใช้กับ
verifyPOST /api/v1/verify (default ของทุก key)
readGET /api/v1/quota · GET /api/v1/me · GET /api/v1/transactions[/:id]
webhookสำรองไว้สำหรับอนาคต — ตอนนี้ยังไม่มี public endpoint ใช้งาน scope นี้ (ตั้งค่า outgoing webhook ทำผ่าน Dashboard → Settings → Webhooks เท่านั้น ไม่ใช่ API key)

scope ไม่พอ → 403 Forbidden (API key missing scope(s): …)


Rate limit

120 req/min ต่อ key (เท่ากันทุกแพ็ก) + เพดานรวมของ POST /api/v1/verify ที่ 240 req/min เกินแล้ว → 429 Too Many Requests + header Retry-After · ดู Authentication


Transaction status

statusverifiedความหมาย
SUCCESSสลิปถูกต้อง โอนเข้าบัญชีร้าน
DUPLICATEเลขอ้างอิง (transRef) นี้เคยตรวจแล้ว
AMOUNT_MISMATCHยอดบนสลิปไม่ตรงเงื่อนไข
RECEIVER_MISMATCHโอนเข้าบัญชีที่ไม่ใช่ของร้าน
INVALIDอ่านสลิปไม่ได้ / ไม่ใช่สลิป / สลิปไม่ใช่รายการล่าสุด (errorCode: STALE_SLIP)
FRAUDสลิปปลอม/แก้ไข — ไม่ตรงข้อมูลธนาคาร
QUOTA_EXCEEDEDโควตาหมด (ดู 402 ด้านบน)
PROVIDER_ERRORผู้ให้บริการตรวจสลิปขัดข้องชั่วคราว — ลองใหม่

Idempotency

ระบบ dedupe ให้อัตโนมัติ — สองเคสที่ต่างกัน:

  • ส่ง "รูปเดิม" ซ้ำภายใน 24 ชม.cached: true · status/verified เป็นผลจริงของ รายการเดิม (retry ตอนเน็ตตัด/timeout ได้อย่างปลอดภัย — ไม่ตรวจซ้ำ ไม่หักโควตา)
  • transRef เดียวกัน เคยตรวจผ่านไปแล้ว (คนละไฟล์รูป/ถ่ายซ้ำ) → duplicate: true และ status จะเป็น "DUPLICATE" เสมอในการตอบครั้งนี้ (verified: false) — ไม่ว่าผลตรวจ ครั้งแรกจะเป็นอะไรก็ตาม

ห้าม credit ออเดอร์จากแค่ verified === true — ต้องเช็ค duplicate ก่อนเสมอถ้าระบบคุณ เคยประมวลผล transRef นั้นไปแล้ว ไม่งั้นการ retry/ส่งซ้ำโดยไม่ตั้งใจจะ credit ซ้ำ


Error envelope

ทุก error รูปแบบเดียวกัน:

{
  "success": false,
  "error": "Forbidden",
  "message": "API key missing scope(s): read",
  "statusCode": 403,
  "path": "/api/v1/quota",
  "requestId": "req_abc123",
  "timestamp": "2026-06-17T10:30:45+07:00"
}
Statusเมื่อไร
400body ไม่ใช่ raw bytes / ไม่มีรูป
401API key ผิด/ถูก revoke
402โควตาหมด — QUOTA_EXCEEDED (key live) หรือ TEST_QUOTA_EXCEEDED (key test เกิน 200/เดือน)
403scope ไม่พอ หรือแพ็กปัจจุบันไม่รวม REST API (PLAN_API_ACCESS_REQUIRED)
404ไม่พบรายการ (GET /transactions/:id) — หรือเป็นของร้านอื่น
429เกิน rate limit

ถัดไป

  • Webhooks — รับ event slip.verified / slip.duplicate / slip.failed แบบ HMAC-signed แทนการ poll
  • Authentication — รายละเอียด key · โควตาต่อแพ็ก · rate limit