Developers
Webhooks & integrations
Push spinbook events (inquiries, contracts, payments, planning) to Make.com, n8n, or Zapier with token-authenticated webhooks, keeping Monday and your other tools in sync.
Updated Mon Sep 21 2026 00:00:00 GMT+0000 (Coordinated Universal Time)
spinbook can POST a JSON event to your own endpoints whenever something happens: a new inquiry, a signed contract, a payment, a completed planning form, an updated event. Point those at Make.com, n8n, or Zapier and spinbook becomes the source of truth while your other tools (like Monday.com) stay in sync automatically. No double entry.
Set one up
In Settings → Webhooks & integrations, add your endpoint URL (e.g. a Make.com custom
webhook). spinbook generates a secret token for that endpoint. It's sent in the
X-SpinBook-Token header on every request, so your receiver can require it and
reject anything that doesn't match. Use Send test to fire a ping and confirm it works;
recent deliveries (with HTTP status) show at the bottom of the page.
When you add an endpoint you can also pick which events it receives. Leave every box unchecked and it gets everything. Subscriptions are locked in at creation; to change them, add a new endpoint with the events you want and delete the old one. That's deliberate: the contract between spinbook and your automation shouldn't drift silently.
Events
By default an endpoint receives all event types; branch on the type field in your
automation. An endpoint created with specific subscriptions receives only those.
| Event | Fires when… |
|---|---|
inquiry.created | someone submits your booking form |
booking.updated | booking-form answers are edited (full details in the payload) |
contract.signed | a contract is e-signed |
payment.requested | a payment request is created (added by you, or auto-generated with the schedule). Carries the amount, due date, and couple contact so an automation can mint a checkout link (e.g. a Stripe Payment Link) and write it back via PATCH /api/v1/payments/{id} as pay_url; the couple then sees a Pay by card button |
payment.claimed | the couple says a deposit is sent via a self-reported method (Venmo / PayPal / Zelle) on the public pay page. Carries the method and any confirmation number; the payment isn't paid until the DJ confirms |
payment.paid | a payment is marked paid (in the app, or via the API) |
payment.updated | staff edits a payment from the edit screen: label, amount, due date, method, or confirmation number. Carries the new values plus a previous object (old label, amount, due date) so an automation can reconcile, e.g. update a Stripe Payment Link amount or fix a Monday money column |
planning.completed | the planning form is finished |
checklist.item_completed | a checklist item turns done: planning form, timeline, playlist, contract, payments, or a manual check. Fires once per item, on the moment it completes, with the item's binding, the couple's contact, and a booking snapshot (event date, venue, package, total) |
checklist.item_updated | the content behind an already-completed item changes: the couple revises their timeline, re-saves the planning form, re-syncs the playlist, or re-signs the contract. Carries a revision counter so your automation can post "Timeline revised, rev 7" instead of guessing |
event.updated | any event change (status, etc.); carries event_id |
call.booked | a guest books a call via call scheduling; carries the slot, guest details, and any matched client/event |
Both checklist events also carry doc_url for document-backed items (planning, timeline, contract, playlist): a signed link, valid for about an hour, that downloads the CURRENT version as a PDF. No API key needed. Need one later? GET /api/v1/events/{id} returns fresh doc_urls on every read. Point an HTTP module at it to file the document wherever your workflow lives (a Monday file column, Dropbox, email).
Payload
Each delivery is a JSON body:
{
"id": "…",
"type": "payment.paid",
"created_at": "2026-07-01T18:20:00.000Z",
"org_id": "…",
"data": { "event_id": "…", "payment_id": "…", "label": "Deposit", "amount_cents": 50000 }
}
With these headers:
X-SpinBook-Token: your endpoint's secret token (see below)X-SpinBook-Signature: an HMAC signature of the body (see below)X-SpinBook-Event: the event typeX-SpinBook-Delivery: a unique id for this delivery
Confirm it's really from spinbook
Every request includes your endpoint's secret token in the X-SpinBook-Token header:
X-SpinBook-Token: <your token>
On your receiver, check that this header matches your token and ignore anything that doesn't. Keep the token secret, use an HTTPS endpoint, and rotate the token any time from the settings page.
Verifying the signature (recommended)
Every delivery is also signed. If your tool can run a snippet of code, prefer this over the token: it proves the body wasn't altered in transit, and you never have to accept the secret itself on the wire.
X-SpinBook-Signature: t=1730000000,v1=3f8a…
t is the Unix time we sent it, and v1 is an HMAC-SHA256, in hex, of the string
`${t}.${rawBody}` keyed with your webhook token. Recompute it over the raw request
body. Parsing and re-serializing the JSON changes the bytes and the signature won't match.
import crypto from "node:crypto";
function verify(rawBody, header, token) {
const parts = Object.fromEntries(header.split(",").map((p) => p.split("=")));
const expected = crypto
.createHmac("sha256", token)
.update(`${parts.t}.${rawBody}`)
.digest("hex");
// Constant-time compare, so a timing side-channel can't leak the digest.
const ok = crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(parts.v1));
// Reject anything older than 5 minutes so a captured delivery can't be replayed.
const fresh = Math.abs(Date.now() / 1000 - Number(parts.t)) < 300;
return ok && fresh;
}
Sending a Test delivery from the settings page signs the ping exactly like a real event, so you can check your verification code without waiting for a booking.
Re-send a delivery
Each delivery in the Recent deliveries table has a Re-send button. It posts the original body again, byte for byte, with the same event id inside, so an idempotent receiver treats it as the same event. Use it when your automation was down or a scenario was mid-edit and the delivery was missed.
Two things to know:
- Re-sending a
payment.*event asks you to confirm first. If the receiving system isn't idempotent (it doesn't dedupe on the event id), replaying a payment can double count revenue there. - Delivery bodies are kept for 30 days, then cleared. Older rows stay in the log for debugging but can't be re-sent.
Make / n8n / Zapier
- Make.com: add a Custom webhook trigger, paste its URL into spinbook, and route on the
typefield (e.g.inquiry.created→ create a Monday item). To verify the token, add a step that only continues when the incomingX-SpinBook-Tokenheader equals your token. - n8n: a Webhook node; compare the
X-SpinBook-Tokenheader to your token. - Zapier: Catch Hook trigger; filter on the
X-SpinBook-Tokenheader +type.
Checklist
- Add your endpoint in Settings → Webhooks & integrations
- Pick the events it should receive, or leave all boxes unchecked for everything
- Send a test and confirm it arrives
- Require the
X-SpinBook-Tokenheader on your side (or verifyX-SpinBook-Signature) - Branch on
typeand map fields into your tool (Monday, etc.)