Skip to main content

Webhooks

Warning

Webhooks are only available for enterprise accounts.

Webhooks are a technique of programming that allows a web application to receive real-time notifications of events from another application. In other words, webhooks allow two applications to communicate with each other.

Webhooks configuration

The webhooks are available only for organizations, so the events will be triggered when something happens in a resource owned by that organization.

The webhook configuration has 2 important fields:

  • url: The URL that will be used to send a webhook.
  • password (optional): This will allow webhook receivers to verify that the webhook is coming from TimeTime and not from a bad actor that discovered the webhook receiver URL. The password will be securely stored in TimeTime, and then appended as a header in each webhook delivery, the header name is X-TT-Webhook-Password.

Webhooks delivery order

TimeTime will send webhooks to your org one by one, reducing the chances of race conditions on your side. No webhooks will be sent if there is one webhook delivery in process.

Webhooks retries

TimeTime will perform 10 attempts to deliver a webhook. To allow your server recover from a temporary issue, TimeTime will apply an exponential backoff between retries. The initial delay will be 200ms, and it'll be doubling that till reaching the 10 attempts (with a maximum delay of 10 seconds).

After exhausting all the attempts, TimeTime won't try again to send this webhook, and it'll move on to the next one. Here we're making a decision in favor the "availability" over "consistency", we prefer to move one instead of "stopping the world" so your system keep receiving webhooks, at the chance of losing some of them.

TimeTime will securely store the webhook payloads, so if you missed some webhooks and really need them, contact us so we can consider retrying those ones.

Available webhooks

TimeTime offers webhooks for some events happening in the system, full list with OpenApi docs can be found here.

  • CalendarEventChanged: Triggered when a calendar event is created or updated.
  • BookingChanged: Triggered when a booking is created or updated in any way, such as an status change (confirmed, canceled, etc).
  • UpcomingBooking: Sent if "webhook" reminders are enabled in your tenant (enterprise customers only), then the webhook will be sent respecting the time configured in the event type settings.

Event type location in booking webhooks

BookingChanged and UpcomingBooking include the event type's configured location in eventType.location:

  • BookingChanged: new.eventType.location and, when old is present, old.eventType.location.
  • UpcomingBooking: booking.eventType.location.

The field uses the same location format as the event type API. It is null when no location is configured. Older stored webhook payloads and booking snapshots may omit the field or contain null; receivers should accept both.

typeAdditional fieldsMeaning
FixedLocationfreeTextA configured address, room, meeting URL, or other location text.
GoogleMeetLocationNoneThe event type uses Google Meet.
MicrosoftOutlookLocationNoneThe event type uses Microsoft Teams.
BookerPhoneLocationquestionIdIdentifies the question that collects the booker's phone number.
BookerSelectionLocationquestionIdIdentifies the question that collects the booker's location choice.

This field describes the event type configuration. For a generated online meeting URL, use the booking's conferenceLink (which can be null until the conference is created). For a phone number or location supplied by the booker, find the entry in answeredQuestions whose id matches questionId.

Location changes after booking

eventType.location reflects the event type configuration when the booking is read. It is not a snapshot of the location at booking creation, and editing the event type can change the location returned for existing bookings.

  • In BookingChanged, old.eventType.location retains the configuration read immediately before the booking change. new.eventType.location is refreshed when the webhook is processed. If the booking can no longer be loaded, new falls back to the stored payload.
  • In UpcomingBooking, booking.eventType.location is read when the reminder is processed.

For example, if a booking is created with Room A, the event type is changed to Room B, and the booking is then cancelled, both old and new contain Room B. If the event type changes again to Room C before that cancellation webhook is processed, old contains Room B and new contains Room C.

These fields do not provide a history of event type location changes. Store the original location separately if your integration needs to preserve where the appointment was booked. The booking's answeredQuestions and conferenceLink remain booking-specific; changing a location's questionId can mean it no longer matches an answer collected for an earlier booking.

Location examples

Each example below is a booking fragment, showing only the fields needed to find the location. In BookingChanged, these fields are inside new and, when present, old. In UpcomingBooking, they are inside booking. See the webhook payload examples for the surrounding structure.

Fixed address or room

Read the address from eventType.location.freeText.

{
"eventType": {
"location": {
"type": "FixedLocation",
"freeText": "Sala 4, Calle Mayor 123, Madrid"
}
},
"conferenceLink": null,
"answeredQuestions": []
}

