Webhooks
Webhooks allow you to listen for changes in a SavvyCal account and get notified via HTTP POST requests.
Registering a webhook
Via the user interface
Navigate to Settings > Integrations and click the + icon in the Webhooks:

Via the REST API
See the Webhooks documentation for managing webhooks via the SavvyCal API.
Consuming webhooks
When an event occurs that triggers a webhook, we will send an HTTP POST to the URL you specified, with a JSON-encoded body:
POST /my-webhook-receiver HTTP/1.1
Host: https://myapp.com
User-Agent: SavvyCal Webhooks (https://savvycal.com)
x-savvycal-signature: sha256=6CAA4DEF5C3463B785E885FF19B8987B348E19399D2C5FB291274EDFA7128105
x-savvycal-webhook-id: wh_XXXXXXXXXX
Content-Type: application/json
{
"type": "event.created",
"id": "payload_XXXXXXXXXX",
[...]
}
The URL you specify to received webhook payloads should respond quickly with a 200 OK
response code. To ensure you respond in a timely manner, it's best to enqueue the body in a job queue to process asynchronously (rather than processing it in the request cycle). If we receive a non-2xx response code, we will retry with exponential backoff.
Securing webhooks
Webhooks are sent over HTTPS and are signed with a secret key. The secret key is unique to each webhook and is used to verify the authenticity of the requests. This secret can be found alongside the webhook configuration in your account's integration settings. Using the secret key, SavvyCal signs the request body with a SHA-256 HMAC signature. The signature is included in the x-savvycal-signature
header.
Click here to copy your signing secret to clipboard:

Therefore, you can verify the authenticity of a webhook request you receive by generating your own signature (with your webhook's secret key) and comparing it to the signature in the x-savvycal-signature
header.
An example of how to verify the signature is shown below in Elixir:
def verify_signature(body, signature, secret) do
body = Plug.Conn.read_body(conn)
header_signature = Plug.Conn.get_req_header(conn, "x-savvycal-signature") |> List.first()
computed_signature =
:sha256
|> :crypto.hmac(secret, body)
|> Base.encode16()
# Performs a "constant time" string comparison, which helps mitigate certain
# timing attacks against regular equality operators.
Plug.Crypto.secure_compare(header_signature, computed_signature)
end
Event types
The following event types are available:
Event Type | Description |
---|---|
event.created | Emitted when someone schedules a new event via one of your scheduling links (or when approval is granted by the organizer on a event that requires approval). (Schema) |
event.requested | Emitted when someone schedules a new event via one of your scheduling links with approval required (see Require approval for new events). (Schema) |
event.approved | Emitted when an organizer approves an event (see Require approval for new events). (Schema) |
event.declined | Emitted when an organizer declines an event (see Require approval for new events). (Schema) |
event.rescheduled | Emitted when an existing event is rescheduled. (Schema) |
event.changed | Emitted when any time an event is updated, providing details about the change. (Schema) |
event.canceled | Emitted when an existing event is canceled. (Schema) |
event.attendee.added | Emitted when a new attendee is added to an existing event (either as guest on a non-group event, or an attendee on a group event). (Schema) |
event.attendee.canceled | Emitted when an individual cancels from a group event. (Schema) |
event.attendee.rescheduled | Emitted when an individual reschedules their response to a group event. (Schema) |
poll.response.created | Emitted when an individual creates a response to a poll. (Schema) |
poll.response.updated | Emitted when an individual updates a response to a poll. (Schema) |
workflow.action.triggered | Emitted when an action is triggered in a workflow. (Schema) |