SavvyCal API
SavvyCal API
Home Help


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:

The interface for registering a new webhook

Via the REST API

See the Webhooks endpoints 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
User-Agent: SavvyCal Webhooks (
x-savvycal-signature: sha256=6CAA4DEF5C3463B785E885FF19B8987B348E19399D2C5FB291274EDFA7128105
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.

Explore webhook payloads →

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:

Copy 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 =
|> :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)