core

Authentication

Bearer JWT (dashboard) vs API key (server-to-server) — เลือกใช้แบบไหนเมื่อไร

SlipBolt มี 2 ชนิด credential — ใช้แยกตามบทบาท

Bearer JWT (Dashboard / LIFF)

ผู้ใช้ login ที่ app.slip-bolt.com แล้วได้ JWT access token (TTL 15 นาที) + refresh cookie (sb_rt · 7 วัน · หมุนทุกครั้งที่ใช้)

HTTP
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
  • Refresh อัตโนมัติเมื่อ access token หมดอายุ → useApi composable จัดให้ทั้งหมด
  • เหมาะกับ browser / mobile app

API Key (Server-to-server)

สร้างที่ Settings → API Keys — รูปแบบ sk_live_<32 chars>

ต้องใช้แพ็ก Starter ขึ้นไป — ช่วงทดลองใช้ฟรี (Trial) ออก API key ไม่ได้ และคีย์ที่ออกไว้ตอนอยู่แพ็กที่จ่ายเงิน จะถูกปฏิเสธด้วย 403 ทันทีที่แพ็กหมดอายุ หรือร้านถูกพักใช้งาน (ตรวจทุกคำขอ ไม่ใช่แค่ตอนออกคีย์)

HTTP
Authorization: Bearer sk_live_a1b2c3d4...
  • ไม่หมดอายุ จนกว่าจะ revoke
  • สูงสุด 5 คีย์ที่ยังไม่ถูกเพิกถอนต่อร้าน — เพิกถอนใบที่ไม่ใช้ก่อนจึงจะออกใบใหม่ได้
  • มี scope จำกัด (default: verify · เพิ่ม read/webhook ได้ที่ Settings → API Keys) — ดู API Reference
  • เก็บใน server environment variable เท่านั้น อย่าใส่ในโค้ด frontend

สิทธิ์ต่อแพ็ก (โควตา · ที่นั่ง · LINE · API)

ร้านแรกช่วงทดลอง 7 วันได้สิทธิ์แถว Trial เท่านั้น — ไม่ใช่แพ็กที่เลือกตอน onboarding

Planสลิป/เดือนที่นั่งกลุ่ม LINELINE OAREST API
Trial50111❌
Starter500113✅
Pro1,5005310✅
Business5,000ไม่จำกัด10ไม่จำกัด✅

โควตาสลิปเป็น pool เดียวกับสลิปที่เข้าทาง LINE — ใช้ช่องไหนก็หักจากก้อนเดียวกัน อ่านยอดคงเหลือได้ที่ GET /api/v1/quota · GET /api/v1/me หรือ header X-Slipbolt-Quota-* (ดู API Reference)

เครดิตสลิป (จ่ายตามใช้)

ร้านที่ซื้อ เครดิตสลิป ไว้ — โควตาแพ็กเดือนนั้นหมดแล้ว ระบบหักเครดิตก่อน แล้วค่อยคิดค่าส่วนเกิน · ใช้ได้ทั้งสลิปจาก LINE และ POST /api/v1/verify (นับเฉพาะใบที่ตรวจผ่าน เหมือนโควตา) · 402 QUOTA_EXCEEDED จะมาก็ต่อเมื่อทั้งโควตาและเครดิตหมด

  • ยอดเครดิตคงเหลือ: creditsAvailable ใน GET /api/v1/quota และ credits.available ใน GET /api/v1/me
  • ตรวจได้อีกทั้งหมด ≈ remaining + creditsAvailable
  • ร้านที่มีแต่เครดิต (ไม่มีแพ็กรายเดือน) ได้สิทธิ์แถว Trial — ไม่มี REST API คีย์จะได้ 403

sk_test_ — เลนทดสอบ

คีย์ที่ขึ้นต้นด้วย sk_test_ ไม่กินโควตาสลิปรายเดือนของร้าน ใช้ต่อ integration ได้โดยไม่เปลืองของที่จ่ายเงินมา · response มี "environment": "test" กำกับ

เลนทดสอบมีเพดาน 200 ครั้ง/ร้าน/เดือน — เพราะมันยังตรวจสลิปกับธนาคารจริงทุกครั้ง (ไม่ใช่ของจำลอง) เกินเพดานได้ 402 พร้อม code: "TEST_QUOTA_EXCEEDED" (สถานะเดียวกับตอนโควตาของร้านหมด — ทางออกคือย้ายไปคีย์ sk_live_) · นับทุกคำขอ ที่ยิงเข้ามา ไม่ใช่เฉพาะที่สำเร็จ เพราะค่าใช้จ่ายเกิดตั้งแต่ตอนยิง

Rate limit (เท่ากันทุกแพ็ก)

