Skip to main content

Message Personalization

Personalization lets each recipient receive their own version of a message. Write Liquid variables into any text field — title, body, subtitle, URL, or email HTML — and Hober renders them per recipient at send time from the subscriber's attributes.

Hi {{ subscriber.first_name | default: "there" }}, your {{ subscriber.plan }} plan renews tomorrow.

For a subscriber with {"first_name": "Ada", "plan": "Growth"}, this delivers "Hi Ada, your Growth plan renews tomorrow." A subscriber without first_name gets "Hi there, …".


Variables

Two namespaces are available, and only these two — bare variables like {{ first_name }} are not bound and render blank:

NamespaceSourceExample
subscriber.*The subscriber's attributes (set via SDK identify, the ingest API, or the dashboard){{ subscriber.first_name }}
device.*The receiving device's attributes{{ device.model }}

Attribute names are the keys you set on the subscriber — there is no fixed schema. As soon as any subscriber carries an attribute, its key appears in the dashboard's Insert variable menus.

Missing values and fallbacks

A variable the subscriber doesn't have renders as an empty string — the send does not fail. When blank text would read badly, declare a fallback with the default filter:

{{ subscriber.first_name | default: "there" }}

Filters

The standard Shopify Liquid filter set is available — default, upcase, downcase, capitalize, date, truncate, and the rest. Referencing a filter that doesn't exist fails validation when you save (see below).


Where personalization applies

  • Push, in-app, SMS, WhatsApp-adjacent text: title, body, subtitle, and URL personalize on every channel. For SMS the rendered length varies per recipient, so segment counts shown at compose time are estimates.
  • Email: subject (the title), the HTML body, and the plain-text body all personalize. In the drag-and-drop email editor, insert variables through the editor's Merge Tags menu.
  • SendGrid dynamic templates: not rendered by Hober — those templates carry their own Handlebars variables, which SendGrid substitutes.

In the compose and template forms, the { } Insert variable button next to each field lists your workspace's known attribute keys and inserts the tag at the cursor.


Previewing

When a field contains Liquid, a Personalization preview panel appears below the content form. Pick one of your subscribers (or "No subscriber" to see fallback behavior) and click Preview to see the exact rendered output — the preview uses the same rendering engine as delivery, so what you see is what recipients get.


Validation and failure behavior

  • At save time: malformed Liquid (an unterminated {% if %}, an unknown tag, a misspelled filter) is rejected when you create the notification or save the template, with the failing field named. Requests rejected here never consume quota.
  • At send time: if a recipient's attributes cannot be read or a field fails to render for them, that recipient's delivery is recorded as failed with the reason in the delivery log — other recipients are unaffected, and the failure is not retried.
  • Unclosed output tags ({{ first_name with no closing braces) are not an error — Liquid treats them as literal text, matching Shopify behavior. The preview panel is the fastest way to catch these.

Worked example

Subscriber attributes:

{ "first_name": "Rocío", "tier": "vip", "credits": 12 }

Compose body:

{% if subscriber.tier == "vip" %}Thanks for being a VIP, {{ subscriber.first_name }}!{% else %}Hi {{ subscriber.first_name | default: "there" }}!{% endif %}
You have {{ subscriber.credits | default: 0 }} credits left.

Delivered to this subscriber:

Thanks for being a VIP, Rocío!
You have 12 credits left.