Webhooks: Send TidyCal Events to Your Own Server

Webhooks send signed booking, payment and contact events from TidyCal to your own server. Add one in Integrations → Automations; every plan can use them.

Overview

A webhook lets TidyCal tell your own app the moment something happens, such as a new booking or a cancellation. TidyCal sends a message to a web address you choose, and your app does whatever you want with it.

If you only want to connect TidyCal to other apps without writing code, use Zapier instead. Zaps that use TidyCal's instant triggers register a webhook for you, so you do not need this page for them. Each of those Zaps uses one of your 10 webhook slots.

What you need

  • A TidyCal account on any plan, including the Free Plan.
  • A web address (URL) on your server that can receive requests. It must start with https://, use port 443 or 8443, and be reachable from the public internet. Addresses on private networks, such as localhost or 192.168.x.x, are not allowed.
  • A little code on your server to read the message and check that it really came from TidyCal.

Add a Webhook

  1. Go to Integrations → Automations.
  2. Find the Webhooks card and click Add webhook.
  3. Enter your Endpoint URL. You can add a Description (optional) to remember what it is for.
  4. Under Send events for, choose what the webhook covers:
    • All my bookings and contacts covers your whole account.
    • A team covers the booking types in that team. You need to be the team owner or an admin.
    • One booking type covers a single booking type.
  5. Under Events, tick the events you want (listed below). If you do not tick any, TidyCal asks you to choose at least one.
  6. Click Add webhook.

Add webhook form with Endpoint URL, Description, Send events for and the Events checkboxes

Webhooks card with one enabled webhook, its event tags and Edit, Delivery log, Send test and Delete buttons

TidyCal now shows your signing secret under "Copy your signing secret now". Click Copy secret and keep it somewhere safe, such as your password manager or your server's settings. TidyCal shows it only once. If you lose it, use Rotate secret to get a new one.

You can have up to 10 webhooks, and each URL can be added once per account. A webhook's scope cannot be changed after you add it. To change it, delete the webhook and add a new one. A team webhook counts toward the team owner's 10.

Events You Can Choose

Event When it is sent
Booking created A booking is confirmed. For paid bookings, this happens after payment.
Booking cancelled A booking is cancelled.
Booking rescheduled A booking is moved to a new time.
Booking approved A booking that needed your approval is approved.
Payment completed A payment for a booking is completed.
Payment refunded A payment for a booking is refunded.
Contact created A new contact is added.
Store order completed A Store order is paid.
Subscription started A contact starts a booking subscription.
Subscription renewed A subscription renews.
Subscription cancelled A subscription is cancelled.

Only Booking created is ticked by default. Contact events carry no booking type, so only a webhook set to All my bookings and contacts receives Contact created.

What Your Server Receives

TidyCal sends a POST request with a JSON body. Every event has the same outer shape:

  • id is the unique ID of the event.
  • type is the event name, such as booking.created.
  • created_at is when the event happened.
  • data is the booking, contact or order itself. It holds a selected set of fields, never the booker's IP address or payment processor IDs. The full field list for each event is in the Webhooks section of the API documentation.

Each request also carries these headers:

Header What it holds
TidyCal-Event The event name, such as booking.created.
TidyCal-Event-Id The event ID, the same as id in the body. Use it to spot repeats.
TidyCal-Delivery-Id The ID of this webhook's delivery record, as shown in the delivery log.
TidyCal-Signature The proof that TidyCal sent it. See the next section.

Reply with any 2xx status (such as 200 or 204) within 10 seconds to tell TidyCal the event arrived. Do slow work after you reply, for example in a background job.

Check That a Message Is Really From TidyCal

Anyone who knows your URL could send fake requests to it. The signature lets you tell the difference. For every request:

  1. Read the raw body exactly as it arrived. Do not parse the JSON and write it back out, because that can change the bytes.
  2. Read the TidyCal-Signature header. It looks like t=1792595045,v1=9f2c..., where t is a timestamp and v1 is the signature.
  3. Join the timestamp, a dot and the raw body: t + . + body.
  4. Calculate an HMAC with SHA-256 over that text. Use your whole signing secret as the key, including the whsec_ at the start, and write the result as lowercase hex.
  5. Compare your result with v1 using a constant-time comparison function. A plain == can leak information.
  6. Check that t is within 5 minutes of your server's clock. If it is older, reject the request. This stops someone from re-sending a message they captured earlier.

If any step fails, reply with an error (such as 401) and ignore the message. Code examples in PHP and Node are in the Webhooks section of the API documentation.

You May Get the Same Event Twice

TidyCal delivers every event at least once. If your server is slow or down, or TidyCal is not sure the message arrived, it sends the event again. A delivery you resend from the delivery log also arrives a second time.

Every copy of an event has the same ID. Save the IDs you have handled and skip any you have seen before, so a repeat never creates a duplicate record in your system. Events can also arrive slightly out of order, so use the dates inside the booking, not the order of arrival.

Test It and See What Was Sent

