Events a shop can subscribe to, and how GreenLeaze delivers, signs and retries them.
Event types
A shop can subscribe to the following event types. Each has its own page with the full payload.
| Event type | Fired when | Payload family |
|---|---|---|
order_created | An order is created | Order created |
order_confirmed | An order is validated, or a booking deposit is paid | Order (full) |
order_booked | A booking deposit is paid | Order (full) + booking |
order_booking_cancelled | A booking is cancelled and its deposit refunded | Order (full) + booking + cancellation |
order_cancelled | An order is cancelled | Order cancelled (shop) |
order_archived | A non-active order is archived | Order (full) + archivedAt |
order_unarchived | An archived order is restored | Order (full) + archivedAt |
order_abandoned | The abandoned-cart reminder is sent for a draft order | Order abandoned |
subscription_shop_status_changed | The shop-facing status of an order changes | Shop status changed |
invoice_upload_failed | An invoice declared through the API never received a usable PDF | Invoice upload failed |
A shop receives an event from three kinds of destination:
- Webhook subscriptions. Subscriptions scoped to the shop, created from the dashboard. Each subscription = one event type + one URL + one secret (
secureToken). - External-app destinations. An active external app (Shopify middleware, PrestaShop, WooCommerce…) whose configuration holds
webhookValidateOrderUrlreceivesorder_confirmed, and one withwebhookCancelBookingUrlreceivesorder_booking_cancelled. These have no webhook subscription: the app configuration is the subscription. - LEGACY -~~ Order URLs.~~~~ The
urls.successgiven when the order was created receives theorder_confirmedpayload (see). The other order URLs and the discount-code URL are request/response callbacks, documented inorder success URL. ~~shop integration callbacks
Delivery
Every event listed above goes out through the GreenLeaze webhook gateway.
Request
POST <destination url>
Content-Type: application/json
User-Agent: GreenLeaze-Webhook-Gateway/1.0
X-Webhook-Delivery-Id: <uuid>
X-Webhook-Signature: <base64 HMAC-SHA256 of the raw body> (only when a secret is set)
<JSON payload>
| Header | Meaning |
|---|---|
X-Webhook-Delivery-Id | Gateway delivery id. Stable across the retries of one delivery, so use it to deduplicate. |
X-Webhook-Signature | base64(HMAC_SHA256(secret, rawBody)). Sent only when the destination has a secret (see below). |
The event type is in neither the body nor the headers. A destination knows what it receives from its subscription: register one URL per event type, or put the event type in the URL (path or query string).
Signature
| Destination | Secret used |
|---|---|
| Webhook subscription | Its secureToken (required for shops) |
| Webhook subscription linked to an app | Its secureToken, else the app's webhookSecret |
| External-app destination | The app's configuration.webhookSecret |
| Order success URL | The shop's API key |
Sign the raw request bytes. Re-serialising parsed JSON can reorder keys or change number formatting and break the comparison.
import { createHmac, timingSafeEqual } from 'crypto';
export function isGreenLeazeSignatureValid(params: {
rawBody: Buffer;
signature: string | undefined;
secret: string;
}): boolean {
if (!params.signature) return false;
const expected = createHmac('sha256', params.secret)
.update(params.rawBody)
.digest();
const received = Buffer.from(params.signature, 'base64');
return (
received.length === expected.length && timingSafeEqual(received, expected)
);
}Responses and retries
| Destination answers | Gateway does |
|---|---|
2xx | Success. The body (first part) is kept in the delivery log. |
429, 5xx, timeout (25 s), network error | Retries with exponential backoff: 5 s, 10 s, 20 s, 40 s… |
Any other 4xx | Gives up immediately (dead). A retry would not fix it. |
- The attempt count is the destination's
maxAttemptswhich is 8 by default. - Delivery is at least once: deduplicate on
X-Webhook-Delivery-Id. - A delivery replayed from the dashboard ("relancer") is a new delivery with a new
X-Webhook-Delivery-Id. Make the handler idempotent on business keys as well, for exampleid+ event type. - Delivery logs are visible in your GreenLeaze Dashboard
Which destinations an event reaches
GreenLeaze selects the active webhook subscriptions of the event's shop, then filters them on the order's origin, read from the payload's source field. External-app destinations are not filtered this way (see order_confirmed for their own rule).
source in the payload | Only destinations with |
|---|---|
"dashboard" | runsForDashboardSubscription = true |
| any other non-empty value | runsForApiSubscription = true |
null / absent | no filter |
order_cancelledsetssourceto the cancellation origin (shop,
greenleaze, …), neverdashboard. A destination withrunsForApiSubscription = falsetherefore never receivesorder_cancelled, even for an order created from the dashboard.
Delayed and held deliveries
- LEGACY - Order success URL. A
webhook_delayconfigured for the shop and the order'ssourceturns the call into a scheduled delivery, sent afterdelayInMinutes. - Held until the manual-validation decision. An external-app destination whose app has
delayWebhookUntilAutoApprovalreceivesorder_confirmedlate when a manual validation is open on the order. The delivery is scheduled for the auto-approval time + 24 h, released at once if the order is approved, and cancelled if the order is rejected.
Conventions
| Convention | Rule |
|---|---|
| Dates | ISO 8601 strings in UTC, e.g. "2026-04-20T10:00:00.000Z". |
| Absent vs null | A field marked optional is left out of the JSON when it has no value. null means the value exists and is empty. |
| Amounts | Euros, as JSON numbers with up to two decimals. TTC = VAT included, HT = VAT excluded. One exception: deliveryMode.price is a string (unfortunatly legacy). |
| VAT | vat is a percentage (20 = 20 %). |
| Identifiers | GreenLeaze ids are UUIDs. In products[], id / combinationId are the shop's product and variant ids, and greenleazeId is the GreenLeaze order line id. |
| Order keys | id is the GreenLeaze order (subscription) id. transactionId is the customer-facing order reference. cartId / cartSecureKey are the values the shop gave when it created the order. |
| Unknown fields | Ignore them. Fields are added without notice; only documented fields are maintained and considered as a change if removed or renamed |
Order status
statusdraft · deposit_paid · validating · active · inactive · canceled · canceled_with_fees · finished
Order shopStatus
shopStatusnot-opened · opened · account-selected · pending-additional-information · rejected · bank-selected · bank-connected · pending-company-validation · draft · canceled · finished · payment-method-entered · adresses-entered · customer-created · accepted-pending-customer-validation · active
An order that enters validation (status: "validating") is already shopStatus: "active": product decision, shops see the order as active from that point.
