@proveanything/smartlinks 1.2.4 → 1.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +85 -1
- package/{API_SUMMARY.md → dist/API_SUMMARY.md} +225 -1
- package/dist/README.md +569 -0
- package/dist/api/index.d.ts +1 -0
- package/dist/api/index.js +2 -0
- package/dist/api/realtime.d.ts +103 -0
- package/dist/api/realtime.js +113 -0
- package/dist/docs/API_SUMMARY.md +3230 -0
- package/dist/docs/i18n.md +287 -0
- package/dist/docs/liquid-templates.md +484 -0
- package/dist/docs/realtime.md +764 -0
- package/dist/docs/theme-defaults.md +100 -0
- package/dist/docs/theme.system.md +338 -0
- package/dist/docs/widgets.md +510 -0
- package/dist/http.js +132 -19
- package/dist/i18n.md +287 -0
- package/dist/liquid-templates.md +484 -0
- package/dist/realtime.md +764 -0
- package/dist/theme-defaults.md +100 -0
- package/dist/theme.system.md +338 -0
- package/dist/types/error.d.ts +69 -3
- package/dist/types/error.js +98 -1
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.js +1 -0
- package/dist/types/realtime.d.ts +44 -0
- package/dist/types/realtime.js +2 -0
- package/dist/widgets.md +510 -0
- package/docs/API_SUMMARY.md +3230 -0
- package/docs/i18n.md +287 -0
- package/docs/liquid-templates.md +484 -0
- package/docs/realtime.md +764 -0
- package/docs/theme-defaults.md +100 -0
- package/docs/theme.system.md +338 -0
- package/docs/widgets.md +510 -0
- package/package.json +4 -4
- package/dist/api/actions.d.ts +0 -32
- package/dist/api/actions.js +0 -99
- package/dist/build-docs.js +0 -61
- package/dist/types/actions.d.ts +0 -123
- package/dist/types/actions.js +0 -2
|
@@ -0,0 +1,484 @@
|
|
|
1
|
+
# Liquid Templates in SmartLinks
|
|
2
|
+
|
|
3
|
+
Liquid is a templating language that allows you to dynamically insert data into text content. SmartLinks uses Liquid Templates in various APIs—such as email templates, notification messages, and dynamic content—to personalize communications with real-time data from your collections, products, proofs, and users.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## What are Liquid Templates?
|
|
8
|
+
|
|
9
|
+
Liquid is an open-source template language created by Shopify. It uses a simple syntax with two main components:
|
|
10
|
+
|
|
11
|
+
- **Output tags** `{{ }}` — Insert dynamic values
|
|
12
|
+
- **Logic tags** `{% %}` — Control flow (if/else, loops, etc.)
|
|
13
|
+
|
|
14
|
+
### Basic Example
|
|
15
|
+
|
|
16
|
+
```liquid
|
|
17
|
+
Hello {{ contact.name }},
|
|
18
|
+
|
|
19
|
+
Thank you for registering your {{ product.name }}!
|
|
20
|
+
Your proof ID is: {{ proof.id }}
|
|
21
|
+
|
|
22
|
+
{% if proof.claimed %}
|
|
23
|
+
This item was claimed on {{ proof.claimedAt | date: "%B %d, %Y" }}.
|
|
24
|
+
{% endif %}
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## Core Data Objects
|
|
30
|
+
|
|
31
|
+
SmartLinks provides several core objects that can be accessed in Liquid Templates. The available objects depend on the context (e.g., a proof-level template has access to `proof`, `product`, and `collection`).
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
### Collection
|
|
36
|
+
|
|
37
|
+
A **Collection** represents a top-level business, brand, or organization. All products belong to a collection.
|
|
38
|
+
|
|
39
|
+
| Field | Type | Description |
|
|
40
|
+
|-------|------|-------------|
|
|
41
|
+
| `collection.id` | string | Unique identifier |
|
|
42
|
+
| `collection.name` | string | Display name of the collection |
|
|
43
|
+
| `collection.description` | string | Description text |
|
|
44
|
+
| `collection.slug` | string | URL-friendly identifier |
|
|
45
|
+
| `collection.logoUrl` | string | URL to the collection's logo image |
|
|
46
|
+
| `collection.websiteUrl` | string | Primary website URL |
|
|
47
|
+
| `collection.metadata` | object | Custom key-value metadata |
|
|
48
|
+
| `collection.createdAt` | datetime | When the collection was created |
|
|
49
|
+
| `collection.updatedAt` | datetime | When the collection was last updated |
|
|
50
|
+
|
|
51
|
+
#### Example Usage
|
|
52
|
+
|
|
53
|
+
```liquid
|
|
54
|
+
Welcome to {{ collection.name }}!
|
|
55
|
+
|
|
56
|
+
{% if collection.websiteUrl %}
|
|
57
|
+
Visit us at {{ collection.websiteUrl }}
|
|
58
|
+
{% endif %}
|
|
59
|
+
|
|
60
|
+
<img src="{{ collection.logoUrl }}" alt="{{ collection.name }} logo" />
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
### Product
|
|
66
|
+
|
|
67
|
+
A **Product** represents a type or definition of a physical or digital item. Products belong to a collection and can have many proofs (instances).
|
|
68
|
+
|
|
69
|
+
| Field | Type | Description |
|
|
70
|
+
|-------|------|-------------|
|
|
71
|
+
| `product.id` | string | Unique identifier |
|
|
72
|
+
| `product.name` | string | Product name |
|
|
73
|
+
| `product.description` | string | Product description |
|
|
74
|
+
| `product.sku` | string | Stock keeping unit |
|
|
75
|
+
| `product.slug` | string | URL-friendly identifier |
|
|
76
|
+
| `product.imageUrl` | string | Primary product image URL |
|
|
77
|
+
| `product.images` | array | Array of image URLs |
|
|
78
|
+
| `product.category` | string | Product category |
|
|
79
|
+
| `product.tags` | array | Array of tag strings |
|
|
80
|
+
| `product.metadata` | object | Custom key-value metadata |
|
|
81
|
+
| `product.createdAt` | datetime | When the product was created |
|
|
82
|
+
| `product.updatedAt` | datetime | When the product was last updated |
|
|
83
|
+
|
|
84
|
+
#### Example Usage
|
|
85
|
+
|
|
86
|
+
```liquid
|
|
87
|
+
Your {{ product.name }} (SKU: {{ product.sku }})
|
|
88
|
+
|
|
89
|
+
{{ product.description }}
|
|
90
|
+
|
|
91
|
+
{% if product.tags.size > 0 %}
|
|
92
|
+
Tags: {{ product.tags | join: ", " }}
|
|
93
|
+
{% endif %}
|
|
94
|
+
|
|
95
|
+
{% for image in product.images %}
|
|
96
|
+
<img src="{{ image }}" alt="{{ product.name }}" />
|
|
97
|
+
{% endfor %}
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
---
|
|
101
|
+
|
|
102
|
+
### Proof
|
|
103
|
+
|
|
104
|
+
A **Proof** is a specific instance of a product—think of it as a unique digital certificate for a physical item. Proofs can be claimed by users and carry ownership information.
|
|
105
|
+
|
|
106
|
+
| Field | Type | Description |
|
|
107
|
+
|-------|------|-------------|
|
|
108
|
+
| `proof.id` | string | Unique identifier |
|
|
109
|
+
| `proof.serialNumber` | string | Human-readable serial number |
|
|
110
|
+
| `proof.claimed` | boolean | Whether the proof has been claimed |
|
|
111
|
+
| `proof.claimedAt` | datetime | When the proof was claimed |
|
|
112
|
+
| `proof.claimedBy` | string | User ID of the claimer |
|
|
113
|
+
| `proof.status` | string | Current status (e.g., "active", "transferred") |
|
|
114
|
+
| `proof.nfcTagId` | string | Associated NFC tag ID (if applicable) |
|
|
115
|
+
| `proof.qrCode` | string | QR code identifier |
|
|
116
|
+
| `proof.shortCode` | string | Short code for easy lookup |
|
|
117
|
+
| `proof.metadata` | object | Custom key-value metadata |
|
|
118
|
+
| `proof.createdAt` | datetime | When the proof was created |
|
|
119
|
+
| `proof.updatedAt` | datetime | When the proof was last updated |
|
|
120
|
+
|
|
121
|
+
#### Example Usage
|
|
122
|
+
|
|
123
|
+
```liquid
|
|
124
|
+
Proof of Authenticity
|
|
125
|
+
|
|
126
|
+
Serial Number: {{ proof.serialNumber }}
|
|
127
|
+
Status: {{ proof.status }}
|
|
128
|
+
|
|
129
|
+
{% if proof.claimed %}
|
|
130
|
+
Claimed on: {{ proof.claimedAt | date: "%B %d, %Y at %H:%M" }}
|
|
131
|
+
{% else %}
|
|
132
|
+
This item has not been claimed yet.
|
|
133
|
+
{% endif %}
|
|
134
|
+
|
|
135
|
+
{% if proof.metadata.warrantyExpiry %}
|
|
136
|
+
Warranty expires: {{ proof.metadata.warrantyExpiry | date: "%B %d, %Y" }}
|
|
137
|
+
{% endif %}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
---
|
|
141
|
+
|
|
142
|
+
### Contact
|
|
143
|
+
|
|
144
|
+
A **Contact** represents a customer or user in the system. Contacts are associated with a collection and can own multiple proofs.
|
|
145
|
+
|
|
146
|
+
| Field | Type | Description |
|
|
147
|
+
|-------|------|-------------|
|
|
148
|
+
| `contact.id` | string | Unique identifier |
|
|
149
|
+
| `contact.email` | string | Email address |
|
|
150
|
+
| `contact.name` | string | Full name |
|
|
151
|
+
| `contact.firstName` | string | First name |
|
|
152
|
+
| `contact.lastName` | string | Last name |
|
|
153
|
+
| `contact.phone` | string | Phone number |
|
|
154
|
+
| `contact.locale` | string | Preferred language/locale (e.g., "en", "de") |
|
|
155
|
+
| `contact.timezone` | string | Preferred timezone |
|
|
156
|
+
| `contact.avatarUrl` | string | Profile picture URL |
|
|
157
|
+
| `contact.metadata` | object | Custom key-value metadata |
|
|
158
|
+
| `contact.tags` | array | Array of tag strings for segmentation |
|
|
159
|
+
| `contact.createdAt` | datetime | When the contact was created |
|
|
160
|
+
| `contact.updatedAt` | datetime | When the contact was last updated |
|
|
161
|
+
| `contact.lastSeenAt` | datetime | Last activity timestamp |
|
|
162
|
+
|
|
163
|
+
#### Example Usage
|
|
164
|
+
|
|
165
|
+
```liquid
|
|
166
|
+
Hi {{ contact.firstName | default: contact.name | default: "there" }},
|
|
167
|
+
|
|
168
|
+
{% if contact.locale == "de" %}
|
|
169
|
+
Willkommen!
|
|
170
|
+
{% elsif contact.locale == "fr" %}
|
|
171
|
+
Bienvenue!
|
|
172
|
+
{% else %}
|
|
173
|
+
Welcome!
|
|
174
|
+
{% endif %}
|
|
175
|
+
|
|
176
|
+
{% if contact.phone %}
|
|
177
|
+
We'll send updates to {{ contact.phone }}.
|
|
178
|
+
{% endif %}
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
---
|
|
182
|
+
|
|
183
|
+
### User (Account)
|
|
184
|
+
|
|
185
|
+
A **User** represents an authenticated account in the system. This is typically the logged-in user performing an action.
|
|
186
|
+
|
|
187
|
+
| Field | Type | Description |
|
|
188
|
+
|-------|------|-------------|
|
|
189
|
+
| `user.id` | string | Unique identifier |
|
|
190
|
+
| `user.email` | string | Email address |
|
|
191
|
+
| `user.name` | string | Display name |
|
|
192
|
+
| `user.admin` | boolean | Whether user has admin privileges |
|
|
193
|
+
| `user.avatarUrl` | string | Profile picture URL |
|
|
194
|
+
| `user.createdAt` | datetime | Account creation date |
|
|
195
|
+
|
|
196
|
+
#### Example Usage
|
|
197
|
+
|
|
198
|
+
```liquid
|
|
199
|
+
Logged in as: {{ user.name }} ({{ user.email }})
|
|
200
|
+
|
|
201
|
+
{% if user.admin %}
|
|
202
|
+
🔐 You have administrator access.
|
|
203
|
+
{% endif %}
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
---
|
|
207
|
+
|
|
208
|
+
### Attestation
|
|
209
|
+
|
|
210
|
+
An **Attestation** is flexible data attached to a specific proof. It's used to store additional information like warranty registrations, tasting notes, service records, etc.
|
|
211
|
+
|
|
212
|
+
| Field | Type | Description |
|
|
213
|
+
|-------|------|-------------|
|
|
214
|
+
| `attestation.id` | string | Unique identifier |
|
|
215
|
+
| `attestation.type` | string | Attestation type (app-defined) |
|
|
216
|
+
| `attestation.data` | object | The attestation payload (varies by type) |
|
|
217
|
+
| `attestation.createdBy` | string | User ID who created it |
|
|
218
|
+
| `attestation.createdAt` | datetime | When the attestation was created |
|
|
219
|
+
| `attestation.updatedAt` | datetime | When the attestation was last updated |
|
|
220
|
+
|
|
221
|
+
#### Example Usage
|
|
222
|
+
|
|
223
|
+
```liquid
|
|
224
|
+
{% if attestation.type == "warranty_registration" %}
|
|
225
|
+
Warranty Registration Details:
|
|
226
|
+
- Registered: {{ attestation.createdAt | date: "%B %d, %Y" }}
|
|
227
|
+
- Purchase Date: {{ attestation.data.purchaseDate }}
|
|
228
|
+
- Store: {{ attestation.data.storeName }}
|
|
229
|
+
{% endif %}
|
|
230
|
+
|
|
231
|
+
{% if attestation.type == "tasting_note" %}
|
|
232
|
+
🍷 Tasting Note by {{ attestation.data.author }}:
|
|
233
|
+
"{{ attestation.data.notes }}"
|
|
234
|
+
Rating: {{ attestation.data.rating }}/5
|
|
235
|
+
{% endif %}
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
---
|
|
239
|
+
|
|
240
|
+
## Liquid Filters
|
|
241
|
+
|
|
242
|
+
Liquid provides built-in filters to transform data. Common filters include:
|
|
243
|
+
|
|
244
|
+
### Text Filters
|
|
245
|
+
|
|
246
|
+
| Filter | Description | Example |
|
|
247
|
+
|--------|-------------|---------|
|
|
248
|
+
| `upcase` | Convert to uppercase | `{{ product.name \| upcase }}` |
|
|
249
|
+
| `downcase` | Convert to lowercase | `{{ product.name \| downcase }}` |
|
|
250
|
+
| `capitalize` | Capitalize first letter | `{{ contact.name \| capitalize }}` |
|
|
251
|
+
| `truncate` | Limit string length | `{{ product.description \| truncate: 100 }}` |
|
|
252
|
+
| `strip_html` | Remove HTML tags | `{{ content \| strip_html }}` |
|
|
253
|
+
| `escape` | HTML escape special chars | `{{ user_input \| escape }}` |
|
|
254
|
+
| `default` | Fallback value if empty | `{{ contact.name \| default: "Customer" }}` |
|
|
255
|
+
|
|
256
|
+
### Date Filters
|
|
257
|
+
|
|
258
|
+
| Filter | Description | Example |
|
|
259
|
+
|--------|-------------|---------|
|
|
260
|
+
| `date` | Format a date | `{{ proof.claimedAt \| date: "%B %d, %Y" }}` |
|
|
261
|
+
|
|
262
|
+
Common date formats:
|
|
263
|
+
- `%B %d, %Y` → January 15, 2025
|
|
264
|
+
- `%Y-%m-%d` → 2025-01-15
|
|
265
|
+
- `%d/%m/%Y` → 15/01/2025
|
|
266
|
+
- `%H:%M` → 14:30
|
|
267
|
+
|
|
268
|
+
### Array Filters
|
|
269
|
+
|
|
270
|
+
| Filter | Description | Example |
|
|
271
|
+
|--------|-------------|---------|
|
|
272
|
+
| `join` | Join array elements | `{{ product.tags \| join: ", " }}` |
|
|
273
|
+
| `first` | Get first element | `{{ product.images \| first }}` |
|
|
274
|
+
| `last` | Get last element | `{{ product.images \| last }}` |
|
|
275
|
+
| `size` | Get array length | `{{ product.tags.size }}` |
|
|
276
|
+
| `sort` | Sort array | `{{ items \| sort: "name" }}` |
|
|
277
|
+
|
|
278
|
+
### Number Filters
|
|
279
|
+
|
|
280
|
+
| Filter | Description | Example |
|
|
281
|
+
|--------|-------------|---------|
|
|
282
|
+
| `plus` | Add | `{{ count \| plus: 1 }}` |
|
|
283
|
+
| `minus` | Subtract | `{{ total \| minus: discount }}` |
|
|
284
|
+
| `times` | Multiply | `{{ price \| times: quantity }}` |
|
|
285
|
+
| `divided_by` | Divide | `{{ total \| divided_by: 2 }}` |
|
|
286
|
+
| `round` | Round number | `{{ average \| round: 2 }}` |
|
|
287
|
+
|
|
288
|
+
---
|
|
289
|
+
|
|
290
|
+
## Control Flow
|
|
291
|
+
|
|
292
|
+
### Conditionals
|
|
293
|
+
|
|
294
|
+
```liquid
|
|
295
|
+
{% if proof.claimed %}
|
|
296
|
+
This item is claimed.
|
|
297
|
+
{% elsif proof.status == "pending" %}
|
|
298
|
+
Claim pending verification.
|
|
299
|
+
{% else %}
|
|
300
|
+
Available to claim.
|
|
301
|
+
{% endif %}
|
|
302
|
+
|
|
303
|
+
{% unless contact.email %}
|
|
304
|
+
No email on file.
|
|
305
|
+
{% endunless %}
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
### Operators
|
|
309
|
+
|
|
310
|
+
| Operator | Description |
|
|
311
|
+
|----------|-------------|
|
|
312
|
+
| `==` | Equals |
|
|
313
|
+
| `!=` | Not equals |
|
|
314
|
+
| `>` | Greater than |
|
|
315
|
+
| `<` | Less than |
|
|
316
|
+
| `>=` | Greater than or equal |
|
|
317
|
+
| `<=` | Less than or equal |
|
|
318
|
+
| `or` | Logical OR |
|
|
319
|
+
| `and` | Logical AND |
|
|
320
|
+
| `contains` | String/array contains |
|
|
321
|
+
|
|
322
|
+
```liquid
|
|
323
|
+
{% if product.tags contains "premium" %}
|
|
324
|
+
🌟 Premium Product
|
|
325
|
+
{% endif %}
|
|
326
|
+
|
|
327
|
+
{% if contact.email and proof.claimed %}
|
|
328
|
+
Send confirmation to {{ contact.email }}
|
|
329
|
+
{% endif %}
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
### Loops
|
|
333
|
+
|
|
334
|
+
```liquid
|
|
335
|
+
{% for tag in product.tags %}
|
|
336
|
+
<span class="tag">{{ tag }}</span>
|
|
337
|
+
{% endfor %}
|
|
338
|
+
|
|
339
|
+
{% for image in product.images limit: 3 %}
|
|
340
|
+
<img src="{{ image }}" alt="{{ product.name }} image {{ forloop.index }}" />
|
|
341
|
+
{% endfor %}
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
Loop variables:
|
|
345
|
+
- `forloop.index` — Current iteration (1-indexed)
|
|
346
|
+
- `forloop.index0` — Current iteration (0-indexed)
|
|
347
|
+
- `forloop.first` — Is this the first iteration?
|
|
348
|
+
- `forloop.last` — Is this the last iteration?
|
|
349
|
+
- `forloop.length` — Total number of iterations
|
|
350
|
+
|
|
351
|
+
---
|
|
352
|
+
|
|
353
|
+
## Common Use Cases
|
|
354
|
+
|
|
355
|
+
### Email Templates
|
|
356
|
+
|
|
357
|
+
```liquid
|
|
358
|
+
Subject: Your {{ product.name }} has been registered!
|
|
359
|
+
|
|
360
|
+
Hi {{ contact.firstName | default: "there" }},
|
|
361
|
+
|
|
362
|
+
Great news! Your {{ product.name }} (Serial: {{ proof.serialNumber }})
|
|
363
|
+
has been successfully registered to your account.
|
|
364
|
+
|
|
365
|
+
{% if product.metadata.warrantyYears %}
|
|
366
|
+
Your warranty is valid for {{ product.metadata.warrantyYears }} years
|
|
367
|
+
from the date of purchase.
|
|
368
|
+
{% endif %}
|
|
369
|
+
|
|
370
|
+
If you have any questions, please contact {{ collection.name }} support.
|
|
371
|
+
|
|
372
|
+
Best regards,
|
|
373
|
+
The {{ collection.name }} Team
|
|
374
|
+
```
|
|
375
|
+
|
|
376
|
+
### Notification Messages
|
|
377
|
+
|
|
378
|
+
```liquid
|
|
379
|
+
🎉 {{ contact.firstName }}, your {{ product.name }} is now verified!
|
|
380
|
+
Proof ID: {{ proof.shortCode }}
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
### Dynamic Content Blocks
|
|
384
|
+
|
|
385
|
+
```liquid
|
|
386
|
+
{% if proof.metadata.tier == "gold" %}
|
|
387
|
+
<div class="gold-benefits">
|
|
388
|
+
As a Gold member, you get exclusive access to...
|
|
389
|
+
</div>
|
|
390
|
+
{% elsif proof.metadata.tier == "silver" %}
|
|
391
|
+
<div class="silver-benefits">
|
|
392
|
+
Your Silver membership includes...
|
|
393
|
+
</div>
|
|
394
|
+
{% endif %}
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
### Multilingual Content
|
|
398
|
+
|
|
399
|
+
```liquid
|
|
400
|
+
{% case contact.locale %}
|
|
401
|
+
{% when "de" %}
|
|
402
|
+
Vielen Dank für Ihre Registrierung!
|
|
403
|
+
{% when "fr" %}
|
|
404
|
+
Merci pour votre inscription!
|
|
405
|
+
{% when "es" %}
|
|
406
|
+
¡Gracias por registrarte!
|
|
407
|
+
{% else %}
|
|
408
|
+
Thank you for registering!
|
|
409
|
+
{% endcase %}
|
|
410
|
+
```
|
|
411
|
+
|
|
412
|
+
---
|
|
413
|
+
|
|
414
|
+
## Accessing Nested Data
|
|
415
|
+
|
|
416
|
+
Use dot notation to access nested fields in metadata or data objects:
|
|
417
|
+
|
|
418
|
+
```liquid
|
|
419
|
+
{{ product.metadata.manufacturer }}
|
|
420
|
+
{{ attestation.data.warranty.expiryDate }}
|
|
421
|
+
{{ collection.metadata.social.twitter }}
|
|
422
|
+
```
|
|
423
|
+
|
|
424
|
+
For dynamic keys, you may need to use bracket notation (if supported):
|
|
425
|
+
|
|
426
|
+
```liquid
|
|
427
|
+
{{ product.metadata["custom-field"] }}
|
|
428
|
+
```
|
|
429
|
+
|
|
430
|
+
---
|
|
431
|
+
|
|
432
|
+
## Best Practices
|
|
433
|
+
|
|
434
|
+
1. **Always use `default` filter** for optional fields to avoid blank output:
|
|
435
|
+
```liquid
|
|
436
|
+
{{ contact.name | default: "Valued Customer" }}
|
|
437
|
+
```
|
|
438
|
+
|
|
439
|
+
2. **Escape user-generated content** when outputting as HTML:
|
|
440
|
+
```liquid
|
|
441
|
+
{{ attestation.data.userNotes | escape }}
|
|
442
|
+
```
|
|
443
|
+
|
|
444
|
+
3. **Check for existence** before accessing nested data:
|
|
445
|
+
```liquid
|
|
446
|
+
{% if proof.metadata.warranty %}
|
|
447
|
+
Warranty: {{ proof.metadata.warranty.type }}
|
|
448
|
+
{% endif %}
|
|
449
|
+
```
|
|
450
|
+
|
|
451
|
+
4. **Use meaningful fallbacks** for a better user experience:
|
|
452
|
+
```liquid
|
|
453
|
+
Hi {{ contact.firstName | default: contact.name | default: "there" }},
|
|
454
|
+
```
|
|
455
|
+
|
|
456
|
+
5. **Format dates appropriately** for the user's locale:
|
|
457
|
+
```liquid
|
|
458
|
+
{{ proof.claimedAt | date: "%d %B %Y" }}
|
|
459
|
+
```
|
|
460
|
+
|
|
461
|
+
---
|
|
462
|
+
|
|
463
|
+
## API Context
|
|
464
|
+
|
|
465
|
+
Different APIs provide different objects in the Liquid context:
|
|
466
|
+
|
|
467
|
+
| API / Feature | Available Objects |
|
|
468
|
+
|---------------|-------------------|
|
|
469
|
+
| Email Templates | `collection`, `product`, `proof`, `contact`, `attestation` |
|
|
470
|
+
| Push Notifications | `collection`, `product`, `proof`, `contact` |
|
|
471
|
+
| SMS Messages | `collection`, `product`, `proof`, `contact` |
|
|
472
|
+
| Wallet Passes | `collection`, `product`, `proof`, `contact` |
|
|
473
|
+
| Journey Actions | `collection`, `product`, `proof`, `contact`, `event` |
|
|
474
|
+
| Broadcast Campaigns | `collection`, `contact`, `segment` |
|
|
475
|
+
|
|
476
|
+
Check the specific API documentation for the exact objects available in each context.
|
|
477
|
+
|
|
478
|
+
---
|
|
479
|
+
|
|
480
|
+
## Further Resources
|
|
481
|
+
|
|
482
|
+
- [Liquid Template Language Documentation](https://shopify.github.io/liquid/)
|
|
483
|
+
- [Liquid for Designers](https://github.com/Shopify/liquid/wiki/Liquid-for-Designers)
|
|
484
|
+
- SmartLinks Template API Reference
|