Integrations

Putting Wunder details on your Shopify emails

Get the gift message, the buyer's details and the delivery date onto your Shopify order confirmations — with the Liquid to paste and the four things that silently return nothing.

For
Owner
Takes about
30 minutes
Updated

You want your order confirmation email to show the gift message, or the buyer’s own name, or the delivery date. All of that is on the order — so it should be a matter of dropping in a variable.

It is, but not the variable you are expecting.

Wunder's print variables do not work in Shopify emails

{{ gift_message }}, {{ buyer_name }} and the rest exist only inside Wunder Delivery's own print templates. Shopify's notification templates are rendered by Shopify, which has never heard of them, so pasting one produces nothing at all — no error, just a blank space.

What you do instead is read the same underlying value that Wunder reads. Every one of those variables is a shortcut to something stored on the Shopify order, and Shopify's own Liquid can reach it.

Where to edit

In your Shopify admin: Settings → Notifications → Customer notifications, then pick the email — usually Order confirmation. Edit the code, and use Preview and Send test before you save anything your customers will see.

The four things that silently return nothing

Read these first. Each one produces a blank rather than an error, so you will not be told you got it wrong.

You must use bracket syntax, not a dot

attributes.Buyer-First-Name looks reasonable and returns nothing — Liquid reads the hyphens as minus signs and tries to do arithmetic.

Always write attributes["Buyer-First-Name"].

The card-message brackets are full-width characters

The per-product message is stored under a property whose name uses full-width brackets — 【 and 】, not the ASCII [ and ] on your keyboard.

Copy the key from this page rather than retyping it. A normal square bracket will never match, and the result is a blank.

Metafields need a definition before they will render

The AI note, the AI-checked address and edited card messages are stored as order metafields. Shopify will not output an undefined metafield in an email template — it returns blank.

Create them under Settings → Custom data → Orders, namespace wunder:

  • ai_note — multi-line text
  • ai_address — multi-line text
  • card_messages — JSON
`attributes` only exists in order notifications

Order confirmation, shipping confirmation, order edited and similar all have it. Customer-account and abandoned-checkout templates do not, so none of this works there.

What maps to what

What you want Where it lives on the order Shopify Liquid
Buyer’s first name Attribute Buyer-First-Name attributes["Buyer-First-Name"]
Buyer’s last name Attribute Buyer-Last-Name attributes["Buyer-Last-Name"]
Buyer’s phone Attribute Buyer-Phone attributes["Buyer-Phone"]
Gift message for the order Attribute Gift-Message attributes["Gift-Message"]
Message on one product Line-item property 【Card message】 Loop the line items — see below
Delivery date and time Whatever your delivery app writes attributes["Delivery-Date"]
AI note Metafield wunder.ai_note order.metafields.wunder.ai_note.value
AI-checked address Metafield wunder.ai_address order.metafields.wunder.ai_address.value

Two more worth knowing about:

  • delivery_status is a placeholder in the app — it is hard-coded to an empty string and prints blank even on your dispatch sheets. Use Shopify’s own {{ fulfillment_status }}.
  • The QR code variables point at your Shopify admin and at the driver portal. They are staff and driver links. Do not put either in a customer email.

Gift message and item list

This handles both places a per-product message can live: an edit your team made after checkout wins, and the original checkout property is the fallback.

{% assign gift = attributes["Gift-Message"] %}
{% if gift != blank %}<p><strong>Gift message:</strong> {{ gift }}</p>{% endif %}

<table width="100%" cellpadding="6" cellspacing="0">
  {% for line in line_items %}
    {% assign lk = "gid://shopify/LineItem/" | append: line.id %}
    {% assign edited = order.metafields.wunder.card_messages.value[lk] %}
    {% assign msg = edited | default: line.properties["【Card message】"] %}
    <tr>
      <td>{{ line.quantity }} &times; {{ line.title }}
        {% if msg != blank %}<br><em>{{ msg | newline_to_br }}</em>{% endif %}
      </td>
      <td align="right">{{ line.final_line_price | money }}</td>
    </tr>
  {% endfor %}
</table>
Use final_line_price, not price

line.price is the price before discounts. On a discounted order it will not match what the customer paid, which is the sort of thing that generates an email back.

The buyer’s own details

With fallbacks for orders placed before the field names were tidied up.

{% assign bf = attributes["Buyer-First-Name"] | default: attributes["Buyer first name"] %}
{% assign bl = attributes["Buyer-Last-Name"] | default: attributes["Buyer last name"] %}
{% assign bp = attributes["Buyer-Phone"] | default: attributes["Buyer phone"] %}
{% if bf != blank or bl != blank %}
  <p><strong>Ordered by:</strong> {{ bf }} {{ bl }}{% if bp != blank %} &middot; {{ bp }}{% endif %}</p>
{% endif %}
Older orders carry older field names

Orders placed before the rename use "Buyer first name", "Buyer phone", "Gift message" and "Card message" — with spaces instead of hyphens.

If you only ever email on new orders this does not matter. If you resend confirmations on historical orders, chain a | default: fallback as above, or old orders will quietly show nothing.

The phone number already has its country code

On current orders Buyer-Phone includes the + code. Older orders kept it separately in Buyer-Phone-Country.

Delivery or collection, with the date

This mirrors how the app decides between the two headings.

{% if attributes["Checkout-Method"] == "pickup" %}
  <h3>PICKUP</h3>
  <p>{{ attributes["Pickup-Location-Company"] }}<br>
     {{ attributes["Pickup-Location-Address-Line-1"] }}, {{ attributes["Pickup-Location-Address-City"] }}</p>
{% else %}
  <h3>DELIVERY</h3>
  <p>{{ shipping_address | format_address }}</p>
{% endif %}
<p>{{ attributes["Delivery-Date"] }} {{ attributes["Delivery-Time"] }}</p>
The attribute names depend on your delivery app

Delivery-Date and the pickup fields are Zapiet's names. If you use a different app, check a real order's Additional details and substitute yours — the same names you used when setting up the integration mapping.

The AI note and checked address

Only useful once the metafield definitions above exist.

{% assign checked = order.metafields.wunder.ai_address.value %}
{% assign note = order.metafields.wunder.ai_note.value %}
{% if checked != blank %}<p><strong>Address:</strong> {{ checked | newline_to_br }}</p>{% endif %}
{% if note != blank %}<p><strong>Note:</strong> {{ note | newline_to_br }}</p>{% endif %}
Think before you send these to a customer

The AI note is written for whoever is packing or delivering. It can say an address looks incomplete, or that the delivery fee does not match the destination. That is operational information, and it usually reads oddly in a customer's inbox.

These are worth adding to a staff-facing notification. Be deliberate about a customer-facing one.

Before you save

  1. Use Preview on the notification template.
  2. Use Send test to your own address.
  3. Then test against a real order that actually has the data — a preview uses sample data, and sample data has no Wunder attributes on it, so everything you just added will look blank whether or not it works.

That last point catches almost everyone.

Next

Still stuck? Ask us about this page and we will answer directly.

Spend less time processing orders and more time delivering them.

Bring delivery-date orders, printing, invoices and driver hand-offs into one Shopify-connected workflow.