Developer docs
Webhooks
Subscribe an HTTPS endpoint to live events from your shop — appointments, invoices, payments, warranties. Each delivery is HMAC-SHA256 signed.
Quick start
- Open Settings → Webhooks in the SalesThumb app.
- Click New subscription.
- Enter a label, your HTTPS URL, and pick the events you care about.
- Save the signing secret — it's shown once.
- Click Send test to verify your endpoint receives + verifies the request.
Payload envelope
Every event is wrapped in the same envelope. Thedata field varies by event type.
{
"event": "invoice.paid",
"shopId": "01234567-89ab-cdef-0123-456789abcdef",
"timestamp": "2026-04-26T14:23:11.000Z",
"data": {
"invoiceId": "01234567-...",
"customerId": "01234567-...",
"totalCents": 84900,
"paidAt": "2026-04-26T14:23:11.000Z"
}
}Language: jsonHeaders on every request:
Content-Type: application/json
User-Agent: SalesThumb-Webhook/1.0
X-SalesThumb-Event: invoice.paid
X-SalesThumb-Signature: sha256=<hex digest>
X-SalesThumb-Delivery: <unique uuid per attempt>Language: textSignature verification
Verify the signature before trusting a payload. The signature isHMAC-SHA256(secret, raw_body). Compare in constant time.
Node.js
import crypto from "node:crypto";
export function verifyWebhook(secret, header, rawBody) {
const expected = crypto
.createHmac("sha256", secret)
.update(rawBody)
.digest("hex");
const provided = header.replace(/^sha256=/, "");
return crypto.timingSafeEqual(
Buffer.from(expected, "hex"),
Buffer.from(provided, "hex"),
);
}Language: javascriptPython
import hmac, hashlib
def verify_webhook(secret: str, header: str, raw_body: bytes) -> bool:
expected = hmac.new(
secret.encode(), raw_body, hashlib.sha256
).hexdigest()
provided = header.replace("sha256=", "", 1)
return hmac.compare_digest(expected, provided)Language: pythonRuby
require "openssl"
def verify_webhook(secret, header, raw_body)
expected = OpenSSL::HMAC.hexdigest("SHA256", secret, raw_body)
provided = header.sub(/^sha256=/, "")
Rack::Utils.secure_compare(expected, provided)
endLanguage: rubyEvent catalog
All event types you can subscribe to. More are added as new modules ship — see /changelog for additions.
appointment.created
A new appointment was booked.
appointment.confirmed
An appointment moved to CONFIRMED status.
appointment.checked_in
Customer checked in for an appointment.
appointment.completed
An appointment was marked complete.
appointment.canceled
An appointment was canceled or no-showed.
quote.sent
A quote was sent to a customer.
quote.accepted
A customer accepted a quote.
invoice.created
A new invoice was created.
invoice.sent
An invoice was sent to a customer.
invoice.paid
An invoice was paid in full.
payment.received
A payment was recorded against an invoice.
payment.refunded
A payment was fully or partially refunded.
warranty.registered
A warranty was registered for an install.
warranty.claim_filed
A customer filed a warranty claim.
customer.created
A new customer record was created.
Retries & auto-disable
The platform considers any non-2xx response a failure. Failures are recorded but not retried automatically yet— this is on the immediate roadmap (exponential backoff over ~24h). For now, your endpoint should be idempotent and queue work internally if it can't process synchronously.
After 10 consecutive failures, the subscription is auto-disabledand you'll be notified by email. Re-enable it from the dashboard once you've fixed the endpoint.
Timeout: 10 seconds. Long-running work should ack the webhook quickly (return 200 immediately) and process asynchronously.
Best practices
- Verify
X-SalesThumb-Signatureon every request before trusting the payload. - Treat
X-SalesThumb-Deliveryas an idempotency key — store it and skip duplicates. - Return 2xx fast. Queue heavy work for an async worker.
- Subscribe to the narrowest set of events you need.
- Rotate the signing secret periodically (90 days is reasonable).
FAQ
Is there a REST API?+
A documented public REST/RPC API is on the roadmap. For automation today, use webhooks (push) for events you want to react to and Zapier (pull-style triggers) for tools that don't accept webhooks directly. CSV export covers bulk reporting. Email info@roffik.com if you have a use case the webhooks don't cover and we'll prioritize accordingly.
Do you charge for webhook deliveries?+
No — outbound webhooks are unlimited on every plan. The only thing we measure is consecutive failures (auto-disable kicks in at 10 in a row to protect your endpoint).
Can I subscribe to events from multiple shops?+
Each subscription is scoped to a single shop. If you operate multiple shops on SalesThumb, create one subscription per shop or have your endpoint distinguish based on the shopId in the payload envelope.
How do I rotate the signing secret?+
From the subscription detail in the app. Rotation invalidates the old secret immediately, so deploy your secret update first, then rotate.
Is HMAC-SHA256 enough? Are you planning IP allowlisting?+
HMAC-SHA256 with a 32-byte secret is industry-standard for webhook authenticity (Stripe, GitHub, Twilio all use it). IP allowlisting is on the roadmap for shops that want defense-in-depth.
Ready to wire it up?
Create your first subscription in 30 seconds.
More developer docs