Lemon Squeezy uses webhooks to notify your application when an event happens in your store. Webhooks are particularly useful for asynchronous events like when a license key is created or when a subscription expires.
Webhooks can be set up in your store settings panel. Webhooks require three elements to function:
POST
request to when an event is triggered.Lemon Squeezy supports passing custom data through the checkout that can be captured using webhooks. See this doc for more information.
When a webhook event occurs in Lemon Squeezy, a POST
request will be sent to the configured webhook URL. If the response status is 200
then the webhook will consider the request successfully sent. If the response status is anything else, the webhook will be retried up to three more times using an exponential backoff strategy (e.g. 5 secs, 25 secs, 125 secs) at which point the request will be considered failed and will no longer be retried.
The webhook body will be a JSON:API resource object that relates to the event (e.g. “order” events will be sent an order object, “subscription” events will be sent a subscription object, etc). The request headers will include:
Content-Type
header set to application/json
.X-Event-Name
header with the name of the event that triggered the webhook.X-Signature
header containing the request signature.If a webhook is configured to send a request to an HTTPS URL, the SSL certificate of the URL will be verified before sending the request.
{
"meta": {
"event_name": "order_created"
},
"data": {
"type": "orders",
"id": "1”,
"attributes": {
"store_id": 1,
"identifier": "636f855c-1fb9-4c07-b75c-3a10afef010a",
"order_number": 1,
"user_name": "Darlene Daugherty",
"user_email": "gernser@yahoo.com",
"currency": "USD",
"currency_rate": "1.0000",
"subtotal": 999,
"discount_total": 0,
"tax": 200,
"total": 1199,
"subtotal_usd": 999,
"discount_total_usd": 0,
"tax_usd": 200,
"total_usd": 1199,
"tax_name": "VAT",
"tax_rate": "20.00",
"status": "paid",
"status_formatted": "Paid",
"refunded": false,
"refunded_at": null,
"created_at": "2021-08-11T13:47:29.000000Z",
"updated_at": "2021-08-11T13:54:54.000000Z"
},
"relationships": {
"store": {
"links": {
"related": "https://api.lemonsqueezy.test/v1/orders/1/store",
"self": "https://api.lemonsqueezy.test/v1/orders/1/relationships/store"
}
},
"order-items": {
"links": {
"related": "https://api.lemonsqueezy.test/v1/orders/1/order-items",
"self": "https://api.lemonsqueezy.test/v1/orders/1/relationships/order-items"
}
},
"subscriptions": {
"links": {
"related": "https://api.lemonsqueezy.test/v1/orders/1/subscriptions",
"self": "https://api.lemonsqueezy.test/v1/orders/1/relationships/subscriptions"
}
},
"license-keys": {
"links": {
"related": "https://api.lemonsqueezy.test/v1/orders/1/license-keys",
"self": "https://api.lemonsqueezy.test/v1/orders/1/relationships/license-keys"
}
}
},
"links": {
"self": "https://api.lemonsqueezy.test/v1/orders/1"
}
}
}
To ensure that webhook requests are coming from Lemon Squeezy, you will be asked to enter a signing secret when creating your webhook. The secret can be anything you want but is normally a random string between 6 and 40 characters in length.
When the webhook request is sent, Lemon Squeezy will use the signing secret to generate a hash of the payload and send the hash in the X-Signature
header of the request. You can use the same secret to calculate the hash in your application and check it against the request signature to verify that the hashes match.
Lemon Squeezy uses a HMAC hex digest to compute the hash. Calculating the hash is different in each programming language, but an example in PHP would look like this:
$secret = '[SIGNING_SECRET]';
$payload = file_get_contents('php://input');
$hash = hash_hmac('sha256', $payload, $secret);
$signature = $_SERVER['HTTP_X_SIGNATURE'] ?? null;
if ($hash !== $signature) {
throw new Exception('Invalid signature.');
}