HomeGuidesAPI Reference
ChangelogHelp CenterCommunityContact Us
Guides

Working with system webhooks

Learn how to receive and verify system webhook requests.

Before you begin

Check out our Webhooks API overview to make sure you’re ready to receive and verify webhook requests.

📘

Note that only Advanced KDP customers and Klaviyo app partners can access and manage webhooks with our Webhooks API.

In this guide, we’ll detail the structure of the request that Klaviyo sends to your app when the webhook is triggered, and give an example. Make sure your app is configured to receive the request.

Webhook request structure

Request headers:

  • Klaviyo-Webhook-Id
    Unique ID for the webhook.
  • Klaviyo-Signature
    The HMAC-SHA256 signature, which you can use to verify the request is coming from Klaviyo. More details on this below in HMAC signature verification.
  • Klaviyo-Timestamp
    When the webhook request was sent.

Request body structure:

  • data
    Array including webhook event payloads. Each item in this array has the following structure:

    • external_id
      ID of the event.
    • payload
      Payload with the same structure as a Get Event API call.
    • topic
      The topic ID associated with this event, e.g., event:klaviyo.email_delivered. Topics are dynamic and based on the metrics available in your account. You can get a full list of the topics available for an account by using the Get Webhook Topics endpoint (see Common webhook topics).
  • meta

    • klaviyo_account_id
      The account ID where the event occurred.
    • klaviyo_webhook_id
      Unique ID for the webhook. This value must match the Klaviyo-Webhook-Id request header. If these values do not match, this indicates potential malicious activity and you should not process the webhook.
    • timestamp
      When the webhook request was sent.

Example webhook request

The following is an example webhook request sent from Klaviyo, triggered by the Sent SMS event.

