intro

Getting started

Verify your first slip in 5 minutes — set up a shop, link LINE, check your quota, call the API (Starter and up)

⏱ 5 min

Everything you need to do for SlipBolt to verify your first slip

1. Create an account + shop

Go to app.slip-bolt.com/auth/signup and confirm your email, then name your shop during onboarding — a shop's first 7 days are always a trial (Trial-plan entitlements only, not a free Pro/Business)

During the trial you get:

  • 50 slips · 1 seat (the owner) · 1 LINE group · 1 LINE OA
  • Slip verification through LINE OA + Dashboard
  • No REST API / Webhooks — API keys become available once you upgrade to Starter or higher

Your remaining quota is shown right on the Dashboard (the quota tile) — no API key needed

2. Add a receiving account

Settings → Shop → Receiving accounts — if a shop has no account for customers to pay into, every slip is rejected (NO_PAYEE_CONFIGURED) before it is sent for verification

At Settings → LINE Channels, paste the Channel ID + Secret + Access Token from LINE OA Manager, then invite the bot into a group and send one test slip

4. Create an API key (Starter plan and up)

At Settings → API Keys, create a new key — the full key is shown only once. Keys cannot be issued during the Trial (403 PLAN_API_ACCESS_REQUIRED)

Terminal
sk_live_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6

Copy it right away — we only keep an argon2 hash on the server (lose it and you have to create a new one)

5. Make your first call

Put the key in an environment variable first (never in code) — during development use an sk_test_ key; it does not consume the shop's quota

Terminal
export SLIPBOLT_API_KEY="sk_test_..."

Then send the slip image as raw bytes to POST /api/v1/verify — pick your language; the choice is remembered on every page

curl -X POST https://api.slip-bolt.com/api/v1/verify \
  -H "Authorization: Bearer $SLIPBOLT_API_KEY" \
  -H "Content-Type: application/octet-stream" \
  --data-binary "@slip.jpg"

Response (abridged — every field is in the API Reference):

JSON
{
  "success": true,
  "data": {
    "status": "SUCCESS",
    "verified": true,
    "duplicate": false,
    "amount": 50.0,
    "transRef": "20260525123045123456",
    "transDate": "2026-05-25T05:30:45.000Z",
    "senderName": "นายสมชาย ใจดี",
    "bankName": "ธนาคารกสิกรไทย",
    "receiverName": "ร้านกาแฟพี่ตุ๊ก",
    "environment": "test",
    "quota": { "used": 1, "limit": 500, "remaining": 499 }
  },
  "meta": { "requestId": "3b1f0c9e-6a2d-4f5e-8b7c-1d2e3f4a5b6c", "timestamp": "2026-05-25T05:30:46.412Z" }
}

Names come back exactly as printed on the slip — for Thai banks that is usually Thai script.

When is it safe to release the order — 4 checks

  1. data.verified === true — a genuine slip, paid into the shop's account
  2. data.duplicate === false — not a slip that has already been used (a customer resending an old slip)
  3. data.amount matches your order total — the API does not know how much this order should cost; you have to compare it yourself (a customer can transfer 1 THB and send that slip — the slip is still "genuine")
  4. data.transRef is not yet attached to another order in your system — store transRef in a UNIQUE column (resending the same image within 24 hours returns the original result with duplicate still false ⇒ checks 1–3 cannot stop one slip closing two orders)

Complete code that runs all 4 checks, with retries and an upload page, is in Recipes

Don't accept old slips? A genuine slip transferred more than 24 hours ago still gets verified: true, but carries errorCode: "STALE_SLIP" — if your business needs to reject those, check that field as well (see Stale slip)

An OpenAPI spec you can import

https://api.slip-bolt.com/api/v1/openapi.json — open it in Postman / Insomnia / Bruno to try every endpoint right away, or generate a client in another language (Go · Java · C# …) with openapi-generator · the full path has the /api/v1/ prefix — https://api.slip-bolt.com/openapi.json at the domain root redirects to the same place

Ready-made Postman collection: https://api.slip-bolt.com/api/v1/postman.json — import it, set the apiKey variable and send · POST /verify comes as 5 separate requests (raw file · multipart · base64 · url · qr)

Copy-paste POST /verify in Node.js · Python · PHP · Go · Java · C# is in Recipes

The result is always under data — every successful response is wrapped in { success, data, meta }. Reading response.verified directly gives undefined (the most common mistake on a first integration)

API calls use the same quota as the shop's LINE slips · check the balance via quota in the response or the X-Slipbolt-Quota-Remaining header · quota used up → 402 (see API Reference) · use an sk_test_ key to test without consuming the shop's quota (separate cap of 200 calls/month)

6. Check your remaining quota (uses no slips)

ChannelUse whenWhat you get
Dashboard → quota tileEvery plan, including Trialused / limit / days until reset
GET /api/v1/quotaYou have an API key + read scopeThe shop's quota only
GET /api/v1/meYou have an API key + read scopeShop · key · plan · live+test quota · receiving accounts (last 4 digits)
Terminal
curl https://api.slip-bolt.com/api/v1/me \
  -H "Authorization: Bearer sk_live_..."

Field details are in API Reference — GET /me

7. Receive Webhooks (optional)

Subscribe at /settings/webhooks and the system POSTs an event every time there is a new slip result:

JSON
{
  "event": "slip.verified",
  "id": "evt_9f3c1a7b2d4e6f8a0b1c2d3e",
  "created": 1781107927,
  "data": {
    "transactionId": "8f3c…",
    "transRef": "20260525123045123456",
    "amount": 50.0,
    "status": "SUCCESS",
    "sender": "นายสมชาย ใจดี",
    "receiver": "ร้านกาแฟพี่ตุ๊ก",
    "verifiedAt": "2026-05-25T05:30:46.412Z",
    "latencyMs": 480
  }
}

Verify the signature every time — the X-Slipbolt-Signature header looks like t=<unix>,v1=<hex> where v1 = HMAC-SHA256(secret, ts + "." + raw_body) (ts = the number after t=) · the signed string has no t= prefix and must use the raw body — ready-to-use code is at Webhooks

Next steps