@proveanything/smartlinks 1.3.44 → 1.3.46
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/dist/docs/API_SUMMARY.md +10 -2
- package/dist/docs/liquid-templates.md +158 -88
- package/dist/http.d.ts +20 -0
- package/dist/http.js +43 -2
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/types/collection.d.ts +4 -0
- package/docs/API_SUMMARY.md +10 -2
- package/docs/liquid-templates.md +158 -88
- package/package.json +1 -1
package/dist/docs/API_SUMMARY.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Smartlinks API Summary
|
|
2
2
|
|
|
3
|
-
Version: 1.3.
|
|
3
|
+
Version: 1.3.46 | Generated: 2026-02-19T21:09:09.402Z
|
|
4
4
|
|
|
5
5
|
This is a concise summary of all available API functions and types.
|
|
6
6
|
|
|
@@ -87,7 +87,10 @@ Return whether proxy mode is currently enabled.
|
|
|
87
87
|
extraHeaders?: Record<string, string>
|
|
88
88
|
iframeAutoResize?: boolean // default true when in iframe
|
|
89
89
|
logger?: Logger // optional console-like or function to enable verbose logging
|
|
90
|
-
|
|
90
|
+
/**
|
|
91
|
+
* When true, bypasses the idempotency guard and forces a full re-initialization.
|
|
92
|
+
* Use only when you intentionally need to reset all SDK state (e.g. in tests or
|
|
93
|
+
* when switching accounts) → `void`
|
|
91
94
|
Call this once (e.g. at app startup) to configure baseURL/auth.
|
|
92
95
|
|
|
93
96
|
**setNgrokSkipBrowserWarning**(flag: boolean) → `void`
|
|
@@ -102,6 +105,9 @@ Allows setting the bearerToken at runtime (e.g. after login/logout).
|
|
|
102
105
|
**getBaseURL**() → `string | null`
|
|
103
106
|
Get the currently configured API base URL. Returns null if initializeApi() has not been called yet.
|
|
104
107
|
|
|
108
|
+
**isInitialized**() → `boolean`
|
|
109
|
+
Returns true if initializeApi() has been called at least once. Useful for guards in widgets or shared modules that want to skip initialization when another module has already done it. ```ts if (!isInitialized()) { initializeApi({ baseURL: 'https://smartlinks.app/api/v1' }) } ```
|
|
110
|
+
|
|
105
111
|
**proxyUploadFormData**(path: string,
|
|
106
112
|
formData: FormData,
|
|
107
113
|
onProgress?: (percent: number) → `void`
|
|
@@ -1504,6 +1510,8 @@ interface Collection {
|
|
|
1504
1510
|
redirectUrl?: string // Whether the collection has a custom domain
|
|
1505
1511
|
shortId: string, // The shortId of this collection
|
|
1506
1512
|
dark?: boolean // if dark mode is enabled for this collection
|
|
1513
|
+
primaryColor?: string
|
|
1514
|
+
secondaryColor?: string
|
|
1507
1515
|
portalUrl?: string // URL for the collection's portal (if applicable)
|
|
1508
1516
|
allowAutoGenerateClaims?: boolean
|
|
1509
1517
|
defaultAuthKitId: string // default auth kit for this collection, used for auth
|
|
@@ -41,23 +41,43 @@ A **Collection** represents a top-level business, brand, or organization. All pr
|
|
|
41
41
|
| `collection.id` | string | Unique identifier |
|
|
42
42
|
| `collection.title` | string | Display title of the collection |
|
|
43
43
|
| `collection.description` | string | Description text |
|
|
44
|
-
| `collection.
|
|
45
|
-
| `collection.
|
|
46
|
-
| `collection.
|
|
47
|
-
| `collection.
|
|
48
|
-
| `collection.
|
|
49
|
-
| `collection.
|
|
44
|
+
| `collection.shortId` | string | Short identifier for the collection |
|
|
45
|
+
| `collection.logoImage.url` | string | URL to the collection's logo image |
|
|
46
|
+
| `collection.logoImage.thumbnails.x100` | string | 100px thumbnail |
|
|
47
|
+
| `collection.logoImage.thumbnails.x200` | string | 200px thumbnail |
|
|
48
|
+
| `collection.logoImage.thumbnails.x512` | string | 512px thumbnail |
|
|
49
|
+
| `collection.headerImage.url` | string | URL to collection header/hero image |
|
|
50
|
+
| `collection.headerImage.thumbnails.*` | string | Header image thumbnails (x100, x200, x512) |
|
|
51
|
+
| `collection.loaderImage.url` | string | URL to collection loader image |
|
|
52
|
+
| `collection.primaryColor` | string | Primary theme color (hex code) |
|
|
53
|
+
| `collection.secondaryColor` | string | Secondary theme color (hex code) |
|
|
54
|
+
| `collection.dark` | boolean | Whether dark mode is enabled |
|
|
55
|
+
| `collection.portalUrl` | string | URL for the collection's portal |
|
|
56
|
+
| `collection.redirectUrl` | string | Custom domain redirect URL |
|
|
57
|
+
| `collection.roles` | object | User roles mapping (userId → role) |
|
|
58
|
+
| `collection.groupTags` | array | Array of group tag names |
|
|
59
|
+
| `collection.languages` | array | Array of supported language objects |
|
|
60
|
+
| `collection.defaultAuthKitId` | string | Default auth kit ID |
|
|
61
|
+
| `collection.allowAutoGenerateClaims` | boolean | Allow claiming without proof ID |
|
|
50
62
|
|
|
51
63
|
#### Example Usage
|
|
52
64
|
|
|
53
65
|
```liquid
|
|
54
|
-
Welcome to {{ collection.
|
|
66
|
+
Welcome to {{ collection.title }}!
|
|
55
67
|
|
|
56
|
-
{% if collection.
|
|
57
|
-
Visit
|
|
68
|
+
{% if collection.portalUrl %}
|
|
69
|
+
Visit our portal at {{ collection.portalUrl }}
|
|
58
70
|
{% endif %}
|
|
59
71
|
|
|
60
|
-
|
|
72
|
+
{% if collection.logoImage %}
|
|
73
|
+
<img src="{{ collection.logoImage.url }}" alt="{{ collection.title }} logo" />
|
|
74
|
+
<!-- Or use a thumbnail: -->
|
|
75
|
+
<img src="{{ collection.logoImage.thumbnails.x200 }}" alt="{{ collection.title }} logo" />
|
|
76
|
+
{% endif %}
|
|
77
|
+
|
|
78
|
+
{% if collection.dark %}
|
|
79
|
+
<!-- Dark mode is enabled -->
|
|
80
|
+
{% endif %}
|
|
61
81
|
```
|
|
62
82
|
|
|
63
83
|
---
|
|
@@ -70,31 +90,44 @@ A **Product** represents a type or definition of a physical or digital item. Pro
|
|
|
70
90
|
|-------|------|-------------|
|
|
71
91
|
| `product.id` | string | Unique identifier |
|
|
72
92
|
| `product.name` | string | Product name |
|
|
93
|
+
| `product.collectionId` | string | ID of the parent collection |
|
|
73
94
|
| `product.description` | string | Product description |
|
|
74
|
-
| `product.
|
|
75
|
-
| `product.
|
|
76
|
-
| `product.
|
|
77
|
-
| `product.
|
|
78
|
-
| `product.
|
|
79
|
-
| `product.
|
|
80
|
-
| `product.
|
|
81
|
-
| `product.
|
|
82
|
-
| `product.
|
|
95
|
+
| `product.gtin` | string | Global Trade Item Number |
|
|
96
|
+
| `product.type` | string | Product type from standard types |
|
|
97
|
+
| `product.heroImage.url` | string | Primary product image URL |
|
|
98
|
+
| `product.heroImage.thumbnails.x100` | string | 100px thumbnail |
|
|
99
|
+
| `product.heroImage.thumbnails.x200` | string | 200px thumbnail |
|
|
100
|
+
| `product.heroImage.thumbnails.x512` | string | 512px thumbnail |
|
|
101
|
+
| `product.tags` | object | Tag map with boolean values |
|
|
102
|
+
| `product.data` | object | Flexible key-value data map |
|
|
103
|
+
| `product.admin` | object | Admin-only configuration |
|
|
104
|
+
| `product.admin.allowAutoGenerateClaims` | boolean | Allow claiming without proof ID |
|
|
105
|
+
| `product.admin.lastSerialId` | number | Last generated serial ID |
|
|
83
106
|
|
|
84
107
|
#### Example Usage
|
|
85
108
|
|
|
86
109
|
```liquid
|
|
87
|
-
Your {{ product.name }}
|
|
110
|
+
Your {{ product.name }}
|
|
88
111
|
|
|
89
112
|
{{ product.description }}
|
|
90
113
|
|
|
91
|
-
{% if product.
|
|
92
|
-
|
|
114
|
+
{% if product.gtin %}
|
|
115
|
+
GTIN: {{ product.gtin }}
|
|
93
116
|
{% endif %}
|
|
94
117
|
|
|
95
|
-
{%
|
|
96
|
-
<img src="{{
|
|
97
|
-
|
|
118
|
+
{% if product.heroImage %}
|
|
119
|
+
<img src="{{ product.heroImage.url }}" alt="{{ product.name }}" />
|
|
120
|
+
<!-- Or use a thumbnail: -->
|
|
121
|
+
<img src="{{ product.heroImage.thumbnails.x512 }}" alt="{{ product.name }}" />
|
|
122
|
+
{% endif %}
|
|
123
|
+
|
|
124
|
+
{% if product.tags.premium %}
|
|
125
|
+
🌟 Premium Product
|
|
126
|
+
{% endif %}
|
|
127
|
+
|
|
128
|
+
{% if product.data.warranty_years %}
|
|
129
|
+
Warranty: {{ product.data.warranty_years }} years
|
|
130
|
+
{% endif %}
|
|
98
131
|
```
|
|
99
132
|
|
|
100
133
|
---
|
|
@@ -106,34 +139,50 @@ A **Proof** is a specific instance of a product—think of it as a unique digita
|
|
|
106
139
|
| Field | Type | Description |
|
|
107
140
|
|-------|------|-------------|
|
|
108
141
|
| `proof.id` | string | Unique identifier |
|
|
109
|
-
| `proof.
|
|
110
|
-
| `proof.
|
|
111
|
-
| `proof.
|
|
112
|
-
| `proof.
|
|
113
|
-
| `proof.
|
|
114
|
-
| `proof.
|
|
115
|
-
| `proof.
|
|
116
|
-
| `proof.shortCode` | string | Short code for easy lookup |
|
|
117
|
-
| `proof.metadata` | object | Custom key-value metadata |
|
|
142
|
+
| `proof.collectionId` | string | ID of the parent collection |
|
|
143
|
+
| `proof.productId` | string | ID of the associated product |
|
|
144
|
+
| `proof.tokenId` | string | Unique token identifier |
|
|
145
|
+
| `proof.userId` | string | User ID of the owner |
|
|
146
|
+
| `proof.claimable` | boolean | Whether the proof can be claimed |
|
|
147
|
+
| `proof.virtual` | boolean | Whether this is a virtual proof |
|
|
148
|
+
| `proof.values` | object | Arbitrary key-value pairs for proof data |
|
|
118
149
|
| `proof.createdAt` | datetime | When the proof was created |
|
|
119
|
-
|
|
150
|
+
|
|
151
|
+
**Note**: Proof `values` object can contain any custom fields. Common examples:
|
|
152
|
+
- `proof.values.serialNumber` - Serial number
|
|
153
|
+
- `proof.values.claimedAt` - Claim timestamp
|
|
154
|
+
- `proof.values.status` - Current status
|
|
155
|
+
- `proof.values.warrantyExpiry` - Warranty expiration
|
|
120
156
|
|
|
121
157
|
#### Example Usage
|
|
122
158
|
|
|
123
159
|
```liquid
|
|
124
160
|
Proof of Authenticity
|
|
125
161
|
|
|
126
|
-
|
|
127
|
-
|
|
162
|
+
{% if proof.values.serialNumber %}
|
|
163
|
+
Serial Number: {{ proof.values.serialNumber }}
|
|
164
|
+
{% endif %}
|
|
128
165
|
|
|
129
|
-
{% if proof.
|
|
130
|
-
|
|
166
|
+
{% if proof.values.status %}
|
|
167
|
+
Status: {{ proof.values.status }}
|
|
168
|
+
{% endif %}
|
|
169
|
+
|
|
170
|
+
{% if proof.claimable %}
|
|
171
|
+
This item is available to claim.
|
|
131
172
|
{% else %}
|
|
132
|
-
This item has
|
|
173
|
+
This item has been claimed.
|
|
133
174
|
{% endif %}
|
|
134
175
|
|
|
135
|
-
{% if proof.
|
|
136
|
-
|
|
176
|
+
{% if proof.virtual %}
|
|
177
|
+
🌐 Digital Product
|
|
178
|
+
{% endif %}
|
|
179
|
+
|
|
180
|
+
{% if proof.values.claimedAt %}
|
|
181
|
+
Claimed on: {{ proof.values.claimedAt | date: "%B %d, %Y at %H:%M" }}
|
|
182
|
+
{% endif %}
|
|
183
|
+
|
|
184
|
+
{% if proof.values.warrantyExpiry %}
|
|
185
|
+
Warranty expires: {{ proof.values.warrantyExpiry | date: "%B %d, %Y" }}
|
|
137
186
|
{% endif %}
|
|
138
187
|
```
|
|
139
188
|
|
|
@@ -145,25 +194,32 @@ A **Contact** represents a customer or user in the system. Contacts are associat
|
|
|
145
194
|
|
|
146
195
|
| Field | Type | Description |
|
|
147
196
|
|-------|------|-------------|
|
|
148
|
-
| `contact.
|
|
149
|
-
| `contact.
|
|
150
|
-
| `contact.
|
|
197
|
+
| `contact.contactId` | string | Unique identifier |
|
|
198
|
+
| `contact.orgId` | string | Organization/collection ID |
|
|
199
|
+
| `contact.userId` | string | Linked user ID (if authenticated) |
|
|
200
|
+
| `contact.email` | string | Primary email address |
|
|
201
|
+
| `contact.phone` | string | Primary phone number |
|
|
202
|
+
| `contact.emails` | array | Array of all email addresses |
|
|
203
|
+
| `contact.phones` | array | Array of all phone numbers |
|
|
151
204
|
| `contact.firstName` | string | First name |
|
|
152
205
|
| `contact.lastName` | string | Last name |
|
|
153
|
-
| `contact.
|
|
206
|
+
| `contact.displayName` | string | Display name |
|
|
207
|
+
| `contact.company` | string | Company name |
|
|
208
|
+
| `contact.avatarUrl` | string | Profile picture URL |
|
|
154
209
|
| `contact.locale` | string | Preferred language/locale (e.g., "en", "de") |
|
|
155
210
|
| `contact.timezone` | string | Preferred timezone |
|
|
156
|
-
| `contact.avatarUrl` | string | Profile picture URL |
|
|
157
|
-
| `contact.metadata` | object | Custom key-value metadata |
|
|
158
211
|
| `contact.tags` | array | Array of tag strings for segmentation |
|
|
212
|
+
| `contact.source` | string | How the contact was created |
|
|
213
|
+
| `contact.notes` | string | Admin notes |
|
|
214
|
+
| `contact.externalIds` | object | External system IDs |
|
|
215
|
+
| `contact.customFields` | object | Custom key-value data |
|
|
159
216
|
| `contact.createdAt` | datetime | When the contact was created |
|
|
160
217
|
| `contact.updatedAt` | datetime | When the contact was last updated |
|
|
161
|
-
| `contact.lastSeenAt` | datetime | Last activity timestamp |
|
|
162
218
|
|
|
163
219
|
#### Example Usage
|
|
164
220
|
|
|
165
221
|
```liquid
|
|
166
|
-
Hi {{ contact.firstName | default: contact.
|
|
222
|
+
Hi {{ contact.firstName | default: contact.displayName | default: "there" }},
|
|
167
223
|
|
|
168
224
|
{% if contact.locale == "de" %}
|
|
169
225
|
Willkommen!
|
|
@@ -176,6 +232,14 @@ Welcome!
|
|
|
176
232
|
{% if contact.phone %}
|
|
177
233
|
We'll send updates to {{ contact.phone }}.
|
|
178
234
|
{% endif %}
|
|
235
|
+
|
|
236
|
+
{% if contact.company %}
|
|
237
|
+
Company: {{ contact.company }}
|
|
238
|
+
{% endif %}
|
|
239
|
+
|
|
240
|
+
{% if contact.customFields.vip %}
|
|
241
|
+
🌟 VIP Customer
|
|
242
|
+
{% endif %}
|
|
179
243
|
```
|
|
180
244
|
|
|
181
245
|
---
|
|
@@ -186,20 +250,18 @@ A **User** represents an authenticated account in the system. This is typically
|
|
|
186
250
|
|
|
187
251
|
| Field | Type | Description |
|
|
188
252
|
|-------|------|-------------|
|
|
189
|
-
| `user.
|
|
253
|
+
| `user.uid` | string | Unique identifier |
|
|
190
254
|
| `user.email` | string | Email address |
|
|
191
|
-
| `user.
|
|
192
|
-
| `user.
|
|
193
|
-
| `user.avatarUrl` | string | Profile picture URL |
|
|
194
|
-
| `user.createdAt` | datetime | Account creation date |
|
|
255
|
+
| `user.displayName` | string | Display name |
|
|
256
|
+
| `user.accountData` | object | Account-specific data and settings |
|
|
195
257
|
|
|
196
258
|
#### Example Usage
|
|
197
259
|
|
|
198
260
|
```liquid
|
|
199
|
-
Logged in as: {{ user.
|
|
261
|
+
Logged in as: {{ user.displayName }} ({{ user.email }})
|
|
200
262
|
|
|
201
|
-
{% if user.
|
|
202
|
-
|
|
263
|
+
{% if user.accountData.preferences.notifications %}
|
|
264
|
+
Notifications are enabled.
|
|
203
265
|
{% endif %}
|
|
204
266
|
```
|
|
205
267
|
|
|
@@ -212,26 +274,33 @@ An **Attestation** is flexible data attached to a specific proof. It's used to s
|
|
|
212
274
|
| Field | Type | Description |
|
|
213
275
|
|-------|------|-------------|
|
|
214
276
|
| `attestation.id` | string | Unique identifier |
|
|
215
|
-
| `attestation.
|
|
216
|
-
| `attestation.
|
|
217
|
-
| `attestation.
|
|
277
|
+
| `attestation.public` | object | Public attestation data (varies by type) |
|
|
278
|
+
| `attestation.private` | object | Private attestation data (varies by type) |
|
|
279
|
+
| `attestation.proof` | object | Associated proof reference/data |
|
|
218
280
|
| `attestation.createdAt` | datetime | When the attestation was created |
|
|
219
281
|
| `attestation.updatedAt` | datetime | When the attestation was last updated |
|
|
220
282
|
|
|
283
|
+
**Note**: The `public` and `private` objects contain custom fields based on your use case.
|
|
284
|
+
|
|
221
285
|
#### Example Usage
|
|
222
286
|
|
|
223
287
|
```liquid
|
|
224
|
-
{% if attestation.type == "warranty_registration" %}
|
|
288
|
+
{% if attestation.public.type == "warranty_registration" %}
|
|
225
289
|
Warranty Registration Details:
|
|
226
290
|
- Registered: {{ attestation.createdAt | date: "%B %d, %Y" }}
|
|
227
|
-
- Purchase Date: {{ attestation.
|
|
228
|
-
- Store: {{ attestation.
|
|
291
|
+
- Purchase Date: {{ attestation.public.purchaseDate }}
|
|
292
|
+
- Store: {{ attestation.public.storeName }}
|
|
293
|
+
{% endif %}
|
|
294
|
+
|
|
295
|
+
{% if attestation.public.type == "tasting_note" %}
|
|
296
|
+
🍷 Tasting Note:
|
|
297
|
+
"{{ attestation.public.notes }}"
|
|
298
|
+
Rating: {{ attestation.public.rating }}/5
|
|
229
299
|
{% endif %}
|
|
230
300
|
|
|
231
|
-
{% if attestation.
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
Rating: {{ attestation.data.rating }}/5
|
|
301
|
+
{% if attestation.private.internalNotes %}
|
|
302
|
+
<!-- Private data only visible to admins -->
|
|
303
|
+
Notes: {{ attestation.private.internalNotes }}
|
|
235
304
|
{% endif %}
|
|
236
305
|
```
|
|
237
306
|
|
|
@@ -357,37 +426,37 @@ Loop variables:
|
|
|
357
426
|
```liquid
|
|
358
427
|
Subject: Your {{ product.name }} has been registered!
|
|
359
428
|
|
|
360
|
-
Hi {{ contact.firstName | default: "there" }},
|
|
429
|
+
Hi {{ contact.firstName | default: contact.displayName | default: "there" }},
|
|
361
430
|
|
|
362
|
-
Great news! Your {{ product.name }} (Serial: {{ proof.serialNumber }})
|
|
431
|
+
Great news! Your {{ product.name }}{% if proof.values.serialNumber %} (Serial: {{ proof.values.serialNumber }}){% endif %}
|
|
363
432
|
has been successfully registered to your account.
|
|
364
433
|
|
|
365
|
-
{% if product.
|
|
366
|
-
Your warranty is valid for {{ product.
|
|
434
|
+
{% if product.data.warranty_years %}
|
|
435
|
+
Your warranty is valid for {{ product.data.warranty_years }} years
|
|
367
436
|
from the date of purchase.
|
|
368
437
|
{% endif %}
|
|
369
438
|
|
|
370
|
-
If you have any questions, please contact {{ collection.
|
|
439
|
+
If you have any questions, please contact {{ collection.title }} support.
|
|
371
440
|
|
|
372
441
|
Best regards,
|
|
373
|
-
The {{ collection.
|
|
442
|
+
The {{ collection.title }} Team
|
|
374
443
|
```
|
|
375
444
|
|
|
376
445
|
### Notification Messages
|
|
377
446
|
|
|
378
447
|
```liquid
|
|
379
448
|
🎉 {{ contact.firstName }}, your {{ product.name }} is now verified!
|
|
380
|
-
Proof ID: {{ proof.shortCode }}
|
|
449
|
+
{% if proof.values.shortCode %}Proof ID: {{ proof.values.shortCode }}{% endif %}
|
|
381
450
|
```
|
|
382
451
|
|
|
383
452
|
### Dynamic Content Blocks
|
|
384
453
|
|
|
385
454
|
```liquid
|
|
386
|
-
{% if proof.
|
|
455
|
+
{% if proof.values.tier == "gold" %}
|
|
387
456
|
<div class="gold-benefits">
|
|
388
457
|
As a Gold member, you get exclusive access to...
|
|
389
458
|
</div>
|
|
390
|
-
{% elsif proof.
|
|
459
|
+
{% elsif proof.values.tier == "silver" %}
|
|
391
460
|
<div class="silver-benefits">
|
|
392
461
|
Your Silver membership includes...
|
|
393
462
|
</div>
|
|
@@ -413,18 +482,19 @@ Proof ID: {{ proof.shortCode }}
|
|
|
413
482
|
|
|
414
483
|
## Accessing Nested Data
|
|
415
484
|
|
|
416
|
-
Use dot notation to access nested fields in
|
|
485
|
+
Use dot notation to access nested fields in data objects:
|
|
417
486
|
|
|
418
487
|
```liquid
|
|
419
|
-
{{ product.
|
|
420
|
-
{{ attestation.
|
|
421
|
-
{{
|
|
488
|
+
{{ product.data.manufacturer }}
|
|
489
|
+
{{ attestation.public.warranty.expiryDate }}
|
|
490
|
+
{{ contact.customFields.vip_level }}
|
|
491
|
+
{{ proof.values.serialNumber }}
|
|
422
492
|
```
|
|
423
493
|
|
|
424
494
|
For dynamic keys, you may need to use bracket notation (if supported):
|
|
425
495
|
|
|
426
496
|
```liquid
|
|
427
|
-
{{ product.
|
|
497
|
+
{{ product.data["custom-field"] }}
|
|
428
498
|
```
|
|
429
499
|
|
|
430
500
|
---
|
|
@@ -433,29 +503,29 @@ For dynamic keys, you may need to use bracket notation (if supported):
|
|
|
433
503
|
|
|
434
504
|
1. **Always use `default` filter** for optional fields to avoid blank output:
|
|
435
505
|
```liquid
|
|
436
|
-
{{ contact.
|
|
506
|
+
{{ contact.displayName | default: contact.firstName | default: "Valued Customer" }}
|
|
437
507
|
```
|
|
438
508
|
|
|
439
509
|
2. **Escape user-generated content** when outputting as HTML:
|
|
440
510
|
```liquid
|
|
441
|
-
{{ attestation.
|
|
511
|
+
{{ attestation.public.userNotes | escape }}
|
|
442
512
|
```
|
|
443
513
|
|
|
444
514
|
3. **Check for existence** before accessing nested data:
|
|
445
515
|
```liquid
|
|
446
|
-
{% if proof.
|
|
447
|
-
Warranty: {{ proof.
|
|
516
|
+
{% if proof.values.warranty %}
|
|
517
|
+
Warranty: {{ proof.values.warranty.type }}
|
|
448
518
|
{% endif %}
|
|
449
519
|
```
|
|
450
520
|
|
|
451
521
|
4. **Use meaningful fallbacks** for a better user experience:
|
|
452
522
|
```liquid
|
|
453
|
-
Hi {{ contact.firstName | default: contact.
|
|
523
|
+
Hi {{ contact.firstName | default: contact.displayName | default: "there" }},
|
|
454
524
|
```
|
|
455
525
|
|
|
456
526
|
5. **Format dates appropriately** for the user's locale:
|
|
457
527
|
```liquid
|
|
458
|
-
{{ proof.
|
|
528
|
+
{{ proof.createdAt | date: "%d %B %Y" }}
|
|
459
529
|
```
|
|
460
530
|
|
|
461
531
|
---
|
package/dist/http.d.ts
CHANGED
|
@@ -16,6 +16,13 @@ export declare function initializeApi(options: {
|
|
|
16
16
|
extraHeaders?: Record<string, string>;
|
|
17
17
|
iframeAutoResize?: boolean;
|
|
18
18
|
logger?: Logger;
|
|
19
|
+
/**
|
|
20
|
+
* When true, bypasses the idempotency guard and forces a full re-initialization.
|
|
21
|
+
* Use only when you intentionally need to reset all SDK state (e.g. in tests or
|
|
22
|
+
* when switching accounts). In normal application code, prefer letting the guard
|
|
23
|
+
* protect runtime state such as login tokens.
|
|
24
|
+
*/
|
|
25
|
+
force?: boolean;
|
|
19
26
|
}): void;
|
|
20
27
|
/** Enable/disable automatic "ngrok-skip-browser-warning" header. */
|
|
21
28
|
export declare function setNgrokSkipBrowserWarning(flag: boolean): void;
|
|
@@ -30,6 +37,19 @@ export declare function setBearerToken(token: string | undefined): void;
|
|
|
30
37
|
* Returns null if initializeApi() has not been called yet.
|
|
31
38
|
*/
|
|
32
39
|
export declare function getBaseURL(): string | null;
|
|
40
|
+
/**
|
|
41
|
+
* Returns true if initializeApi() has been called at least once.
|
|
42
|
+
* Useful for guards in widgets or shared modules that want to skip
|
|
43
|
+
* initialization when another module has already done it.
|
|
44
|
+
*
|
|
45
|
+
* @example
|
|
46
|
+
* ```ts
|
|
47
|
+
* if (!isInitialized()) {
|
|
48
|
+
* initializeApi({ baseURL: 'https://smartlinks.app/api/v1' })
|
|
49
|
+
* }
|
|
50
|
+
* ```
|
|
51
|
+
*/
|
|
52
|
+
export declare function isInitialized(): boolean;
|
|
33
53
|
/**
|
|
34
54
|
* Upload a FormData payload via proxy with progress events using chunked postMessage.
|
|
35
55
|
* Parent is expected to implement the counterpart protocol.
|
package/dist/http.js
CHANGED
|
@@ -20,6 +20,8 @@ let bearerToken = undefined;
|
|
|
20
20
|
let proxyMode = false;
|
|
21
21
|
let ngrokSkipBrowserWarning = false;
|
|
22
22
|
let extraHeadersGlobal = {};
|
|
23
|
+
/** Whether initializeApi has been successfully called at least once. */
|
|
24
|
+
let initialized = false;
|
|
23
25
|
let logger;
|
|
24
26
|
function logDebug(...args) {
|
|
25
27
|
if (!logger)
|
|
@@ -169,9 +171,32 @@ function normalizeErrorResponse(responseBody, statusCode) {
|
|
|
169
171
|
import { iframe } from './iframe';
|
|
170
172
|
export function initializeApi(options) {
|
|
171
173
|
// Normalize baseURL by removing trailing slashes.
|
|
172
|
-
|
|
174
|
+
const normalizedBaseURL = options.baseURL.replace(/\/+$/g, "");
|
|
175
|
+
// ------------------------------------------------------------------
|
|
176
|
+
// Firebase-style idempotency guard
|
|
177
|
+
// If we have already been initialized with the same baseURL and the
|
|
178
|
+
// caller is not forcing a reset, return immediately. This prevents
|
|
179
|
+
// any module – widget, component, or re-rendered page – from
|
|
180
|
+
// accidentally wiping runtime state such as a bearerToken that was
|
|
181
|
+
// set by auth.login() after the first initialization.
|
|
182
|
+
// ------------------------------------------------------------------
|
|
183
|
+
if (initialized && !options.force && baseURL === normalizedBaseURL) {
|
|
184
|
+
logDebug('[smartlinks] initializeApi: already initialized with this baseURL – skipping.', { baseURL });
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
baseURL = normalizedBaseURL;
|
|
173
188
|
apiKey = options.apiKey;
|
|
174
|
-
bearerToken
|
|
189
|
+
// Only overwrite bearerToken when the caller explicitly supplies one,
|
|
190
|
+
// OR when this is the very first initialization (start with a clean slate).
|
|
191
|
+
// Re-initialization calls that omit bearerToken must NOT clear a token that
|
|
192
|
+
// was acquired at runtime (e.g. from a successful auth.login()).
|
|
193
|
+
if (options.bearerToken !== undefined) {
|
|
194
|
+
bearerToken = options.bearerToken;
|
|
195
|
+
}
|
|
196
|
+
else if (!initialized) {
|
|
197
|
+
bearerToken = undefined;
|
|
198
|
+
}
|
|
199
|
+
// else: preserve the existing runtime bearerToken.
|
|
175
200
|
proxyMode = !!options.proxyMode;
|
|
176
201
|
// Auto-enable ngrok skip header if domain contains .ngrok.io and user did not explicitly set the flag.
|
|
177
202
|
// Infer ngrok usage from common domains (.ngrok.io or .ngrok-free.dev)
|
|
@@ -185,6 +210,7 @@ export function initializeApi(options) {
|
|
|
185
210
|
iframe.enableAutoIframeResize();
|
|
186
211
|
}
|
|
187
212
|
logger = options.logger;
|
|
213
|
+
initialized = true;
|
|
188
214
|
logDebug('[smartlinks] initializeApi', {
|
|
189
215
|
baseURL,
|
|
190
216
|
proxyMode,
|
|
@@ -215,6 +241,21 @@ export function setBearerToken(token) {
|
|
|
215
241
|
export function getBaseURL() {
|
|
216
242
|
return baseURL;
|
|
217
243
|
}
|
|
244
|
+
/**
|
|
245
|
+
* Returns true if initializeApi() has been called at least once.
|
|
246
|
+
* Useful for guards in widgets or shared modules that want to skip
|
|
247
|
+
* initialization when another module has already done it.
|
|
248
|
+
*
|
|
249
|
+
* @example
|
|
250
|
+
* ```ts
|
|
251
|
+
* if (!isInitialized()) {
|
|
252
|
+
* initializeApi({ baseURL: 'https://smartlinks.app/api/v1' })
|
|
253
|
+
* }
|
|
254
|
+
* ```
|
|
255
|
+
*/
|
|
256
|
+
export function isInitialized() {
|
|
257
|
+
return initialized;
|
|
258
|
+
}
|
|
218
259
|
// Map of pending proxy requests: id -> {resolve, reject}
|
|
219
260
|
const proxyPending = {};
|
|
220
261
|
function generateProxyId() {
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// src/index.ts
|
|
2
2
|
// Top-level entrypoint of the npm package. Re-export initializeApi + all namespaces.
|
|
3
|
-
export { initializeApi, request, sendCustomProxyMessage } from "./http";
|
|
3
|
+
export { initializeApi, isInitialized, request, sendCustomProxyMessage } from "./http";
|
|
4
4
|
export * from "./api";
|
|
5
5
|
export * from "./types";
|
|
6
6
|
// Iframe namespace
|
|
@@ -62,6 +62,10 @@ export interface Collection {
|
|
|
62
62
|
shortId: string;
|
|
63
63
|
/** if dark mode is enabled for this collection */
|
|
64
64
|
dark?: boolean;
|
|
65
|
+
/** Primary theme color */
|
|
66
|
+
primaryColor?: string;
|
|
67
|
+
/** Secondary theme color */
|
|
68
|
+
secondaryColor?: string;
|
|
65
69
|
portalUrl?: string;
|
|
66
70
|
/** Allow users to claim products without providing a proof ID (auto-generates serial on-demand) */
|
|
67
71
|
allowAutoGenerateClaims?: boolean;
|
package/docs/API_SUMMARY.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Smartlinks API Summary
|
|
2
2
|
|
|
3
|
-
Version: 1.3.
|
|
3
|
+
Version: 1.3.46 | Generated: 2026-02-19T21:09:09.402Z
|
|
4
4
|
|
|
5
5
|
This is a concise summary of all available API functions and types.
|
|
6
6
|
|
|
@@ -87,7 +87,10 @@ Return whether proxy mode is currently enabled.
|
|
|
87
87
|
extraHeaders?: Record<string, string>
|
|
88
88
|
iframeAutoResize?: boolean // default true when in iframe
|
|
89
89
|
logger?: Logger // optional console-like or function to enable verbose logging
|
|
90
|
-
|
|
90
|
+
/**
|
|
91
|
+
* When true, bypasses the idempotency guard and forces a full re-initialization.
|
|
92
|
+
* Use only when you intentionally need to reset all SDK state (e.g. in tests or
|
|
93
|
+
* when switching accounts) → `void`
|
|
91
94
|
Call this once (e.g. at app startup) to configure baseURL/auth.
|
|
92
95
|
|
|
93
96
|
**setNgrokSkipBrowserWarning**(flag: boolean) → `void`
|
|
@@ -102,6 +105,9 @@ Allows setting the bearerToken at runtime (e.g. after login/logout).
|
|
|
102
105
|
**getBaseURL**() → `string | null`
|
|
103
106
|
Get the currently configured API base URL. Returns null if initializeApi() has not been called yet.
|
|
104
107
|
|
|
108
|
+
**isInitialized**() → `boolean`
|
|
109
|
+
Returns true if initializeApi() has been called at least once. Useful for guards in widgets or shared modules that want to skip initialization when another module has already done it. ```ts if (!isInitialized()) { initializeApi({ baseURL: 'https://smartlinks.app/api/v1' }) } ```
|
|
110
|
+
|
|
105
111
|
**proxyUploadFormData**(path: string,
|
|
106
112
|
formData: FormData,
|
|
107
113
|
onProgress?: (percent: number) → `void`
|
|
@@ -1504,6 +1510,8 @@ interface Collection {
|
|
|
1504
1510
|
redirectUrl?: string // Whether the collection has a custom domain
|
|
1505
1511
|
shortId: string, // The shortId of this collection
|
|
1506
1512
|
dark?: boolean // if dark mode is enabled for this collection
|
|
1513
|
+
primaryColor?: string
|
|
1514
|
+
secondaryColor?: string
|
|
1507
1515
|
portalUrl?: string // URL for the collection's portal (if applicable)
|
|
1508
1516
|
allowAutoGenerateClaims?: boolean
|
|
1509
1517
|
defaultAuthKitId: string // default auth kit for this collection, used for auth
|
package/docs/liquid-templates.md
CHANGED
|
@@ -41,23 +41,43 @@ A **Collection** represents a top-level business, brand, or organization. All pr
|
|
|
41
41
|
| `collection.id` | string | Unique identifier |
|
|
42
42
|
| `collection.title` | string | Display title of the collection |
|
|
43
43
|
| `collection.description` | string | Description text |
|
|
44
|
-
| `collection.
|
|
45
|
-
| `collection.
|
|
46
|
-
| `collection.
|
|
47
|
-
| `collection.
|
|
48
|
-
| `collection.
|
|
49
|
-
| `collection.
|
|
44
|
+
| `collection.shortId` | string | Short identifier for the collection |
|
|
45
|
+
| `collection.logoImage.url` | string | URL to the collection's logo image |
|
|
46
|
+
| `collection.logoImage.thumbnails.x100` | string | 100px thumbnail |
|
|
47
|
+
| `collection.logoImage.thumbnails.x200` | string | 200px thumbnail |
|
|
48
|
+
| `collection.logoImage.thumbnails.x512` | string | 512px thumbnail |
|
|
49
|
+
| `collection.headerImage.url` | string | URL to collection header/hero image |
|
|
50
|
+
| `collection.headerImage.thumbnails.*` | string | Header image thumbnails (x100, x200, x512) |
|
|
51
|
+
| `collection.loaderImage.url` | string | URL to collection loader image |
|
|
52
|
+
| `collection.primaryColor` | string | Primary theme color (hex code) |
|
|
53
|
+
| `collection.secondaryColor` | string | Secondary theme color (hex code) |
|
|
54
|
+
| `collection.dark` | boolean | Whether dark mode is enabled |
|
|
55
|
+
| `collection.portalUrl` | string | URL for the collection's portal |
|
|
56
|
+
| `collection.redirectUrl` | string | Custom domain redirect URL |
|
|
57
|
+
| `collection.roles` | object | User roles mapping (userId → role) |
|
|
58
|
+
| `collection.groupTags` | array | Array of group tag names |
|
|
59
|
+
| `collection.languages` | array | Array of supported language objects |
|
|
60
|
+
| `collection.defaultAuthKitId` | string | Default auth kit ID |
|
|
61
|
+
| `collection.allowAutoGenerateClaims` | boolean | Allow claiming without proof ID |
|
|
50
62
|
|
|
51
63
|
#### Example Usage
|
|
52
64
|
|
|
53
65
|
```liquid
|
|
54
|
-
Welcome to {{ collection.
|
|
66
|
+
Welcome to {{ collection.title }}!
|
|
55
67
|
|
|
56
|
-
{% if collection.
|
|
57
|
-
Visit
|
|
68
|
+
{% if collection.portalUrl %}
|
|
69
|
+
Visit our portal at {{ collection.portalUrl }}
|
|
58
70
|
{% endif %}
|
|
59
71
|
|
|
60
|
-
|
|
72
|
+
{% if collection.logoImage %}
|
|
73
|
+
<img src="{{ collection.logoImage.url }}" alt="{{ collection.title }} logo" />
|
|
74
|
+
<!-- Or use a thumbnail: -->
|
|
75
|
+
<img src="{{ collection.logoImage.thumbnails.x200 }}" alt="{{ collection.title }} logo" />
|
|
76
|
+
{% endif %}
|
|
77
|
+
|
|
78
|
+
{% if collection.dark %}
|
|
79
|
+
<!-- Dark mode is enabled -->
|
|
80
|
+
{% endif %}
|
|
61
81
|
```
|
|
62
82
|
|
|
63
83
|
---
|
|
@@ -70,31 +90,44 @@ A **Product** represents a type or definition of a physical or digital item. Pro
|
|
|
70
90
|
|-------|------|-------------|
|
|
71
91
|
| `product.id` | string | Unique identifier |
|
|
72
92
|
| `product.name` | string | Product name |
|
|
93
|
+
| `product.collectionId` | string | ID of the parent collection |
|
|
73
94
|
| `product.description` | string | Product description |
|
|
74
|
-
| `product.
|
|
75
|
-
| `product.
|
|
76
|
-
| `product.
|
|
77
|
-
| `product.
|
|
78
|
-
| `product.
|
|
79
|
-
| `product.
|
|
80
|
-
| `product.
|
|
81
|
-
| `product.
|
|
82
|
-
| `product.
|
|
95
|
+
| `product.gtin` | string | Global Trade Item Number |
|
|
96
|
+
| `product.type` | string | Product type from standard types |
|
|
97
|
+
| `product.heroImage.url` | string | Primary product image URL |
|
|
98
|
+
| `product.heroImage.thumbnails.x100` | string | 100px thumbnail |
|
|
99
|
+
| `product.heroImage.thumbnails.x200` | string | 200px thumbnail |
|
|
100
|
+
| `product.heroImage.thumbnails.x512` | string | 512px thumbnail |
|
|
101
|
+
| `product.tags` | object | Tag map with boolean values |
|
|
102
|
+
| `product.data` | object | Flexible key-value data map |
|
|
103
|
+
| `product.admin` | object | Admin-only configuration |
|
|
104
|
+
| `product.admin.allowAutoGenerateClaims` | boolean | Allow claiming without proof ID |
|
|
105
|
+
| `product.admin.lastSerialId` | number | Last generated serial ID |
|
|
83
106
|
|
|
84
107
|
#### Example Usage
|
|
85
108
|
|
|
86
109
|
```liquid
|
|
87
|
-
Your {{ product.name }}
|
|
110
|
+
Your {{ product.name }}
|
|
88
111
|
|
|
89
112
|
{{ product.description }}
|
|
90
113
|
|
|
91
|
-
{% if product.
|
|
92
|
-
|
|
114
|
+
{% if product.gtin %}
|
|
115
|
+
GTIN: {{ product.gtin }}
|
|
93
116
|
{% endif %}
|
|
94
117
|
|
|
95
|
-
{%
|
|
96
|
-
<img src="{{
|
|
97
|
-
|
|
118
|
+
{% if product.heroImage %}
|
|
119
|
+
<img src="{{ product.heroImage.url }}" alt="{{ product.name }}" />
|
|
120
|
+
<!-- Or use a thumbnail: -->
|
|
121
|
+
<img src="{{ product.heroImage.thumbnails.x512 }}" alt="{{ product.name }}" />
|
|
122
|
+
{% endif %}
|
|
123
|
+
|
|
124
|
+
{% if product.tags.premium %}
|
|
125
|
+
🌟 Premium Product
|
|
126
|
+
{% endif %}
|
|
127
|
+
|
|
128
|
+
{% if product.data.warranty_years %}
|
|
129
|
+
Warranty: {{ product.data.warranty_years }} years
|
|
130
|
+
{% endif %}
|
|
98
131
|
```
|
|
99
132
|
|
|
100
133
|
---
|
|
@@ -106,34 +139,50 @@ A **Proof** is a specific instance of a product—think of it as a unique digita
|
|
|
106
139
|
| Field | Type | Description |
|
|
107
140
|
|-------|------|-------------|
|
|
108
141
|
| `proof.id` | string | Unique identifier |
|
|
109
|
-
| `proof.
|
|
110
|
-
| `proof.
|
|
111
|
-
| `proof.
|
|
112
|
-
| `proof.
|
|
113
|
-
| `proof.
|
|
114
|
-
| `proof.
|
|
115
|
-
| `proof.
|
|
116
|
-
| `proof.shortCode` | string | Short code for easy lookup |
|
|
117
|
-
| `proof.metadata` | object | Custom key-value metadata |
|
|
142
|
+
| `proof.collectionId` | string | ID of the parent collection |
|
|
143
|
+
| `proof.productId` | string | ID of the associated product |
|
|
144
|
+
| `proof.tokenId` | string | Unique token identifier |
|
|
145
|
+
| `proof.userId` | string | User ID of the owner |
|
|
146
|
+
| `proof.claimable` | boolean | Whether the proof can be claimed |
|
|
147
|
+
| `proof.virtual` | boolean | Whether this is a virtual proof |
|
|
148
|
+
| `proof.values` | object | Arbitrary key-value pairs for proof data |
|
|
118
149
|
| `proof.createdAt` | datetime | When the proof was created |
|
|
119
|
-
|
|
150
|
+
|
|
151
|
+
**Note**: Proof `values` object can contain any custom fields. Common examples:
|
|
152
|
+
- `proof.values.serialNumber` - Serial number
|
|
153
|
+
- `proof.values.claimedAt` - Claim timestamp
|
|
154
|
+
- `proof.values.status` - Current status
|
|
155
|
+
- `proof.values.warrantyExpiry` - Warranty expiration
|
|
120
156
|
|
|
121
157
|
#### Example Usage
|
|
122
158
|
|
|
123
159
|
```liquid
|
|
124
160
|
Proof of Authenticity
|
|
125
161
|
|
|
126
|
-
|
|
127
|
-
|
|
162
|
+
{% if proof.values.serialNumber %}
|
|
163
|
+
Serial Number: {{ proof.values.serialNumber }}
|
|
164
|
+
{% endif %}
|
|
128
165
|
|
|
129
|
-
{% if proof.
|
|
130
|
-
|
|
166
|
+
{% if proof.values.status %}
|
|
167
|
+
Status: {{ proof.values.status }}
|
|
168
|
+
{% endif %}
|
|
169
|
+
|
|
170
|
+
{% if proof.claimable %}
|
|
171
|
+
This item is available to claim.
|
|
131
172
|
{% else %}
|
|
132
|
-
This item has
|
|
173
|
+
This item has been claimed.
|
|
133
174
|
{% endif %}
|
|
134
175
|
|
|
135
|
-
{% if proof.
|
|
136
|
-
|
|
176
|
+
{% if proof.virtual %}
|
|
177
|
+
🌐 Digital Product
|
|
178
|
+
{% endif %}
|
|
179
|
+
|
|
180
|
+
{% if proof.values.claimedAt %}
|
|
181
|
+
Claimed on: {{ proof.values.claimedAt | date: "%B %d, %Y at %H:%M" }}
|
|
182
|
+
{% endif %}
|
|
183
|
+
|
|
184
|
+
{% if proof.values.warrantyExpiry %}
|
|
185
|
+
Warranty expires: {{ proof.values.warrantyExpiry | date: "%B %d, %Y" }}
|
|
137
186
|
{% endif %}
|
|
138
187
|
```
|
|
139
188
|
|
|
@@ -145,25 +194,32 @@ A **Contact** represents a customer or user in the system. Contacts are associat
|
|
|
145
194
|
|
|
146
195
|
| Field | Type | Description |
|
|
147
196
|
|-------|------|-------------|
|
|
148
|
-
| `contact.
|
|
149
|
-
| `contact.
|
|
150
|
-
| `contact.
|
|
197
|
+
| `contact.contactId` | string | Unique identifier |
|
|
198
|
+
| `contact.orgId` | string | Organization/collection ID |
|
|
199
|
+
| `contact.userId` | string | Linked user ID (if authenticated) |
|
|
200
|
+
| `contact.email` | string | Primary email address |
|
|
201
|
+
| `contact.phone` | string | Primary phone number |
|
|
202
|
+
| `contact.emails` | array | Array of all email addresses |
|
|
203
|
+
| `contact.phones` | array | Array of all phone numbers |
|
|
151
204
|
| `contact.firstName` | string | First name |
|
|
152
205
|
| `contact.lastName` | string | Last name |
|
|
153
|
-
| `contact.
|
|
206
|
+
| `contact.displayName` | string | Display name |
|
|
207
|
+
| `contact.company` | string | Company name |
|
|
208
|
+
| `contact.avatarUrl` | string | Profile picture URL |
|
|
154
209
|
| `contact.locale` | string | Preferred language/locale (e.g., "en", "de") |
|
|
155
210
|
| `contact.timezone` | string | Preferred timezone |
|
|
156
|
-
| `contact.avatarUrl` | string | Profile picture URL |
|
|
157
|
-
| `contact.metadata` | object | Custom key-value metadata |
|
|
158
211
|
| `contact.tags` | array | Array of tag strings for segmentation |
|
|
212
|
+
| `contact.source` | string | How the contact was created |
|
|
213
|
+
| `contact.notes` | string | Admin notes |
|
|
214
|
+
| `contact.externalIds` | object | External system IDs |
|
|
215
|
+
| `contact.customFields` | object | Custom key-value data |
|
|
159
216
|
| `contact.createdAt` | datetime | When the contact was created |
|
|
160
217
|
| `contact.updatedAt` | datetime | When the contact was last updated |
|
|
161
|
-
| `contact.lastSeenAt` | datetime | Last activity timestamp |
|
|
162
218
|
|
|
163
219
|
#### Example Usage
|
|
164
220
|
|
|
165
221
|
```liquid
|
|
166
|
-
Hi {{ contact.firstName | default: contact.
|
|
222
|
+
Hi {{ contact.firstName | default: contact.displayName | default: "there" }},
|
|
167
223
|
|
|
168
224
|
{% if contact.locale == "de" %}
|
|
169
225
|
Willkommen!
|
|
@@ -176,6 +232,14 @@ Welcome!
|
|
|
176
232
|
{% if contact.phone %}
|
|
177
233
|
We'll send updates to {{ contact.phone }}.
|
|
178
234
|
{% endif %}
|
|
235
|
+
|
|
236
|
+
{% if contact.company %}
|
|
237
|
+
Company: {{ contact.company }}
|
|
238
|
+
{% endif %}
|
|
239
|
+
|
|
240
|
+
{% if contact.customFields.vip %}
|
|
241
|
+
🌟 VIP Customer
|
|
242
|
+
{% endif %}
|
|
179
243
|
```
|
|
180
244
|
|
|
181
245
|
---
|
|
@@ -186,20 +250,18 @@ A **User** represents an authenticated account in the system. This is typically
|
|
|
186
250
|
|
|
187
251
|
| Field | Type | Description |
|
|
188
252
|
|-------|------|-------------|
|
|
189
|
-
| `user.
|
|
253
|
+
| `user.uid` | string | Unique identifier |
|
|
190
254
|
| `user.email` | string | Email address |
|
|
191
|
-
| `user.
|
|
192
|
-
| `user.
|
|
193
|
-
| `user.avatarUrl` | string | Profile picture URL |
|
|
194
|
-
| `user.createdAt` | datetime | Account creation date |
|
|
255
|
+
| `user.displayName` | string | Display name |
|
|
256
|
+
| `user.accountData` | object | Account-specific data and settings |
|
|
195
257
|
|
|
196
258
|
#### Example Usage
|
|
197
259
|
|
|
198
260
|
```liquid
|
|
199
|
-
Logged in as: {{ user.
|
|
261
|
+
Logged in as: {{ user.displayName }} ({{ user.email }})
|
|
200
262
|
|
|
201
|
-
{% if user.
|
|
202
|
-
|
|
263
|
+
{% if user.accountData.preferences.notifications %}
|
|
264
|
+
Notifications are enabled.
|
|
203
265
|
{% endif %}
|
|
204
266
|
```
|
|
205
267
|
|
|
@@ -212,26 +274,33 @@ An **Attestation** is flexible data attached to a specific proof. It's used to s
|
|
|
212
274
|
| Field | Type | Description |
|
|
213
275
|
|-------|------|-------------|
|
|
214
276
|
| `attestation.id` | string | Unique identifier |
|
|
215
|
-
| `attestation.
|
|
216
|
-
| `attestation.
|
|
217
|
-
| `attestation.
|
|
277
|
+
| `attestation.public` | object | Public attestation data (varies by type) |
|
|
278
|
+
| `attestation.private` | object | Private attestation data (varies by type) |
|
|
279
|
+
| `attestation.proof` | object | Associated proof reference/data |
|
|
218
280
|
| `attestation.createdAt` | datetime | When the attestation was created |
|
|
219
281
|
| `attestation.updatedAt` | datetime | When the attestation was last updated |
|
|
220
282
|
|
|
283
|
+
**Note**: The `public` and `private` objects contain custom fields based on your use case.
|
|
284
|
+
|
|
221
285
|
#### Example Usage
|
|
222
286
|
|
|
223
287
|
```liquid
|
|
224
|
-
{% if attestation.type == "warranty_registration" %}
|
|
288
|
+
{% if attestation.public.type == "warranty_registration" %}
|
|
225
289
|
Warranty Registration Details:
|
|
226
290
|
- Registered: {{ attestation.createdAt | date: "%B %d, %Y" }}
|
|
227
|
-
- Purchase Date: {{ attestation.
|
|
228
|
-
- Store: {{ attestation.
|
|
291
|
+
- Purchase Date: {{ attestation.public.purchaseDate }}
|
|
292
|
+
- Store: {{ attestation.public.storeName }}
|
|
293
|
+
{% endif %}
|
|
294
|
+
|
|
295
|
+
{% if attestation.public.type == "tasting_note" %}
|
|
296
|
+
🍷 Tasting Note:
|
|
297
|
+
"{{ attestation.public.notes }}"
|
|
298
|
+
Rating: {{ attestation.public.rating }}/5
|
|
229
299
|
{% endif %}
|
|
230
300
|
|
|
231
|
-
{% if attestation.
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
Rating: {{ attestation.data.rating }}/5
|
|
301
|
+
{% if attestation.private.internalNotes %}
|
|
302
|
+
<!-- Private data only visible to admins -->
|
|
303
|
+
Notes: {{ attestation.private.internalNotes }}
|
|
235
304
|
{% endif %}
|
|
236
305
|
```
|
|
237
306
|
|
|
@@ -357,37 +426,37 @@ Loop variables:
|
|
|
357
426
|
```liquid
|
|
358
427
|
Subject: Your {{ product.name }} has been registered!
|
|
359
428
|
|
|
360
|
-
Hi {{ contact.firstName | default: "there" }},
|
|
429
|
+
Hi {{ contact.firstName | default: contact.displayName | default: "there" }},
|
|
361
430
|
|
|
362
|
-
Great news! Your {{ product.name }} (Serial: {{ proof.serialNumber }})
|
|
431
|
+
Great news! Your {{ product.name }}{% if proof.values.serialNumber %} (Serial: {{ proof.values.serialNumber }}){% endif %}
|
|
363
432
|
has been successfully registered to your account.
|
|
364
433
|
|
|
365
|
-
{% if product.
|
|
366
|
-
Your warranty is valid for {{ product.
|
|
434
|
+
{% if product.data.warranty_years %}
|
|
435
|
+
Your warranty is valid for {{ product.data.warranty_years }} years
|
|
367
436
|
from the date of purchase.
|
|
368
437
|
{% endif %}
|
|
369
438
|
|
|
370
|
-
If you have any questions, please contact {{ collection.
|
|
439
|
+
If you have any questions, please contact {{ collection.title }} support.
|
|
371
440
|
|
|
372
441
|
Best regards,
|
|
373
|
-
The {{ collection.
|
|
442
|
+
The {{ collection.title }} Team
|
|
374
443
|
```
|
|
375
444
|
|
|
376
445
|
### Notification Messages
|
|
377
446
|
|
|
378
447
|
```liquid
|
|
379
448
|
🎉 {{ contact.firstName }}, your {{ product.name }} is now verified!
|
|
380
|
-
Proof ID: {{ proof.shortCode }}
|
|
449
|
+
{% if proof.values.shortCode %}Proof ID: {{ proof.values.shortCode }}{% endif %}
|
|
381
450
|
```
|
|
382
451
|
|
|
383
452
|
### Dynamic Content Blocks
|
|
384
453
|
|
|
385
454
|
```liquid
|
|
386
|
-
{% if proof.
|
|
455
|
+
{% if proof.values.tier == "gold" %}
|
|
387
456
|
<div class="gold-benefits">
|
|
388
457
|
As a Gold member, you get exclusive access to...
|
|
389
458
|
</div>
|
|
390
|
-
{% elsif proof.
|
|
459
|
+
{% elsif proof.values.tier == "silver" %}
|
|
391
460
|
<div class="silver-benefits">
|
|
392
461
|
Your Silver membership includes...
|
|
393
462
|
</div>
|
|
@@ -413,18 +482,19 @@ Proof ID: {{ proof.shortCode }}
|
|
|
413
482
|
|
|
414
483
|
## Accessing Nested Data
|
|
415
484
|
|
|
416
|
-
Use dot notation to access nested fields in
|
|
485
|
+
Use dot notation to access nested fields in data objects:
|
|
417
486
|
|
|
418
487
|
```liquid
|
|
419
|
-
{{ product.
|
|
420
|
-
{{ attestation.
|
|
421
|
-
{{
|
|
488
|
+
{{ product.data.manufacturer }}
|
|
489
|
+
{{ attestation.public.warranty.expiryDate }}
|
|
490
|
+
{{ contact.customFields.vip_level }}
|
|
491
|
+
{{ proof.values.serialNumber }}
|
|
422
492
|
```
|
|
423
493
|
|
|
424
494
|
For dynamic keys, you may need to use bracket notation (if supported):
|
|
425
495
|
|
|
426
496
|
```liquid
|
|
427
|
-
{{ product.
|
|
497
|
+
{{ product.data["custom-field"] }}
|
|
428
498
|
```
|
|
429
499
|
|
|
430
500
|
---
|
|
@@ -433,29 +503,29 @@ For dynamic keys, you may need to use bracket notation (if supported):
|
|
|
433
503
|
|
|
434
504
|
1. **Always use `default` filter** for optional fields to avoid blank output:
|
|
435
505
|
```liquid
|
|
436
|
-
{{ contact.
|
|
506
|
+
{{ contact.displayName | default: contact.firstName | default: "Valued Customer" }}
|
|
437
507
|
```
|
|
438
508
|
|
|
439
509
|
2. **Escape user-generated content** when outputting as HTML:
|
|
440
510
|
```liquid
|
|
441
|
-
{{ attestation.
|
|
511
|
+
{{ attestation.public.userNotes | escape }}
|
|
442
512
|
```
|
|
443
513
|
|
|
444
514
|
3. **Check for existence** before accessing nested data:
|
|
445
515
|
```liquid
|
|
446
|
-
{% if proof.
|
|
447
|
-
Warranty: {{ proof.
|
|
516
|
+
{% if proof.values.warranty %}
|
|
517
|
+
Warranty: {{ proof.values.warranty.type }}
|
|
448
518
|
{% endif %}
|
|
449
519
|
```
|
|
450
520
|
|
|
451
521
|
4. **Use meaningful fallbacks** for a better user experience:
|
|
452
522
|
```liquid
|
|
453
|
-
Hi {{ contact.firstName | default: contact.
|
|
523
|
+
Hi {{ contact.firstName | default: contact.displayName | default: "there" }},
|
|
454
524
|
```
|
|
455
525
|
|
|
456
526
|
5. **Format dates appropriately** for the user's locale:
|
|
457
527
|
```liquid
|
|
458
|
-
{{ proof.
|
|
528
|
+
{{ proof.createdAt | date: "%d %B %Y" }}
|
|
459
529
|
```
|
|
460
530
|
|
|
461
531
|
---
|