Shape webhook payloads with Smarty templates, live variable previews, and Send Test
Payload bodies and custom header values are rendered through a sandboxed Smarty 5 engine. Templates are stored in the database and compiled from strings - admin input is never written to disk as a template file.

Available variables
Every template, on every event, can reference the universal meta.* variables:
{$meta.event}- the Magento event name that triggered this delivery{$meta.store_id}/{$meta.store_code}- the store scope the event fired in{$meta.triggered_at}- ISO-8601 timestamp of when the event fired (not when it was delivered){$meta.webhook_name}- the admin label of the subscription being delivered{$meta.delivery_uid}- the UUID also sent asX-Webhook-Delivery-Id{$meta.test}-trueonly when rendered via the admin “Send Test” button
Event-specific variables ($order, $customer, $product, …) are documented live in the form: the “Available Template Variables” panel always lists meta.*, and the Load Variables for Selected Event button fetches the real, current variable set for your chosen event - resolved from actual store data rather than a hand-maintained list, so it always matches what your templates will see.
The two JSON modifiers
| Modifier | Behavior | Use it for |
|---|---|---|
json | JSON-encodes a complete value, including its own quotes/brackets | Dumping an entire value: {$order.items|json} |
escape_json | Same encoding, but strips the surrounding quotes | Embedding a scalar inside a "..." pair you wrote yourself: "email": "{$order.customer_email|escape_json}" |
Getting these backwards is the most common template bug: {$foo|json} inside hand-written quotes produces double-quoted JSON, while {$foo|escape_json} outside quotes produces an unquoted string.
Beyond those two, only an allowlist of native PHP functions is available as modifiers: count, nl2br, strtolower, strtoupper, ucfirst, trim, number_format, sprintf, implode, json_encode, and date. Anything else is rejected by the sandbox at save time.
Worked example: the default Order Placed template
{ "event": "{$meta.event}", "triggered_at": "{$meta.triggered_at}", "order": { "increment_id": "{$order.increment_id}", "grand_total": {$order.grand_total|number_format:2:".":""}, "currency": "{$order.order_currency_code}", "customer_email": "{$order.customer_email|escape_json}", "items": [ {foreach $order.items as $item} { "sku": "{$item.sku|escape_json}", "name": "{$item.name|escape_json}", "qty": {$item.qty_ordered}, "row_total": {$item.row_total} }{if !$item@last},{/if} {/foreach} ] }}Notice grand_total and qty_ordered are emitted unquoted (they are already numeric), while customer_email, sku, and name go through escape_json because they are free text sitting inside a "..." pair the template author wrote by hand.
Custom headers
Custom header values use the same template engine and modifiers, so a header value of {$meta.store_code} is valid. Headers are sent with every delivery of that webhook.
Send Test
The Send Test button renders the current form state (even unsaved) against sample data resolved from your real store, delivers it synchronously to your URL, and shows the result: HTTP code, duration, response excerpt, the rendered payload, and - when something fails - the exact error, including template compile errors with their line number. Test deliveries carry {$meta.test} = true and an X-Webhook-Test: 1 header so your receiver can distinguish them from real events.
Sandbox rules
Admin-authored templates run with no static class access, no PHP constants, no superglobals, and only the allowlisted modifiers above. A template that uses anything outside the sandbox fails validation at save time with the exact error and never reaches runtime.