Template syntax

Templates turn raw webhook JSON into human-readable messages. Use $${field.path} placeholders and optional pipes to format values.

Variable substitution

Wrap a dot-notation path in $${…}. Hooks reads the value from the parsed payload and inserts it as text.

Slack example
:tada: *New payment*
Customer: ${data.object.customer_email|default:unknown}
Amount: *${data.object.amount_total|div:100} ${data.object.currency|default:USD}*

Not supported: conditionals, loops, or custom functions. For complex branching, use multiple routes with different filters.

Pipes

Chain pipes after the field path, separated by |. Applied left to right.

  • |default:UnknownUse a fallback when the field is missing or null.
  • |date:relativeFormat Unix timestamps or ISO strings. Use date:relative for “3 hours ago”, or omit the arg for ISO output.
  • |div:100Divide a number (e.g. Stripe cents → dollars).
  • |mul:100Multiply a number.

Channel tips

  • Slack: Markdown works: *bold*, _italic_, :emoji:, and line breaks.
  • Discord: Markdown-style formatting (**bold**, line breaks). Embeds are supported in pre-built templates.
  • Email: Plain text subject + body. HTML is supported in the body field for richer layouts.
  • Webhook: Rendered string is sent as the outbound payload body—you control the text, not a raw JSON pass-through.

Pre-built examples

These samples ship in Hooks (from lib/templates/library.ts). Fork them in the route editor when creating a route.

stripeslack: Payment succeeded

Fires when a Stripe Checkout session completes (one-time payment or subscription signup)

:tada: *New payment received*
Customer: ${data.object.customer_email|default:unknown}
Amount: *${data.object.amount_total|div:100} ${data.object.currency|default:USD}*

stripeemail: Payment succeeded

Fires when a Stripe Checkout session completes (one-time payment or subscription signup)

Subject: Payment received — ${data.object.amount_total|div:100} ${data.object.currency|default:USD}

A new payment was completed.

Customer: ${data.object.customer_email|default:unknown}
Amount: ${data.object.amount_total|div:100} ${data.object.currency|default:USD}
Session ID: ${data.object.id|default:—}

stripediscord: Payment succeeded

Fires when a Stripe Checkout session completes (one-time payment or subscription signup)

**New payment received** :tada:
Customer: ${data.object.customer_email|default:unknown}
Amount: **${data.object.amount_total|div:100} ${data.object.currency|default:USD}**

stripewebhook: Payment succeeded

Fires when a Stripe Checkout session completes (one-time payment or subscription signup)

Payment received: ${data.object.amount_total|div:100} ${data.object.currency|default:USD} from ${data.object.customer_email|default:unknown}

shopifyslack: Order created

Fires on each new order. Point your Shopify "orders/create" webhook topic at this endpoint.

:shopping_cart: *New order #${order_number}*
Customer: ${email|default:Guest}
Total: *${total_price} ${currency}*
First item: ${line_items.0.title|default:see order}

shopifyemail: Order created

Fires on each new order. Point your Shopify "orders/create" webhook topic at this endpoint.

Subject: New order #${order_number} — ${total_price} ${currency}

A new order has been placed.

Order: #${order_number}
Customer: ${email|default:Guest}
Total: ${total_price} ${currency}
First item: ${line_items.0.title|default:see order}
Status: ${financial_status|default:pending}

shopifydiscord: Order created

Fires on each new order. Point your Shopify "orders/create" webhook topic at this endpoint.

**New order #${order_number}** :shopping_cart:
Customer: ${email|default:Guest}
Total: **${total_price} ${currency}**
First item: ${line_items.0.title|default:see order}

shopifywebhook: Order created

Fires on each new order. Point your Shopify "orders/create" webhook topic at this endpoint.

New Shopify order #${order_number}: ${total_price} ${currency} from ${email|default:Guest}

githubslack: PR opened

Fires when a pull request is opened (action: "opened", pull_request exists)

:arrow_up: *New PR on ${repository.full_name}*
*#${pull_request.number}: ${pull_request.title}*
By ${pull_request.user.login} → ${pull_request.base.ref}
${pull_request.html_url}

githubemail: PR opened

Fires when a pull request is opened (action: "opened", pull_request exists)

Subject: [${repository.full_name}] PR #${pull_request.number}: ${pull_request.title}

A new pull request has been opened.

Repository: ${repository.full_name}
PR #${pull_request.number}: ${pull_request.title}
Author: ${pull_request.user.login}
Base branch: ${pull_request.base.ref}
${pull_request.html_url}

githubdiscord: PR opened

Fires when a pull request is opened (action: "opened", pull_request exists)

**New PR on ${repository.full_name}** :arrow_up:
**#${pull_request.number}: ${pull_request.title}**
By ${pull_request.user.login} → ${pull_request.base.ref}
${pull_request.html_url}

githubwebhook: PR opened

Fires when a pull request is opened (action: "opened", pull_request exists)

PR opened: [${repository.full_name}] #${pull_request.number} "${pull_request.title}" by ${pull_request.user.login}