Klaviyo system webhooks are the standard webhooks that Klaviyo fires when a profile bounces, subscribes, unsubscribes, or is suppressed. The extension registers these webhooks automatically on install. They are not visible to end users in the Klaviyo UI and do not need to be configured manually.
When a system webhook arrives, the extension finds the matching Omneo profile, combines any consent changes from the event, updates the Omneo profile’s communication preferences, and records an interaction so the activity is tracked on the profile timeline.
Processing flow
- Receive the webhook at the extension’s system webhook endpoint and validate it.
- Queue the work on the inbound Cloud Tasks queue so processing happens in the background.
- Find the Omneo profile using the stored Klaviyo identity, or email as a fallback. If no profile is found, the event is skipped.
- Filter circular events: Any event where Klaviyo’s
custom_method_detail is omneo was originally caused by Omneo itself and is silently ignored to prevent loops.
- Combine events for the same profile and merge their consent changes into a single payload.
- Stamp consent timestamps:
email_consent_updated_at and sms_consent_updated_at are added whenever the corresponding consent fields change. Timestamps are written in Y-m-d H:i:s format.
- Update Omneo with the combined comms payload.
- Master list check: If a master list is configured, the profile is added to it if they are now subscribed.
- Create interactions: One interaction is created per event, with a positive, neutral, or negative signal.
Subscription status is never pushed back to Klaviyo from a system webhook. Klaviyo already made the change on its side and is the source of truth for that event, so pushing back would create a sync loop.
Event reference
The table below summarises what each system webhook does to the Omneo profile. Comms changes lists the fields that are written; Signal indicates the type of interaction recorded.
Email events
| Event | Comms changes | Signal |
|---|
bounced_email (hard) | email_bounced: true, email_promo: false, email_optout: true | Neutral |
bounced_email (soft) | None (interaction only) | Neutral |
clicked_email | None | Positive |
clicked_email_to_unsubscribe | email_promo: false, email_optout: true | Neutral |
dropped_email | None | Neutral |
manually_suppressed_from_email_marketing | email_promo: false, email_optout: true | Negative |
manually_unsuppressed_from_email_marketing | email_promo: true, email_optout: false, email_bounced: false | Positive |
marked_email_as_spam | email_promo: false, email_optout: true | Negative |
subscribed_to_email_marketing | email_promo: true, email_optout: false, email_consent_updated_at set | Positive |
unsubscribed_from_email_marketing | email_promo: false, email_optout: true, email_consent_updated_at set | Negative |
bounce_suppression_added | email_promo: false, email_optout: true | Negative |
invalid_email_suppression_added | email_bounced: true, email_optout: true | Negative |
SMS events
| Event | Comms changes | Signal |
|---|
clicked_sms | None | Neutral |
failed_to_deliver_sms | sms_bounced: true | Neutral |
failed_to_deliver_automated_response_sms | None | Neutral |
subscribed_to_sms_marketing | sms_promo: true, sms_optout: false | Positive |
subscribed_to_sms_transactional | sms_optout: false | Positive |
unsubscribed_from_sms_marketing | sms_promo: false | Negative |
unsubscribed_from_sms_transactional | sms_optout: true | Negative |
Push events
| Event | Comms changes | Signal |
|---|
bounced_push | push_bounced: true | Neutral |
Combined event example
If a single batch contains a hard bounce and an unsubscribe for the same profile, the extension merges them into a single Omneo update:
Events:
1. bounced_email (Hard)
2. unsubscribed_from_email_marketing
Combined Omneo update:
email_bounced = true
email_promo = false
email_optout = true
email_consent_updated_at = 2024-01-15 10:30:00
This avoids redundant API calls and ensures the resulting Omneo profile state is consistent.