Changelog
SlipBolt API changelog — new features, fixes and breaking changes developers should know before updating
New features and changes that affect integrators · newest first
We try hard to avoid breaking changes · if one is needed we announce it in advance and version it
October 2026
More reliable integrations — additive only, everything that worked keeps working
- New: the
Idempotency-Keyheader onPOST /verify— retrying with the same key returns the original answer (Idempotent-Replayed: true), no second verify, no second charge ·409 IDEMPOTENCY_IN_PROGRESS·422 IDEMPOTENCY_KEY_REUSED· MCPverify_slipacceptsidempotencyKey - Webhooks: 6 attempts over ~7 h (was 5 over ~30 s) · new headers
X-Slipbolt-Event-Id+X-Slipbolt-Delivery-Attempt· an Events table under Settings → Webhooks with Resend · email/LINE alerts to the shop owner when deliveries fail · endpoints auto-disable only after more than 3 days of continuous failure (was 25 failed attempts) - New: rotate a webhook secret with the old one still signing for 24 h — the header carries two
v1values meanwhile · the signature samples now accept anyv1 - New: per-key charge caps + per-key IP allowlist —
429 SPEND_CAP_EXCEEDED·403 IP_NOT_ALLOWED·GET /menow hasapiKey.spendCap+apiKey.ipAllowlist - New:
GET /api/v1/health·GET /api/v1/ready— no key needed, for uptime monitors - A wrong method on an existing path (e.g.
GET /verify) now answers405with anAllowheader instead of404· the root ofapi.slip-bolt.comand unknown paths answer in JSON - MCP: a JSON-RPC batch (array) now gets
400with error-32600instead of an empty202 - New:
POST /verify/async— answers202immediately; the result arrives via theverification.completedwebhook or by pollingGET /verifications/{id}· identical results toPOST /verify· new MCP toolget_verification+verify_slipacceptsasync: true - Rate-limit
429now carriescode: "RATE_LIMITED"· body over 5 MB →413JSONPAYLOAD_TOO_LARGE· URL too long →414JSON ·api.slip-bolt.com/openapi.jsonserves the spec directly (200) instead of redirecting · MCP{}now gets-32600(was-32700) - Webhooks: the first attempt to far-away endpoints (Europe/US) failed about half the time and waited for a retry — fixed ·
slip.duplicatealways carriesdata.status: "DUPLICATE"·verification.completedlatencyMsmatches REST · no moresentry-trace/baggageheaders - Webhooks:
data.transRefisnullfor results with no readable reference (an internalerr:…value used to leak) — matchesGET /transactions
Clearer slip results (from an MCP UAT) — additive fields only, existing code is unaffected
warnings— warnings on a passing slip (STALE_SLIP), separate from failure reasons ·errorCodeis unchangedreceiverProxyAccount— the PromptPay id shown on the slip image, next toreceiverAccount(the linked bank account)quotaCharged(POST /verifyonly) — whether this request used quota/credits, no need to infer it fromquota.used- Fixed: a slip sent through the API that happened to pay SlipBolt's own billing account returned
SUCCESSbut was not recorded (missing fromGET /transactions· no quota charge · noreceipt) — it is now checked against the shop's accounts as usual - Creating an API key with the same name as an active key is now rejected (
409) — keys stay distinguishable by name
New: MCP server for AI tools — https://docs.slip-bolt.com/mcp
- Connect Claude Code · Cursor · Claude Desktop and let the AI search/read these docs (no key needed) · check quota · look up past slips · fetch receipts · verify slips
- Uses your existing shop key in the
Authorizationheader — exactly the REST API's permissions; aread-only key is recommended unless the AI must verify new slips - Copy-paste setup: MCP for AI tools
New: verification receipts — one design across the LINE bot · LIFF · API
GET /api/v1/transactions/:id/receipt.png·receipt.pdf— the receipt image/PDF for a passed slip (?maskSender=truemasks the sender's name)- Results gain a
receiptfield (code·checkUrl) — additive only, existing code is unaffected - The QR on the receipt opens a check page on
slip-bolt.comso anyone can confirm SlipBolt really issued it (guards against edited images)
Fuller API docs + a more detailed OpenAPI spec (from an integrator's review)
- API Reference adds: a complete
errorCode/codetable · a Stale slip section · a Bank codes table (3-digit code ↔ slug in/me) · the status table now says whether quota is charged and which webhook event fires · exampleDUPLICATE/RECEIVER_MISMATCH/INVALID/FRAUDresults · theX-RateLimit-*headers - Clarified:
errorCodeis not alwaysnullonSUCCESS— a genuine slip transferred more than 24 hours ago getsSUCCESS+errorCode: "STALE_SLIP"(behaviour unchanged since September — the docs used to sayINVALID) - The OpenAPI spec now has
components.schemas(VerifyResult·Transaction·Quota·Account·ErrorEnvelope…) and declares response headers and5xx - New: a Postman collection at
/api/v1/postman.json— generated from the same OpenAPI spec ·https://api.slip-bolt.com/openapi.jsonat the domain root now redirects to the spec - New: an
X-Request-Idheader on every response — the same value asmeta.requestId/requestIdin an error - Changed:
{ "qr": … }holding a payment QR (PromptPay — text starting000201) now gets400instead of being sent for verification and coming back asFRAUD· real slip QRs are unaffected - Changed:
errorMessageonPROVIDER_ERRORis now a fixed neutral message — it used to be internal text never meant to be read · keep branching onstatus/errorCode
Fixed: resending a slip that had answered DUPLICATE could return SUCCESS
- A slip already used through LINE / the Dashboard, sent to
POST /api/v1/verifytwice in a row, used to getverified: true·duplicate: falsefrom the image cache on the second call — it is nowDUPLICATEevery time (duplicate: true·cached: true) - A retry of a request the API itself verified first still returns the original result · see Idempotency
POST /api/v1/verify accepts the slip in 5 ways — it used to take raw bytes only
- Adds JSON
{ "url": "https://…" }(we download the image) and{ "qr": "<QR text>" }(no image needed) multipart/form-data(a file in thefilefield) and JSON{ "image": "<base64>" }now work · raw bytes may also be sent withContent-Type: image/jpeg/image/png- The result, the quota and the 24-hour cache are identical for all of them · existing code that sends
application/octet-streamneeds no change - Examples in every language: Recipes · the OpenAPI spec declares every form
New page: Recipes — copy-paste code for Node.js / Python / PHP / Go / Java / C#
- A client with a timeout + retries (
429/5xx/ network failure) · taking a slip from a customer and closing the order end to end · sending the image as multipart / base64 / URL · a script that signs a webhook so you can test on localhost - API Reference adds a field table for the verify result and a "Retry?" column in the error table
- New guidance: 4 checks before releasing an order (was 3) — adds "
transRefUNIQUE on your side", because resending the same image within 24 hours returns the original result withduplicate: false· API behaviour is unchanged
September 2026
Documentation in English
- The developer docs are now fully translated — every page on
docs.slip-bolt.com/en/docs
Slip credits (pay as you go)
- Order in which a passed slip is charged: monthly plan quota → credits → per-slip overage · credits are valid for 12 months
GET /api/v1/quotaaddscreditsAvailable·GET /api/v1/meadds acreditsblock (available·usedThisMonth·nextExpiry) —usedis still the count against the plan quota, as before- The
402 QUOTA_EXCEEDEDmessage now mentions credits — it only fires when both quota and credits are used up · branch oncode, not on the message text
Error bodies now match the docs
- Every error has
success: false·path·requestId·timestampper the Error envelope (previously some production errors only hadmessage/error/statusCode) · the existingcodefield is unchanged ·429carries aRetry-Afterheader
Webhooks: alerts straight into Discord / Telegram — see Webhooks
- Destinations must be public
https://· redirects aren't followed · fake slips arrive as a separateslip.fraudevent
The 7-day trial = Trial-plan entitlements only
- A shop's first unpaid period is always on the
TRIALplan (50 slips · 1 seat · 1 group · 1 OA · no REST API) - Picking the Pro/Business card at signup does not give 7 free days of paid-plan limits
GET /api/v1/meandGET /api/v1/quota(scoperead) read the plan + remaining quota without using a slip — use them to check at integration boot
System status — endpoints monitors can actually call
GET /health·/health/liveness·/health/uptimenow answer at these exact paths (previously only/v1/healthexisted — an uptime monitor pointed at/healthas documented got404)- Added
GET /health/ready(readiness — the same DB + Redis check asGET /health) for monitors that use the liveness/readiness naming - Use
/health*only —/v1/health*is not public onapi.slip-bolt.com(answers404) · if your monitor points at/v1/health, change it to/health - The status page now reads real values from these endpoints
Anti-fraud — recycled old slips: on by default
SLIP_STALE_MODEdefault changed fromoff→alert· the slip is stillSUCCESS(witherrorCode: "STALE_SLIP"on the result), but the shop owner is alerted when the bank time on the slip is older thanSLIP_STALE_MAX_AGE_HOURS(default 24 hours)alertmode never rejects a payment · to actually reject, setSLIP_STALE_MODE=reject
August 2026
Public API
- The plan's
apiAccessis now enforced — API keys of shops on a plan without API access are rejected (previously the column was written but never read) · if your integration used to pass on a small plan, check the plan first - Responses no longer leak the name of the external slip-verification provider (body · errors · headers)
- Finer API key management — scopes · usage · rotate/revoke
Webhooks & Realtime
- The Webhooks page shows the delivery history for every attempt, with status and retry round
- "Send test" gives a clear message when the job queue isn't ready (previously a bare HTTP 500)
- The dashboard shows new slips the moment they're verified via SSE — no refresh needed
Billing & quota
- Overage is charged for real, with a free buffer · a monthly cap · and a hard ceiling that blocks when usage runs many times over
- Invoices that include overage show where that amount came from on its own line
- Plan changes show a quote before charging (proration is visible before you confirm)
- LINE alerts when a card is declined · when renewal fails · and when payment succeeds
- Choose how to pay an outstanding bill yourself (card / PromptPay)
Team · security
- Team seats per plan + email invitations
- 2FA (TOTP) with backup codes · enable at Settings → Security
- Password policy enforced for real · CSP and a full set of security headers added
- Audit log viewable from the settings page
LINE & LIFF
- LIFF can post the verification result back into the group the customer sent from
- Quick replies on shop-owner commands
- Slips are no longer swallowed when Redis is full — the customer is asked to resend instead of silence
App
- Switch shops without reloading + command palette (⌘K)
- Every list is paginated · exports include the whole set, not just the visible page
- Thai/English across the whole app and docs chrome
- Product tour for the dashboard and linking a LINE channel
- Cookie consent + analytics that wait for consent
July 2026
- LIFF — manage groups · verification history · receipts · notification settings · link/unlink accounts, all inside the mini-app
- Shop status shown clearly in the sidebar (trial · active · past due · suspended)
- Close any modal with Esc · copy buttons report real success
June 2026
Public API — Quota UX
- Every
POST /api/v1/verifyresponse carriesX-Slipbolt-Quota-{Limit,Remaining,Used,Reset-Days}headers + aquotafield in the body - Quota used up (
livekey) → HTTP 402QUOTA_EXCEEDEDinstead of a silent200— check the HTTP status directly - Added
GET /api/v1/quota(scoperead) to read the balance without spending a verify - Confirmed: API calls use the same quota pool as the shop's LINE slips
Anti-fraud — recycled slip detection
- Detects genuine but "old/reused" slips from the bank time (
transDate) ·alert/rejectmodes configurable ·rejectmode answersINVALID+errorCode: STALE_SLIP·alertmode answersSUCCESSwith the sameerrorCode— off by default at the time (see September 2026)
LINE
- Rich Menu set up automatically when a channel is linked (Today's total → LIFF)
- Marketing broadcast to opted-in shop owners (platform side)
Billing
- Hardened Stripe webhook handling (retries only money-related events · idempotent)
May 2026
- Public Verify API —
POST /api/v1/verifytakes the slip image as raw bytes, authenticated with an API key (sk_live_/sk_test_) - Outgoing Webhooks —
slip.verified/duplicate/failedevents · HMAC-signed · 5 retries - API Keys — create/rotate/revoke · scopes (
verify/read/webhook) · usage metrics - Prepaid terms — pay 1/3/6/9/12 months ahead (card + PromptPay) with tiered discounts
- Dashboard — totals · reports · transactions · manage LINE channels/groups
Related docs
- API Reference — every endpoint
- Webhooks — events + signature
- Security & PDPA