Fixed meeting URL

A manually configured meeting URL is also a FixedLocation. Read it from freeText; conferenceLink can be null because no conference needs to be generated.

{
"eventType": {
"location": {
"type": "FixedLocation",
"freeText": "https://video.example.com/consultation"
}
},
"conferenceLink": null,
"answeredQuestions": []
}

Google Meet

The location type identifies the provider. Read the generated join URL from conferenceLink.

{
"eventType": {
"location": {
"type": "GoogleMeetLocation"
}
},
"conferenceLink": "https://meet.google.com/abc-defg-hij",
"answeredQuestions": []
}

Microsoft Teams

MicrosoftOutlookLocation identifies a Microsoft Teams meeting. The join URL is in conferenceLink.

{
"eventType": {
"location": {
"type": "MicrosoftOutlookLocation"
}
},
"conferenceLink": "https://teams.microsoft.com/l/meetup-join/example",
"answeredQuestions": []
}

The URLs in these examples are illustrative.

A booking webhook may arrive before the meeting URL has been generated. The location still identifies the provider, while conferenceLink is null. This also applies to Microsoft Teams and online meetings selected by the booker.

{
"eventType": {
"location": {
"type": "GoogleMeetLocation"
}
},
"conferenceLink": null,
"answeredQuestions": []
}

Phone call

Match eventType.location.questionId to an answeredQuestions[].id, then read that entry's answer for the phone number. Question IDs can be customized; use the ID from the payload.

{
"eventType": {
"location": {
"type": "BookerPhoneLocation",
"questionId": "phone"
}
},
"conferenceLink": null,
"answeredQuestions": [
{
"id": "phone",
"label": "Phone number",
"answer": "+34910000000"
}
]
}

Booker chooses the location

BookerSelectionLocation identifies the question used to choose a location. Match its questionId to an answeredQuestions[].id. The webhook serializes the selected answer as human-readable text. For an online choice, read the join URL from conferenceLink.

The following fragments show each supported choice.

In person

{
"eventType": {
"location": {
"type": "BookerSelectionLocation",
"questionId": "meeting-location"
}
},
"conferenceLink": null,
"answeredQuestions": [
{
"id": "meeting-location",
"label": "Where would you like to meet?",
"answer": "Sala 4, Calle Mayor 123, Madrid"
}
]
}

Google Meet

{
"eventType": {
"location": {
"type": "BookerSelectionLocation",
"questionId": "meeting-location"
}
},
"conferenceLink": "https://meet.google.com/abc-defg-hij",
"answeredQuestions": [
{
"id": "meeting-location",
"label": "Where would you like to meet?",
"answer": "Google Meet"
}
]
}

Microsoft Teams

{
"eventType": {
"location": {
"type": "BookerSelectionLocation",
"questionId": "meeting-location"
}
},
"conferenceLink": "https://teams.microsoft.com/l/meetup-join/example",
"answeredQuestions": [
{
"id": "meeting-location",
"label": "Where would you like to meet?",
"answer": "Teams meeting"
}
]
}

Phone call

{
"eventType": {
"location": {
"type": "BookerSelectionLocation",
"questionId": "meeting-location"
}
},
"conferenceLink": null,
"answeredQuestions": [
{
"id": "meeting-location",
"label": "Where would you like to meet?",
"answer": "Booker phone"
},
{
"id": "phone",
"label": "Phone number",
"answer": "+34910000000"
}
]
}

For the phone choice, "Booker phone" is the selected option. The number is collected through a separate phone question (phone in this example); use the phone question ID configured for your event type. If a fixed-location choice has no address, its answer is "Fixed location". An unanswered question can have "answer": null.

No configured location

When the event type has no configured location, eventType.location is explicitly null.

{
"eventType": {
"location": null
},
"conferenceLink": null,
"answeredQuestions": []
}

Older payload without the field

Previously stored payloads may omit location entirely. Treat a missing field as an unknown location; it does not prove that the event type has no location configured.

{
"eventType": {
"id": "ed19c788-e869-4de4-81ec-ec0d2ba748cf",
"name": "Consultation"
},
"conferenceLink": null,
"answeredQuestions": []
}

Webhook payload examples

BookingChanged

When a booking is created, old is null. On subsequent changes, old contains the previous booking state, including the event type configuration read before that change when available. Read the configuration at webhook processing time from new.eventType.location; neither field preserves the location at booking creation. See location changes after booking.

