Webhooks

For nurseries

Send every new enquiry and show-round booking from Toddly to Zapier, Make or your own system as a signed POST. Part of Pro.

Last updated 14 November 2026

A webhook sends each event to an address you choose, the moment it happens, so a new enquiry can land in your spreadsheet, CRM or nursery management system without anyone copying it across.

Events

Type When
enquiry.created A parent sends an enquiry, visit request or callback request
visit.booked A show-round is booked: by the parent into one of your times, or by you from the enquiry page
visit.moved The parent moves their show-round to another time
visit.cancelled The show-round is cancelled, by the parent or by you
test You press “Send test event”

Set it up

  1. In your dashboard open Settings, then Webhook.
  2. Paste the https address your tool gives you and press Save.
  3. Copy the signing secret we show you. It is shown once only; you can make a new one at any time.
  4. Press Send test event. The last delivery and its result show under the address.

Only public https addresses are accepted: no IP addresses, no ports other than 443, nothing on a private network. Redirects are not followed.

Zapier

  1. Create a Zap and choose Webhooks by Zapier as the trigger, with the event Catch Hook.
  2. Copy the webhook URL Zapier shows and paste it into Toddly under Settings, Webhook.
  3. Press Send test event in Toddly, then Test trigger in Zapier. Zapier now knows the fields: map data.enquiry.parent_name, data.enquiry.parent_email and the rest into your next step.

Make

  1. Create a scenario and add the Webhooks module Custom webhook. Press Add and copy the address Make creates.
  2. Paste it into Toddly under Settings, Webhook.
  3. Press Send test event in Toddly while Make is listening, so it learns the data structure.

What we send

Each event is a POST with a JSON body:

{
  "id": "evt_6f1c9a0d2b7e4c3a8f5e1d20",
  "type": "enquiry.created",
  "created": 1789466400,
  "nursery_id": "eng-ey490982",
  "data": {
    "enquiry": {
      "id": 1234,
      "type": "visit",
      "child_age_months": 20,
      "start": "2027-01",
      "days": ["mon", "wed"],
      "message": "Could we look round on a Tuesday morning?",
      "parent_name": "Sam Jones",
      "parent_email": "[email protected]",
      "parent_phone": "+447700900123",
      "visit_at": "2026-11-17T10:00:00Z",
      "link": "https://toddly.co.uk/dash/eng-ey490982/enquiries/1234"
    },
    "visit": {
      "id": 56,
      "starts_at": "2026-11-17T10:00:00Z",
      "status": "booked"
    }
  }
}
  • visit is there on visit.* events; visit.moved adds previous_starts_at.
  • start is a month (2027-01), asap or not_sure. Times carry their UK offset. A field the parent left out is left out or empty, never a guess.
  • id is the same on every retry of one event: use it to ignore a repeat.

The body holds the same details as your enquiry email. Your nursery is the controller of them once they reach you, and the tools you send them to work for you.

Check the signature

Every request has a header:

X-Webhook-Signature: t=1789466400,v1=5f2b…

v1 is the HMAC-SHA256 of the text t, a full stop and the raw request body, keyed with your signing secret, in hex. Recompute it, compare in constant time, and refuse a t more than five minutes old.

const crypto = require("crypto");

function verified(header, rawBody, secret) {
  const parts = Object.fromEntries(header.split(",").map((p) => p.split("=")));
  const want = crypto.createHmac("sha256", secret).update(parts.t + "." + rawBody).digest("hex");
  const fresh = Math.abs(Date.now() / 1000 - Number(parts.t)) < 300;
  return fresh && want.length === parts.v1.length &&
    crypto.timingSafeEqual(Buffer.from(want), Buffer.from(parts.v1));
}
import hashlib, hmac, time

def verified(header: str, raw_body: bytes, secret: str) -> bool:
    parts = dict(p.split("=", 1) for p in header.split(","))
    want = hmac.new(secret.encode(), parts["t"].encode() + b"." + raw_body, hashlib.sha256).hexdigest()
    fresh = abs(time.time() - int(parts["t"])) < 300
    return fresh and hmac.compare_digest(want, parts["v1"])

Zapier’s and Make’s catch hooks do not check signatures; the address they give you is long and unguessable, so keep it private.

Delivery

  • Answer with any 2xx status within 5 seconds. We do not read your response body.
  • Anything else, a redirect or no answer is a failure. We try again after 1, 4, 16, 64 and 256 minutes, six tries in all.
  • After 20 failed deliveries in a row the webhook switches off and your team gets an email. Fix the address and send a test event to switch it back on.
On this page
  1. Events
  2. Set it up
  3. What we send
  4. Check the signature
  5. Delivery

Frequently asked questions

Which plan has webhooks?

Pro, and the 14-day Pro trial. On Free nothing is sent.

Do I still get enquiry emails?

Yes. The webhook is an extra copy for your other tools. Enquiries still reach you by email and in your dashboard.

What happens if my endpoint is down?

We try again over about six hours. After 20 failed deliveries in a row we switch the webhook off and email your team; a successful test event switches it back on.

Sign in to Toddly