Webhooks: send events from Leat to your own system
A webhook subscription tells Leat to call a URL on your side every time a certain event happens: a contact is created or updated, points are credited, a voucher is redeemed. Leat sends a POST request with the data about that event, signs it, and retries it when your endpoint doesn't answer.
This is the way to get data out of Leat and into another system. Use it whenever the question is "how do I send X to my system when Y happens".
Webhook subscription or an automation's Webhook action?
An automation also has a Webhook action, and it looks like the quicker route. It isn't the right tool for keeping another system up to date. Compared with a subscription, the automation webhook:
- isn't signed, so your endpoint can't check the request came from Leat
- isn't retried: if your endpoint is down, that call is lost
- counts an error response from your endpoint as sent
- has no delivery history, only a step result on the automation run
- only fires for contacts who pass the automation's filters and re-enrollment settings
Use the Webhook action only when the call is one step in a flow that needs something a subscription can't do, like a delay, a branch or a filter. For everything else, create a webhook subscription as described below.
Before you start: a private app
Webhook subscriptions belong to an integration. For your own system, that's a private app:
- Go to Apps, then Integrations.
- On the Marketplace page, click Create private app in the top right.
- Name it after the system you're connecting and click Create.
You land on the private app's page. Later you'll find it in the sidebar under Installed.
Webhooks can't be created on a marketplace integration such as Shopify or Klaviyo. Those integrations show the webhooks they use on their own Webhooks tab, but they manage them themselves.
Create a webhook
- Open your private app and go to the Webhooks tab.
- Click New webhook.
- Pick the Event. The Name is filled in with the event's name, and you can change it.
- Enter the URL of your endpoint.
- Click Create.
The webhook is active straight away and opens on its own page.
A private app can have one webhook per event. To send the same event to two URLs, create a second private app.
Events you can subscribe to
Contacts:
contact_createdcontact_updatedcontact_was_subscribedcontact_was_unsubscribedcontact_identifier_createdcontact_identifier_updated
Loyalty and tiers:
credit_reception_createdpoints_transaction_createdtier_reachedtier_dropped
Rewards, promotions and vouchers:
reward_updatedreward_redeemedpromotion_updatedvoucher_createdvoucher_updatedvoucher_expiredvoucher_redeemed
Stamp cards:
stamp_card_createdstamp_card_transaction_createdstamp_card_redeemedstamp_card_redemption_reversedstamp_card_expired
The stamp card events only exist as V2 webhooks. See Older V1 webhooks below.
Gift cards and prepaid:
giftcard_createdgiftcard_updatedgiftcard_transaction_createdprepaid_transaction_created
Orders, visits and bookings:
order_createdvisit_createdbooking_createdbooking_updated
Forms and referrals:
form_submission_completedreferral_completed
The Webhooks page of the API reference at docs.leat.com/v3 has an example payload for each one.
What your endpoint receives
Every request is a POST with a JSON body. A webhook created in the dashboard always sends an array of events, even when there's only one:
{
"batch_uuid": "…",
"version": "V2",
"event_type": "contact_updated",
"count": 1,
"events": [
{
"event_uuid": "…",
"event_type": "contact_updated",
"data": { "contact": { … } }
}
]
}
Most events go out on their own, a few seconds after they happen. Leat doesn't wait to fill a batch. Bulk operations, like issuing vouchers to a group of contacts, send real batches of up to 100 events or 1 MB, whichever comes first.
For the contact events, data.contact holds the contact as it is now: the email address, every custom attribute and its value, the points balance and its totals, the tier and the prepaid balance.
contact_updated doesn't say which attributes changed. It sends the whole contact after the change, so compare it with what you have stored if you need to know.
Check that a request came from Leat
Every request carries a Signature header. It's an HMAC-SHA256 of the raw request body, as a lowercase hex string, calculated with your secret.
Calculate the same hash over the body exactly as you received it, before you parse it, and compare the two:
$expected = hash_hmac('sha256', $rawBody, $secret);
if (! hash_equals($expected, $request->header('Signature'))) {
abort(401);
}
A new webhook is signed with your account secret, which you'll find under Apps → Integrations → Account secret in the sidebar. To give one webhook its own secret, open it, click Edit and fill in Secret. Leave blank to keep the current secret.
Answer with a 2xx, and what happens if you don't
Leat counts a delivery as succeeded when your endpoint answers with a 2xx status within 20 seconds. Anything else, including a timeout or a refused connection, is a failure, and Leat tries again.
A delivery gets 5 attempts in total. The waits between them are roughly 1 minute, 2 minutes, 17 minutes and 3 hours. After the fifth failure the delivery is marked Failed and isn't sent again.
The webhook itself stays active, and you don't get a notification, so check the history when you suspect something is missing.
A retry sends the whole batch again. Store the event_uuid of each event you've processed and skip the ones you've already seen.
See what was sent, and send a test
Open the webhook and go to the History tab. Every delivery is listed with its status: Pending, Retrying, Succeeded or Failed. It also shows the number of events in it, the number of attempts and the most recent try. Click one to see the exact body that was posted and, per attempt, the status code your endpoint returned and how long it took.
Deliveries are kept for 90 days, and the individual attempts for 30.
To try your endpoint, click Test on the webhook's page. It's only there while the webhook is active. For a contact event you pick the contact to send. For any other event, the most recent matching record in your account is used to build the test payload, so the test fails if your account doesn't have one yet. A test is a real delivery: it shows up in the history and is retried like any other.
Change, pause or remove a webhook
- Edit changes the name, the URL and the secret. The event can't be changed, so create a new webhook for a different event.
- Deactivate stops deliveries until you activate it again. Events that happen in between aren't sent afterwards.
- Delete is only available on an inactive webhook, so deactivate it first.
Deleting a private app turns off all of its webhooks.
Older V1 webhooks
A webhook created before V2 existed shows V1 as its version, with a notice that V1 support will end. A V1 webhook sends one event per request instead of an array, and gives your endpoint 3 seconds to answer. Events added after V2, such as the stamp card events, have no V1 version.
A webhook's version can't be changed. To move, update your endpoint so it accepts an array of events. Then deactivate and delete the V1 webhook and create a new one for the same event, because a private app can only have one webhook per event.
Managing webhooks through the API
A developer can create and manage webhook subscriptions through the API as well, at /api/v3/oauth/clients/webhook-subscriptions. Pass "version": "V2" when creating one: without it, the API creates a V1 webhook. See Build a custom integration with the Leat API for tokens and the rest of the API.