{
"type": "BookingChanged",
"new": {
"id": "90b33f62-0347-4ad8-b63a-88eab21df3ee",
"eventType": {
"id": "ed19c788-e869-4de4-81ec-ec0d2ba748cf",
"name": "Reunión con Odín del Río Piñeiro",
"owner": {
"type": "BookedEventTypeOwnerUser",
"id": "54330e53-e9b9-430c-a417-7efaa87f39f7",
"email": "odin@timetime.in",
"externalId": null
},
"tags": {},
"location": {
"type": "FixedLocation",
"freeText": "Sala 4, Calle Mayor 123, Madrid"
}
},
"interval": {
"start": "2025-01-19T11:45:00Z",
"end": "2025-01-19T11:55:00Z"
},
"booker": {
"email": "odin+booker@timetime.in"
},
"answeredQuestions": [
{
"id": "tt_booker_email",
"label": "Correo electrónico",
"answer": "odin+booker@timetime.in"
},
{
"id": "8f3e67b3-c381-4fb0-8197-dc53f73aa633",
"label": "Nombre",
"answer": "odinnn test"
}
],
"notes": "",
"units": 1,
"cancellation": null,
"bookedResources": [],
"conferenceLink": null,
"privateNotes": [],
"status": "CONFIRMED",
"price": null,
"confirmedAt": "2025-01-19T11:42:32.234713Z",
"heldUntil": null
},
"old": null
}

UpcomingBooking

Read the location from booking.eventType.location. This webhook has no new or old fields.

{
"type": "UpcomingBooking",
"booking": {
"id": "90b33f62-0347-4ad8-b63a-88eab21df3ee",
"eventType": {
"id": "ed19c788-e869-4de4-81ec-ec0d2ba748cf",
"name": "Reunión con Odín del Río Piñeiro",
"owner": {
"type": "BookedEventTypeOwnerUser",
"id": "54330e53-e9b9-430c-a417-7efaa87f39f7",
"email": "odin@timetime.in",
"externalId": null
},
"tags": {},
"location": {
"type": "FixedLocation",
"freeText": "Sala 4, Calle Mayor 123, Madrid"
}
},
"interval": {
"start": "2025-01-19T11:45:00Z",
"end": "2025-01-19T11:55:00Z"
},
"booker": {
"email": "odin+booker@timetime.in"
},
"answeredQuestions": [
{
"id": "tt_booker_email",
"label": "Correo electrónico",
"answer": "odin+booker@timetime.in"
},
{
"id": "8f3e67b3-c381-4fb0-8197-dc53f73aa633",
"label": "Nombre",
"answer": "odinnn test"
}
],
"notes": "",
"units": 1,
"cancellation": null,
"bookedResources": [],
"conferenceLink": null,
"privateNotes": [],
"status": "CONFIRMED",
"price": null,
"confirmedAt": "2025-01-19T11:42:32.234713Z",
"heldUntil": null
}
}

CalendarEventChanged

Calendar events expose their own new.locations array. They do not contain an eventType.location field.

{
"type": "CalendarEventChanged",
"new": {
"id": "5934c6a5-6f01-4223-8b4d-308af18be8dc",
"calendarId": "ae0e1585-a169-4e0a-bc8a-5a7a4ce7e62b",
"interval": {
"inclusiveStart": {
"dateTime": "2024-03-20T15:00:00+01:00",
"timeZone": "Europe/Madrid"
},
"exclusiveEnd": {
"dateTime": "2024-03-20T16:00:00+01:00",
"timeZone": "Europe/Madrid"
}
},
"status": "CONFIRMED",
"summary": "Some event",
"description": "Some event description",
"attachments": [],
"locations": [],
"attendees": [
{
"id": "460b4f91-87f8-4c03-8724-80a651e56976",
"invitation": {
"id": "27de4f27-46fb-4899-8ad4-bce8f3349afe",
"displayTimeMode": "EXACT",
"summary": "You have been invited to an event",
"subtitle": "Please accept or decline the invitation.",
"description": "Some invitation description"
},
"status": "ACCEPTED",
"organizer": false,
"email": "someone@example.com",
"displayName": null,
"comment": null,
"managementUrl": "https://app.timetime.in/i/27de4f27-46fb-4899-8ad4-bce8f3349afc"
}
],
"comments": [
"comment 1",
"comment 2 🤣"
],
"tags": {
"tag 1": "grande"
}
},
"semanticDiff": []
}