POST ${ENDPOINT_URL} HEADERS Content-Type: application/json Klaviyo-Signature: e6c00e313eaea50ca3b89a7de5a782a2014f96fb8315c065898c669d831912d1 Klaviyo-Timestamp: Thu, 04 Jan 2024 18:05:25 GMT Klaviyo-Webhook-Id: a8b890458b4bbfaa26d961471b83c101d6de23bd826e7e5173a15310985ec3cb BODY { "data": [ { "external_id": "4L3cwQae2TX", "payload": { "data": { "type": "event", "id": "4L3cwQae2TX", "attributes": { "timestamp": 1700503941, "event_properties": { "From Number": "+18108675309", "From Phone Region": "US", "To Number": "+18103317156", "To Phone Region": "US", "Message Body": "Hello, I need help with my order!", "Message Type": "unrecognized", "Intent 1": "Hello", "Intent 1 Confidence": "0.99", "$internal": { "vendor": 1 }, "$event_id": "d28753007bd39d4bda518f9b9eafb5ce", "$extra": { "Message ID": "SM5648159861e451a05bee72f3cb454549", "Inbound Message ID": "01HFPYXW863EM519SWQDY9KTCM", "From State": "MA", "From City": "BOSTON", "From Country": "US" } }, "datetime": "2023-11-20 18:12:21+00:00", "uuid": "58edf080-87d0-11ee-8001-895ec29a6280" }, "relationships": { "profile": { "data": { "type": "profile", "id": "01HFAD5MDRT48NN8VN7H1NB0BH" }, "links": { "self": "https://a.klaviyo.com/api/events/4L3cwQae2TX/relationships/profile/", "related": "https://a.klaviyo.com/api/events/4L3cwQae2TX/profile/" } }, "metric": { "data": { "type": "metric", "id": "Utt3N2" }, "links": { "self": "https://a.klaviyo.com/api/events/4L3cwQae2TX/relationships/metric/", "related": "https://a.klaviyo.com/api/events/4L3cwQae2TX/metric/" } } }, "links": { "self": "https://a.klaviyo.com/api/events/4L3cwQae2TX/" } }, "topic": "event:klaviyo.sent_sms" } ], "meta": { "klaviyo_webhook_id": "a8b890458b4bbfaa26d961471b83c101d6de23bd826e7e5173a15310985ec3cb", "timestamp": "2024-01-04T13:05:25.891446+00:00", "klaviyo_account_id": "abc123" "version": "2023-06-03" } }

HMAC signature verification

Klaviyo generates signatures using a hash-based message authentication code (HMAC) with SHA-256. The signature is found in the Klaviyo-Signature header.

You should generate the HMAC with SHA-256 on your end using the request body, your secret_key, and the timestamp (Klaviyo-Timestamp header). Then, compare this to the one sent along with the request.

📘

We strongly recommend that you only process webhook requests that you have verified are coming from Klaviyo.

Here's some example code for checking whether a webhook request is authentic (i.e., sent from Klaviyo) by generating and comparing the HMAC signature:

import hashlib import hmac def is_webhook_from_klaviyo( request, hmac_secret # should be stored and retrieved in a secure manner ) -> bool: signature = request.headers.get("Klaviyo-Signature") timestamp = request.headers.get("Klaviyo-Timestamp") message_body = request.data computed_signature = hmac.new(hmac_secret, message_body, hashlib.sha256) computed_signature.update(message_timestamp.encode()) return hmac.compare_digest(computed_signature.hexdigest(), message_signature)

Batching

  • Events are batched for outbound webhook requests. Any single request may include up to 1,000 events in the payload, which are processed within a short window of each other.
  • Events batched in the same request may come from multiple topics. Each event payload within the request has an associated topic.

Concurrency

  • Klaviyo will send up to 10 concurrent requests (if there is sufficient volume) to an endpoint URL for a given webhook, i.e., per company.
  • An OAuth app installed across many accounts could theoretically receive up to 10 concurrent requests per app installation from Klaviyo.

Error handling & retries

  • Klaviyo is expecting your service to respond to a webhook request with a 200, 201, or 202 status code within 5 seconds. If those conditions are not met, or another connection error occurs, we will consider the webhook request failed.
  • We use an exponential backoff to retry failed requests to your endpoint with a maximum delay of 1 hour.
  • If the webhook gets into a "failed" state, we will disable the webhook and no longer retry.

Webhook disablement

One failed webhook request will not cause the webhook to become disabled. We will perform retries with backoff until we receive a successful status code (200, 201, or 202) from your endpoint URL.

If a webhook remains in an "error" state for over 48 hours, the webhook will become disabled, i.e., the webhook's enabled field will be updated to false. When this occurs, we will no longer attempt to send any webhooks to your configured endpoint URL.

Once you have fixed the problems with your endpoint URL and you're ready to re-enable it, you can update your webhook to do so.

If you created your webhook in the UI, you can update it there, as shown below:

Edit webhook page in Klaviyo with a field for the endpoint URL and a reset button with a black background.

If you created it via API, you can use the Update Webhook. See the example request below:

PATCH /api/webhooks/WEBHOOK_ID { "data": { "type": "webhook", "id": "WEBHOOK_ID", "attributes": { "enabled": true } } }

📘

Please note that Klaviyo will only send new events (that were processed after the webhook was re-enabled) when this occurs. If you need to catch up on events that occurred while your webhook’s endpoint URL was down, you can use the Get Events endpoint and filter for the relevant metric_id.

Common webhook topics

The table below lists common webhook topics for native Klaviyo metrics. Note this table is not an exhaustive list of Klaviyo webhook topics. To get a full list of available webhook topics for an account (including integration-specific or API metrics), use the Get Webhook Topics endpoint.

Metric NameTopic IDDescription
Bounced Emailevent:klaviyo.bounced_emailWhen an email soft or hard bounced.
Clicked Emailevent:klaviyo.clicked_emailWhen a recipient clicked an email.
Dropped Emailevent:klaviyo.dropped_emailWhen there is a discrepancy between suppression status in Klaviyo and other upstream email service providers that Klaviyo may utilize.
Subscribed to Email Marketingevent:klaviyo.subscribed_to_email_marketingWhen a profile consents to email marketing. For example, this could be triggered via a signup form or directly adding to a list (with the consent box checked).
Unsubscribed from Email Marketingevent:klaviyo.unsubscribed_from_email_marketingWhen a user unsubscribes from email marketing.
Manually Suppressed From Email Marketingevent:klaviyo.manually_suppressed_from_email_marketingWhen a profile is manually suppressed.
Manually Unsuppressed From Email Marketingevent:klaviyo.manually_unsuppressed_from_email_marketingWhen a profile is manually unsuppressed.
Email Deliveredevent:klaviyo.received_emailWhen an email is delivered to a recipient.
Marked Email as Spamevent:klaviyo.marked_email_as_spamWhen a recipient marks an email as spam.
Opened Emailevent:klaviyo.opened_emailWhen a recipient opens an email.
Updated Email Preferencesevent:klaviyo.updated_email_preferencesWhen a user clicks the “Manage Preferences” link and changes them.
Clicked SMSevent:klaviyo.clicked_smsWhen a recipient clicks on a link within an SMS message.
Failed to Deliver Automated Response SMSevent:klaviyo.failed_to_deliver_automated_response_smsWhen an automated response (e.g., from a keyword like "Help") is sent but not delivered to the recipient.
Failed to Deliver SMSevent:klaviyo.failed_to_deliver_smsWhen message is sent but not delivered to the recipient.
Received Automated Response SMSevent:klaviyo.received_automated_response_smsWhen Klaviyo has recorded that a recipient received an automated SMS message in response to a keyword or the auto-responder when no keyword is recognized.
Received SMSevent:klaviyo.received_smsWhen Klaviyo has recorded that your recipient has received an SMS from a flow, campaign, or part of an SMS conversation.
Sent SMSevent:klaviyo.sent_smsWhen an inbound SMS is received by a Klaviyo sending number.
Subscribed to SMS Marketingevent:klaviyo.subscribed_to_sms_marketingWhen a profile consents to SMS marketing.
Unsubscribed from SMS Marketingevent:klaviyo.unsubscribed_from_sms_marketingWhen a profile unsubscribes from SMS marketing.
Received Pushevent:klaviyo.received_pushWhen Klaviyo records that your recipient has received a push notification from a flow or campaign.
Bounced Pushevent:klaviyo.bounced_pushWhen a push notification is sent but not delivered to the recipient.
Opened Pushevent:klaviyo.opened_pushWhen a recipient taps on a push notification and thus opens the app.
Ready to Reviewevent:klaviyo.ready_to_reviewWhen a product is ready to review — triggered after fulfillment or delivery and some delay.
Submitted Ratingevent:klaviyo.submitted_ratingWhen a product rating was submitted. This gets triggered on every review submission.
Submitted Reviewevent:klaviyo.submitted_reviewWhen a product review was submitted. This requires content to be present, in addition to a rating.

Additional resources


Did this page help you?