120 req/min ต่อ 1 API key — นับแบบ fixed window ราย 1 นาที และไม่ได้แยกตามแพ็ก มีเพดานรวมของ endpoint POST /api/v1/verify อีกชั้นที่ 240 req/min

เกิน → 429 Too Many Requests + header Retry-After: <seconds>

ทุกคำตอบมี header X-RateLimit-Limit / -Remaining / -Reset ด้วย — แต่เป็นตัวนับ ต่อ IP ของ endpoint นั้น ไม่ใช่ยอดคงเหลือของคีย์ (รายละเอียด: API Reference)

ต้องการเพดานสูงกว่านี้ ติดต่อทีมงาน — ปรับได้ที่ฝั่งเซิร์ฟเวอร์ (API_KEY_RATE_LIMIT_PER_MIN) ไม่ใช่การอัปเกรดแพ็ก

เพดานต่อคีย์ และ IP ที่อนุญาต

ตั้งได้ที่ Settings → API Keys → ไอคอนโล่ ของแต่ละคีย์ · ไม่ตั้ง = เหมือนเดิมทุกอย่าง · ทุกการแก้ถูกบันทึกใน Audit log

เพดานการหักโควตา — กันคีย์รั่วหรือโค้ดวนลูปเผาโควตา/เครดิตของร้าน (rate limit 240/นาที ยังเท่ากับหลายแสนครั้งต่อวัน)

  • ตั้ง สูงสุดต่อชั่วโมง และ/หรือ ต่อวัน (วัน = 00:00–24:00 เวลาไทย) · ใช้กับคีย์ sk_live_ เท่านั้น
  • นับเฉพาะคำขอที่ หักโควตาจริง (quotaCharged: true) — สลิปไม่ผ่าน · DUPLICATE · cached ไม่นับ
  • ถึงเพดาน → 429 code: "SPEND_CAP_EXCEEDED" + Retry-After ถึงต้นรอบถัดไป + spendCap: { window, limit } — ต่างจาก rate limit: อย่า retry วน รอรอบใหม่หรือให้เจ้าของร้านปรับเพดาน
  • นับแบบจองก่อนตรวจ ⇒ ยิงพร้อมกันกี่คำขอก็ไม่เกินเพดาน (คำขอที่กำลังตรวจอยู่กินที่ชั่วคราว จึงอาจถูกปฏิเสธก่อนถึงเพดานจริงเล็กน้อยตอนใกล้เต็ม)
  • เจ้าของร้านได้อีเมล/LINE เมื่อใช้ถึง 80% และ 100% (ครั้งเดียวต่อรอบ) · ดูเพดาน + ยอดที่ใช้ไปได้จาก GET /api/v1/me → apiKey.spendCap

IP ที่อนุญาต — คีย์ที่รั่วไปใช้จากเครื่องอื่นไม่ได้

  • ใส่ IPv4 · IPv6 · CIDR (เช่น 203.0.113.10 · 198.51.100.0/24 · 2001:db8::/32) สูงสุด 20 รายการ · ว่าง = ทุก IP
  • IP อื่น → 403 code: "IP_NOT_ALLOWED" (แยกจาก 403 เรื่อง scope ที่ไม่มี code)
  • เราใช้ IP ที่ Cloudflare ยืนยัน — ส่ง header X-Forwarded-For เองไม่มีผล · หน้าตั้งค่าโชว์ IP ล่าสุดที่ใช้คีย์ไว้ให้กดเพิ่ม
  • มีผลกับเครื่องมือ MCP ด้วย (IP ของเครื่องที่รัน AI) · อย่าตั้ง ถ้าเซิร์ฟเวอร์ของคุณ IP ไม่คงที่ (serverless ฯลฯ)

Error responses

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

JSON
{
  "success": false,
  "error": "Unauthorized",
  "message": "Invalid or revoked API key",
  "statusCode": 401,
  "path": "/api/v1/verify",
  "requestId": "3b1f0c9e-6a2d-4f5e-8b7c-1d2e3f4a5b6c",
  "timestamp": "2026-05-25T05:30:45.120Z"
}

401 มีสองข้อความ — แยกได้ว่าพลาดตรงไหน:

messageสาเหตุ
Missing API key — send "Authorization: Bearer sk_live_…"ไม่มี header Authorization หรือไม่ได้ขึ้นต้นด้วย Bearer
Invalid or revoked API keyคีย์พิมพ์ผิด · ไม่ครบ · หรือถูกเพิกถอนแล้ว (มีผลทันที ไม่มีช่วง cache)

ส่งคีย์ได้ทางเดียวคือ Authorization: Bearer … — header X-API-Key และ Basic auth ใช้ไม่ได้ (ได้ 401 ข้อความแรก)

ฟิลด์ code (เช่น QUOTA_EXCEEDED) มีเฉพาะบาง error — ตาราง error ทั้งหมดอยู่ที่ API Reference