On a webhook, click Send test. TidyCal sends a signed webhook.test event to your URL, which is a good way to check your signature code before real bookings arrive. The webhook must be enabled to send a test.

Click Delivery log to see the newest 50 deliveries from the last 30 days. Each row shows the Event, its Status (Delivered, Failed, Retrying (attempt N) or Queued), the Response code your server gave, and the Time. TidyCal reads only the response code, never the body of your reply. A failed attempt that got no response shows a general outcome instead, such as http_5xx, timeout, connection_failed, tls_error or blocked_destination.

Webhook delivery log table with Delivered, Failed and Retrying rows, response codes and Resend links

Click Resend on a row to send that event again. It keeps the same event ID, so your duplicate check still works.

What Happens When Your Server Is Down

If your server does not reply with a 2xx within 10 seconds, or replies with an error, TidyCal tries again. It makes up to 8 attempts in total:

Attempt When
1 Right away
2 1 minute after attempt 1 fails
3 5 minutes after attempt 2
4 15 minutes after attempt 3
5 1 hour after attempt 4
6 3 hours after attempt 5
7 6 hours after attempt 6
8 12 hours after attempt 7

If the last attempt fails, the delivery is marked Failed. You can resend it from the delivery log once your server is back.

TidyCal does not follow redirects. If your URL answers with a redirect (such as 301), that counts as a failure, so use the final address.

When a webhook turns off

TidyCal turns a webhook off on its own in two cases:

  • Your server answers 410 Gone. TidyCal takes this to mean you no longer want events, so it turns the webhook off right away and does not retry that delivery.
  • Deliveries keep failing. If deliveries have failed continuously for more than 72 hours, and at least 8 attempts in a row have failed, the webhook is turned off. One successful delivery resets the count, and so does a gap of more than 24 hours between failures, so a short outage will not turn it off.

When this happens TidyCal emails you, and the webhook shows as Disabled with the reason, such as "Turned off because your server answered 410 Gone" or "Turned off after 72 hours of failed deliveries". Events that happen while it is off are not saved for later. Fix the problem on your server, then click Enable to turn it back on. While deliveries are failing but the webhook is still on, the card warns you that deliveries have been failing since a given time.

Who keeps access

  • A team webhook belongs to the person who added it. If they are removed from the team or are no longer an owner or admin, the webhook is turned off.
  • A webhook for your whole account gets a team's bookings only while you own or administer that team.
  • A webhook added through a connected app, such as Zapier, is turned off when you disconnect that app.

Edit, Rotate or Delete a Webhook

  • Edit changes the endpoint URL, description and events. Click Save changes when you are done.
  • Rotate secret makes a new signing secret. The old one stops working right away, so update your server first or at the same moment. Only the account owner, or the team owner or admin who added a team webhook, can rotate its secret.
  • Disable pauses a webhook without deleting it, and Enable turns it back on.
  • Delete removes the webhook and its delivery log.

Troubleshooting

TidyCal rejects the URL when I add it

  • Use https://, port 443 or 8443, and an address that works from the public internet. The message "The url must be an https URL that resolves to a public address" means one of these is not met.
  • "A webhook for this URL already exists on this account" means you already added that URL. Edit the existing webhook instead.
  • "You can have at most 10 webhook endpoints" means you have reached the limit. Delete a webhook you no longer need. Zaps with instant triggers use these slots too.

The signature never matches

  • Use the raw body, not parsed JSON.
  • Use the whole secret, including whsec_.
  • If you rotated the secret, make sure your server has the newest one.
  • If requests are rejected as too old, your server's clock may be off. Sync it with a time server.

I am not getting an event

  • Check that the webhook shows Enabled, the event is ticked, and the scope covers that booking type.
  • Open the Delivery log. A 4xx or 5xx code means your server rejected the request. TidyCal keeps only the status code, so check your server's own logs for that request.
  • If the webhook shows Disabled, read the reason on the card, fix it, then click Enable.

Still stuck? Email support@tidycal.com (see How do I contact TidyCal support?).

Frequently Asked Questions

Which plans can use webhooks?

Every plan, including the Free Plan. The Webhooks card is on Integrations → Automations, next to Zapier, the API and AI assistants (MCP).

Do I need webhooks to use Zapier?

No. Connect Zapier from the same page and use its triggers. Zaps that use TidyCal's instant triggers create a webhook for you, and it appears on your Webhooks card and counts toward your 10. To stop such a Zap, turn it off in Zapier rather than deleting the webhook yourself.

Why did my webhook turn off?

Either your server answered 410 Gone, or deliveries failed for more than 72 hours with at least 8 failed attempts in a row. The reason is shown on the webhook's card, and TidyCal emails you. Fix your server and click Enable. Events from the time it was off are not saved.

I lost my signing secret. What do I do?

TidyCal shows the secret only once. Click Rotate secret on the webhook to get a new one, then update your server. The old secret stops working right away.

Did this answer your question? Thanks for the feedback There was a problem submitting your feedback. Please try again later.