Template system

Updated May 26, 20262 min read

Every notification Carriyo sends (email subject, email body, SMS text) is rendered from a template. Templates separate the merchant's editable copy from the underlying shipment data, with a templating language that injects the right values at send time.

This page is the model behind the template editor in CX Apps → Notifications.

Liquid

Carriyo uses Liquid (liquidjs) for templating. The full default Liquid syntax is available: tags ({% if %}, {% for %}), filters ({{ value | default: "fallback" }}), object access ({{ a.b.c }}).

WhatsApp doesn't use Liquid. WhatsApp Business templates are pre-registered with Meta; Carriyo sends placeholder values as an array against the approved template name. The placeholder layout is fixed when the template is registered.

The shape of a template

A template has:

  • Channel. email_subject, email_body, sms.
  • Trigger event. Which shipment / return / order event fires it (e.g. shipment OUT_FOR_DELIVERY).
  • Language. Per locale; one logical template has multiple variants.
  • Merchant scope. Per merchant on multi-merchant tenants.
  • Body. The editable content with embedded Liquid.

Available data

At render time the template sees a snake-cased view of the event's context. The most-used objects:

  • shipment: the full Shipment document (carrier, tracking number, pickup, dropoff, parcels, status, dates).
  • customer: the customer's name, email, phone (drawn from the shipment's dropoff).
  • order: order-level fields when the shipment belongs to one (partner reference, totals).
  • merchant: merchant brand profile (name, support contacts).
  • carrier: carrier name and carrier account name.
  • trigger.status, trigger.channel: the firing trigger and channel.
  • key_milestones: the canonical events on the shipment so far.
  • profile.brand, profile.logo: merchant brand identity.

Link tags for the major customer-facing pages:

  • tracking_link: the branded tracking page URL.
  • feedback_link: the feedback page URL.
  • return_request_link: the returns portal URL.

For SMS, these are auto-shortened to a Carriyo short-link redirector to keep within character limits.

For return requests, additional context is available:

  • resolution: refund / replace / etc.
  • items[].return_reason, items[].rejection_reason: translated to the recipient's language.

The Dashboard's template editor shows the full tag tree inline as you author; what's documented here is the shape, not the exhaustive field list.

Fallbacks for missing data

Use Liquid's default filter:

Hi {{ customer.first_name | default: "there" }},

Your order {{ order.partner_order_reference }} is out for delivery.
Track here: {{ tracking_link }}

For conditional sections, use if:

{% if shipment.estimated_delivery.from %}
Expected between {{ shipment.estimated_delivery.from }}
and {{ shipment.estimated_delivery.to }}.
{% endif %}

Translations

Each template is one logical entity with multiple language variants:

  • The merchant configures available languages on the brand.
  • Each language variant is authored separately. There's no auto-translation. Localized copy is written by the merchant.
  • Tags are shared across variants. The same customer.* / shipment.* paths work regardless of language; only the surrounding copy differs.

The Dashboard's editor shows variants side-by-side so copy stays in sync.

How it fits with other modules