@proveanything/smartlinks 1.3.34 → 1.3.37
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/api/appConfiguration.d.ts +279 -2
- package/dist/api/appConfiguration.js +295 -23
- package/dist/api/index.d.ts +1 -1
- package/dist/api/index.js +1 -1
- package/dist/api/product.d.ts +32 -0
- package/dist/api/product.js +32 -0
- package/dist/api/proof.d.ts +19 -2
- package/dist/api/proof.js +23 -2
- package/dist/docs/API_SUMMARY.md +66 -8
- package/dist/docs/app-data-storage.md +223 -0
- package/dist/docs/proof-claiming-methods.md +869 -0
- package/dist/types/collection.d.ts +2 -0
- package/dist/types/product.d.ts +20 -3
- package/docs/API_SUMMARY.md +66 -8
- package/docs/app-data-storage.md +223 -0
- package/docs/proof-claiming-methods.md +869 -0
- package/package.json +1 -1
|
@@ -0,0 +1,869 @@
|
|
|
1
|
+
# Proof Claiming Methods
|
|
2
|
+
|
|
3
|
+
SmartLinks supports multiple methods for claiming/registering product ownership. Each method serves different use cases and product types.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Overview of Claiming Methods
|
|
8
|
+
|
|
9
|
+
| Method | Use Case | Requires Physical ID? | SDK Function |
|
|
10
|
+
|--------|----------|---------------------|--------------|
|
|
11
|
+
| **Tag-Based (NFC/QR)** | Physical products with tags | ✅ Yes | `proof.claim(collectionId, productId, proofId, data)` |
|
|
12
|
+
| **Serial Number** | Products with printed codes | ✅ Yes | `proof.claim(collectionId, productId, proofId, data)` |
|
|
13
|
+
| **Claimable/Virtual Proof** | Pre-allocated proofs (tickets, licenses) | ✅ Yes* | `proof.claim(collectionId, productId, proofId, data)` |
|
|
14
|
+
| **Auto-Generated** | Generic product registration | ❌ No | `proof.claimProduct(collectionId, productId, data)` |
|
|
15
|
+
|
|
16
|
+
*Proof ID provided via email/link rather than physical product
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## Method 1-3: Claim with Proof ID
|
|
21
|
+
|
|
22
|
+
**Use when:** You have a proof ID from an NFC tag, QR code, serial number, or pre-allocated proof.
|
|
23
|
+
|
|
24
|
+
### API
|
|
25
|
+
|
|
26
|
+
```typescript
|
|
27
|
+
import { proof } from '@proveanything/smartlinks';
|
|
28
|
+
|
|
29
|
+
const claimed = await proof.claim(
|
|
30
|
+
collectionId, // e.g., 'wine-collection'
|
|
31
|
+
productId, // e.g., 'bordeaux-2020'
|
|
32
|
+
proofId, // e.g., 'abc123-XY7Z' or 'a7Bx3'
|
|
33
|
+
{
|
|
34
|
+
// Optional: additional data
|
|
35
|
+
purchaseDate: '2026-02-17',
|
|
36
|
+
notes: 'First bottle from this vintage'
|
|
37
|
+
}
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
console.log('Claimed proof:', claimed.id);
|
|
41
|
+
console.log('Owner:', claimed.userId);
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### Examples by ID Type
|
|
45
|
+
|
|
46
|
+
**NFC Tag ID:**
|
|
47
|
+
```typescript
|
|
48
|
+
// User scans NFC tag, app reads tag ID
|
|
49
|
+
const tagId = "abc123-XY7Z";
|
|
50
|
+
|
|
51
|
+
const claimed = await proof.claim(
|
|
52
|
+
'wine-collection',
|
|
53
|
+
'bordeaux-2020',
|
|
54
|
+
tagId
|
|
55
|
+
);
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
**Serial Number:**
|
|
59
|
+
```typescript
|
|
60
|
+
// User types in serial from product packaging
|
|
61
|
+
const serial = "a7Bx3";
|
|
62
|
+
|
|
63
|
+
const claimed = await proof.claim(
|
|
64
|
+
'electronics',
|
|
65
|
+
'headphones-pro',
|
|
66
|
+
serial,
|
|
67
|
+
{ warranty: true }
|
|
68
|
+
);
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
**Pre-allocated Proof (Ticket/License):**
|
|
72
|
+
```typescript
|
|
73
|
+
// User received proof ID via email
|
|
74
|
+
const ticketId = "TICKET-VIP-001";
|
|
75
|
+
|
|
76
|
+
const claimed = await proof.claim(
|
|
77
|
+
'event-2026',
|
|
78
|
+
'vip-ticket',
|
|
79
|
+
ticketId
|
|
80
|
+
);
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
---
|
|
84
|
+
|
|
85
|
+
## Method 4: Auto-Generated Claims
|
|
86
|
+
|
|
87
|
+
**Use when:** Product doesn't have unique identifiers, or you want simple post-purchase registration.
|
|
88
|
+
|
|
89
|
+
### Requirements
|
|
90
|
+
|
|
91
|
+
The collection or product must have `allowAutoGenerateClaims` enabled. Apps will know if this is available based on product configuration.
|
|
92
|
+
|
|
93
|
+
### API
|
|
94
|
+
|
|
95
|
+
```typescript
|
|
96
|
+
import { proof } from '@proveanything/smartlinks';
|
|
97
|
+
|
|
98
|
+
// No proof ID needed - system generates one automatically
|
|
99
|
+
const claimed = await proof.claimProduct(
|
|
100
|
+
collectionId, // e.g., 'beauty-brand'
|
|
101
|
+
productId, // e.g., 'moisturizer-pro'
|
|
102
|
+
{
|
|
103
|
+
// Optional: user data
|
|
104
|
+
purchaseDate: '2026-02-17',
|
|
105
|
+
store: 'Target',
|
|
106
|
+
notes: 'Love this product!'
|
|
107
|
+
}
|
|
108
|
+
);
|
|
109
|
+
|
|
110
|
+
console.log('Auto-generated ID:', claimed.id); // e.g., "a7Bx3"
|
|
111
|
+
console.log('You now own this product');
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### When to Use
|
|
115
|
+
|
|
116
|
+
✅ **Good for:**
|
|
117
|
+
- Consumer products (cosmetics, household goods)
|
|
118
|
+
- Post-purchase registration
|
|
119
|
+
- Products without unique identifiers
|
|
120
|
+
- Building product collections/wishlists
|
|
121
|
+
|
|
122
|
+
❌ **Avoid for:**
|
|
123
|
+
- High-value items
|
|
124
|
+
- Limited editions
|
|
125
|
+
- Products requiring proof of purchase
|
|
126
|
+
- Authenticity verification
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
## Choosing the Right Method
|
|
131
|
+
|
|
132
|
+
Apps typically know which method to use based on product configuration:
|
|
133
|
+
|
|
134
|
+
```typescript
|
|
135
|
+
const product = await product.get(collectionId, productId);
|
|
136
|
+
|
|
137
|
+
if (product.admin?.allowAutoGenerateClaims) {
|
|
138
|
+
// Show simple registration button
|
|
139
|
+
return <RegisterButton onClick={() =>
|
|
140
|
+
proof.claimProduct(collectionId, productId, userData)
|
|
141
|
+
} />;
|
|
142
|
+
} else {
|
|
143
|
+
// Show input field for proof ID
|
|
144
|
+
return <ClaimForm onSubmit={(proofId) =>
|
|
145
|
+
proof.claim(collectionId, productId, proofId, userData)
|
|
146
|
+
} />;
|
|
147
|
+
}
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
### Decision Tree
|
|
151
|
+
|
|
152
|
+
```
|
|
153
|
+
Do you have a proof ID?
|
|
154
|
+
├─ YES → Use proof.claim(collectionId, productId, proofId, data)
|
|
155
|
+
└─ NO → Use proof.claimProduct(collectionId, productId, data)
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
---
|
|
159
|
+
|
|
160
|
+
## Complete Examples
|
|
161
|
+
|
|
162
|
+
### Beauty Product Registration (Auto-Claim)
|
|
163
|
+
|
|
164
|
+
```typescript
|
|
165
|
+
import { proof } from '@proveanything/smartlinks';
|
|
166
|
+
|
|
167
|
+
async function registerProduct() {
|
|
168
|
+
try {
|
|
169
|
+
const claimed = await proof.claimProduct(
|
|
170
|
+
'beauty-brand',
|
|
171
|
+
'moisturizer-pro',
|
|
172
|
+
{
|
|
173
|
+
purchaseDate: '2026-02-17',
|
|
174
|
+
store: 'Sephora',
|
|
175
|
+
skinType: 'combination'
|
|
176
|
+
}
|
|
177
|
+
);
|
|
178
|
+
|
|
179
|
+
alert(`Product registered! Your ID is: ${claimed.id}`);
|
|
180
|
+
// Navigate to product dashboard
|
|
181
|
+
|
|
182
|
+
} catch (error) {
|
|
183
|
+
if (error.errorCode === 'FEATURE_NOT_ENABLED') {
|
|
184
|
+
alert('This product requires a code to register');
|
|
185
|
+
} else {
|
|
186
|
+
alert('Registration failed. Please try again.');
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
### Wine Bottle Authentication (NFC Tag)
|
|
193
|
+
|
|
194
|
+
```typescript
|
|
195
|
+
import { proof } from '@proveanything/smartlinks';
|
|
196
|
+
|
|
197
|
+
async function claimWineBottle(tagId: string) {
|
|
198
|
+
try {
|
|
199
|
+
const claimed = await proof.claim(
|
|
200
|
+
'wine-collection',
|
|
201
|
+
'bordeaux-2020',
|
|
202
|
+
tagId,
|
|
203
|
+
{
|
|
204
|
+
purchaseDate: '2026-02-17',
|
|
205
|
+
vendor: 'Fine Wine Shop',
|
|
206
|
+
cellarLocation: 'Rack 3, Position 12'
|
|
207
|
+
}
|
|
208
|
+
);
|
|
209
|
+
|
|
210
|
+
console.log('Bottle authenticated and claimed!');
|
|
211
|
+
console.log('Proof ID:', claimed.id);
|
|
212
|
+
|
|
213
|
+
} catch (error) {
|
|
214
|
+
if (error.status === 404) {
|
|
215
|
+
alert('Invalid tag ID');
|
|
216
|
+
} else if (error.status === 409) {
|
|
217
|
+
alert('This bottle is already claimed');
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// User scans NFC tag
|
|
223
|
+
nfcReader.addEventListener('reading', ({ serialNumber }) => {
|
|
224
|
+
claimWineBottle(serialNumber);
|
|
225
|
+
});
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
### Event Ticket Claiming
|
|
229
|
+
|
|
230
|
+
```typescript
|
|
231
|
+
import { proof } from '@proveanything/smartlinks';
|
|
232
|
+
|
|
233
|
+
async function claimTicket(ticketId: string) {
|
|
234
|
+
const claimed = await proof.claim(
|
|
235
|
+
'music-festival-2026',
|
|
236
|
+
'vip-pass',
|
|
237
|
+
ticketId
|
|
238
|
+
);
|
|
239
|
+
|
|
240
|
+
console.log('Ticket claimed!');
|
|
241
|
+
console.log('Seat:', claimed.data.seatNumber);
|
|
242
|
+
console.log('Access level:', claimed.data.tier);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// User clicks email link with ticket ID
|
|
246
|
+
const urlParams = new URLSearchParams(window.location.search);
|
|
247
|
+
const ticketId = urlParams.get('ticket');
|
|
248
|
+
if (ticketId) {
|
|
249
|
+
claimTicket(ticketId);
|
|
250
|
+
}
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
---
|
|
254
|
+
|
|
255
|
+
## Error Handling
|
|
256
|
+
|
|
257
|
+
### Common Errors
|
|
258
|
+
|
|
259
|
+
```typescript
|
|
260
|
+
try {
|
|
261
|
+
const claimed = await proof.claimProduct(collectionId, productId, data);
|
|
262
|
+
} catch (error) {
|
|
263
|
+
switch (error.errorCode) {
|
|
264
|
+
case 'FEATURE_NOT_ENABLED':
|
|
265
|
+
// Auto-claim not enabled for this product
|
|
266
|
+
// Fall back to proof ID input
|
|
267
|
+
break;
|
|
268
|
+
|
|
269
|
+
case 'NOT_AUTHORIZED':
|
|
270
|
+
// User not logged in
|
|
271
|
+
// Redirect to login
|
|
272
|
+
break;
|
|
273
|
+
|
|
274
|
+
case 'RATE_LIMIT_EXCEEDED':
|
|
275
|
+
// Too many claims
|
|
276
|
+
// Show retry message
|
|
277
|
+
break;
|
|
278
|
+
|
|
279
|
+
case 'ALREADY_CLAIMED':
|
|
280
|
+
// Proof ID already claimed by another user
|
|
281
|
+
break;
|
|
282
|
+
|
|
283
|
+
default:
|
|
284
|
+
// Generic error handling
|
|
285
|
+
break;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
---
|
|
291
|
+
|
|
292
|
+
## API Reference
|
|
293
|
+
|
|
294
|
+
### `proof.claim(collectionId, productId, proofId, data?)`
|
|
295
|
+
|
|
296
|
+
Claim a proof using an existing proof ID (NFC tag, serial number, pre-allocated proof).
|
|
297
|
+
|
|
298
|
+
**Parameters:**
|
|
299
|
+
- `collectionId` (string) - Collection ID
|
|
300
|
+
- `productId` (string) - Product ID
|
|
301
|
+
- `proofId` (string) - The proof ID to claim
|
|
302
|
+
- `data` (object, optional) - Additional data to attach to the proof
|
|
303
|
+
|
|
304
|
+
**Returns:** `Promise<ProofResponse>`
|
|
305
|
+
|
|
306
|
+
**Endpoint:** `PUT /public/collection/:collectionId/product/:productId/proof/:proofId/claim`
|
|
307
|
+
|
|
308
|
+
---
|
|
309
|
+
|
|
310
|
+
### `proof.claimProduct(collectionId, productId, data?)`
|
|
311
|
+
|
|
312
|
+
Claim a product without a proof ID. System auto-generates a unique serial number.
|
|
313
|
+
|
|
314
|
+
**Parameters:**
|
|
315
|
+
- `collectionId` (string) - Collection ID
|
|
316
|
+
- `productId` (string) - Product ID
|
|
317
|
+
- `data` (object, optional) - User data to attach to the proof
|
|
318
|
+
|
|
319
|
+
**Returns:** `Promise<ProofResponse>` with auto-generated `id` field
|
|
320
|
+
|
|
321
|
+
**Endpoint:** `PUT /public/collection/:collectionId/product/:productId/proof/claim`
|
|
322
|
+
|
|
323
|
+
**Requirements:** Collection or product must have `allowAutoGenerateClaims: true`
|
|
324
|
+
|
|
325
|
+
---
|
|
326
|
+
|
|
327
|
+
## TypeScript Definitions
|
|
328
|
+
|
|
329
|
+
### Collection
|
|
330
|
+
|
|
331
|
+
```typescript
|
|
332
|
+
interface Collection {
|
|
333
|
+
// ... other fields
|
|
334
|
+
allowAutoGenerateClaims?: boolean // Enable auto-claim for all products
|
|
335
|
+
}
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
### Product
|
|
339
|
+
|
|
340
|
+
```typescript
|
|
341
|
+
interface Product {
|
|
342
|
+
// ... other fields
|
|
343
|
+
admin?: {
|
|
344
|
+
allowAutoGenerateClaims?: boolean // Override collection setting
|
|
345
|
+
lastSerialId?: number // Last generated serial (auto-incremented)
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
---
|
|
351
|
+
|
|
352
|
+
**Last Updated:** February 17, 2026
|
|
353
|
+
|
|
354
|
+
---
|
|
355
|
+
|
|
356
|
+
## Overview of Claiming Methods
|
|
357
|
+
|
|
358
|
+
| Method | Use Case | Requires Physical ID? | Pre-Generation? | SDK Function |
|
|
359
|
+
|--------|----------|---------------------|----------------|--------------|
|
|
360
|
+
| **1. Tag-Based (NFC/QR)** | Physical products with tags | ✅ Yes | ✅ Yes | `proof.claim(proofId)` |
|
|
361
|
+
| **2. Serial Number** | Products with printed codes | ✅ Yes | ✅ Yes | `proof.claim(proofId)` |
|
|
362
|
+
| **3. Claimable Proof** | Pre-allocated virtual proofs | ❌ No* | ✅ Yes | `proof.claim(proofId)` |
|
|
363
|
+
| **4. Virtual Proof** | Digital-only products | ❌ No | ✅ Yes | `proof.claim(proofId)` |
|
|
364
|
+
| **5. Auto-Generated (NEW)** | Generic product registration | ❌ No | ❌ No | `proof.claimProduct()` |
|
|
365
|
+
|
|
366
|
+
*Claimable proofs have a proof ID but user may receive it via email/link rather than physical product
|
|
367
|
+
|
|
368
|
+
---
|
|
369
|
+
|
|
370
|
+
## Method 1: Tag-Based Claims (NFC/QR)
|
|
371
|
+
|
|
372
|
+
**Best for:** Physical products with embedded NFC chips or printed QR codes
|
|
373
|
+
|
|
374
|
+
### How It Works
|
|
375
|
+
|
|
376
|
+
1. Admin pre-generates claim sets with unique tag IDs
|
|
377
|
+
2. Physical tags are attached to products
|
|
378
|
+
3. User scans tag with phone → reads tag ID
|
|
379
|
+
4. User claims proof using tag ID
|
|
380
|
+
|
|
381
|
+
### Proof ID Format
|
|
382
|
+
|
|
383
|
+
```
|
|
384
|
+
{claimSetId}-{code}
|
|
385
|
+
Example: "abc123-XY7Z"
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
### Implementation
|
|
389
|
+
|
|
390
|
+
**Admin: Generate claim set**
|
|
391
|
+
```typescript
|
|
392
|
+
import { claimSet } from '@proveanything/smartlinks';
|
|
393
|
+
|
|
394
|
+
const result = await claimSet.create({
|
|
395
|
+
collectionId: 'wine-collection',
|
|
396
|
+
productId: 'bordeaux-2020',
|
|
397
|
+
count: 1000, // Generate 1000 unique tags
|
|
398
|
+
type: 'nfc' // or 'qr'
|
|
399
|
+
});
|
|
400
|
+
|
|
401
|
+
// Returns: { ids: ["abc123-0001", "abc123-0002", ...] }
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
**User: Claim via tag**
|
|
405
|
+
```typescript
|
|
406
|
+
import { proof } from '@proveanything/smartlinks';
|
|
407
|
+
|
|
408
|
+
// User scans NFC tag, app reads tag ID
|
|
409
|
+
const tagId = "abc123-XY7Z";
|
|
410
|
+
|
|
411
|
+
const claimed = await proof.claim(tagId, {
|
|
412
|
+
collectionId: 'wine-collection',
|
|
413
|
+
productId: 'bordeaux-2020'
|
|
414
|
+
});
|
|
415
|
+
|
|
416
|
+
console.log('Claimed:', claimed.id);
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
### Advantages
|
|
420
|
+
- ✅ Highly secure (physical possession required)
|
|
421
|
+
- ✅ Simple user experience (tap phone)
|
|
422
|
+
- ✅ Works offline (scan → claim later)
|
|
423
|
+
|
|
424
|
+
### Disadvantages
|
|
425
|
+
- ❌ Requires physical tags (cost)
|
|
426
|
+
- ❌ Tags can be lost/damaged
|
|
427
|
+
- ❌ Pre-generation needed
|
|
428
|
+
|
|
429
|
+
---
|
|
430
|
+
|
|
431
|
+
## Method 2: Serial Number Claims
|
|
432
|
+
|
|
433
|
+
**Best for:** Products with printed serial numbers
|
|
434
|
+
|
|
435
|
+
### How It Works
|
|
436
|
+
|
|
437
|
+
1. Admin generates batch of serial numbers
|
|
438
|
+
2. Serial numbers printed on product packaging
|
|
439
|
+
3. User manually enters serial number
|
|
440
|
+
4. System validates and claims proof
|
|
441
|
+
|
|
442
|
+
### Proof ID Format
|
|
443
|
+
|
|
444
|
+
```
|
|
445
|
+
Base62-encoded with HMAC validation
|
|
446
|
+
Example: "a7Bx3", "K9mP2"
|
|
447
|
+
```
|
|
448
|
+
|
|
449
|
+
### Implementation
|
|
450
|
+
|
|
451
|
+
**Admin: Generate serial numbers**
|
|
452
|
+
```typescript
|
|
453
|
+
import { serialNumber } from '@proveanything/smartlinks';
|
|
454
|
+
|
|
455
|
+
const serials = await serialNumber.generate({
|
|
456
|
+
collectionId: 'electronics',
|
|
457
|
+
productId: 'headphones-pro',
|
|
458
|
+
count: 5000,
|
|
459
|
+
startIndex: 1000 // Optional: continue from previous batch
|
|
460
|
+
});
|
|
461
|
+
|
|
462
|
+
// Returns: ["a7Bx3", "a8Cy4", ...]
|
|
463
|
+
// Print these on product packaging
|
|
464
|
+
```
|
|
465
|
+
|
|
466
|
+
**User: Claim via serial**
|
|
467
|
+
```typescript
|
|
468
|
+
import { proof } from '@proveanything/smartlinks';
|
|
469
|
+
|
|
470
|
+
// User types in serial from product
|
|
471
|
+
const serial = "a7Bx3";
|
|
472
|
+
|
|
473
|
+
const claimed = await proof.claim(serial, {
|
|
474
|
+
collectionId: 'electronics',
|
|
475
|
+
productId: 'headphones-pro'
|
|
476
|
+
});
|
|
477
|
+
|
|
478
|
+
console.log('Product registered:', claimed.id);
|
|
479
|
+
```
|
|
480
|
+
|
|
481
|
+
### Advantages
|
|
482
|
+
- ✅ Lower cost (just printing)
|
|
483
|
+
- ✅ Works on any product
|
|
484
|
+
- ✅ Cryptographically secure
|
|
485
|
+
|
|
486
|
+
### Disadvantages
|
|
487
|
+
- ❌ User must manually type code (typos)
|
|
488
|
+
- ❌ Codes can be shared/leaked
|
|
489
|
+
- ❌ Pre-generation needed
|
|
490
|
+
|
|
491
|
+
---
|
|
492
|
+
|
|
493
|
+
## Method 3: Claimable Proof Claims
|
|
494
|
+
|
|
495
|
+
**Best for:** Event tickets, vouchers, promotional items
|
|
496
|
+
|
|
497
|
+
### How It Works
|
|
498
|
+
|
|
499
|
+
1. Admin creates claimable proofs (virtual state)
|
|
500
|
+
2. Proof IDs distributed via email/link
|
|
501
|
+
3. User clicks claim link or enters proof ID
|
|
502
|
+
4. Proof transitions from virtual → claimed
|
|
503
|
+
|
|
504
|
+
### Proof ID Format
|
|
505
|
+
|
|
506
|
+
```
|
|
507
|
+
Custom proof ID set by admin
|
|
508
|
+
Example: "TICKET-2026-001", "VOUCHER-ABC"
|
|
509
|
+
```
|
|
510
|
+
|
|
511
|
+
### Implementation
|
|
512
|
+
|
|
513
|
+
**Admin: Create claimable proofs**
|
|
514
|
+
```typescript
|
|
515
|
+
import { proof } from '@proveanything/smartlinks';
|
|
516
|
+
|
|
517
|
+
// Create single claimable proof
|
|
518
|
+
const claimableProof = await proof.create({
|
|
519
|
+
collectionId: 'event-2026',
|
|
520
|
+
productId: 'vip-ticket',
|
|
521
|
+
id: 'TICKET-VIP-001',
|
|
522
|
+
admin: true,
|
|
523
|
+
data: {
|
|
524
|
+
claimable: true, // Marks as unclaimed
|
|
525
|
+
virtual: true, // Not yet associated with user
|
|
526
|
+
seatNumber: 'A-12',
|
|
527
|
+
tier: 'VIP'
|
|
528
|
+
}
|
|
529
|
+
});
|
|
530
|
+
|
|
531
|
+
// Send claim link to user
|
|
532
|
+
const claimUrl = `https://yourapp.com/claim/TICKET-VIP-001`;
|
|
533
|
+
sendEmail(user.email, `Your ticket: ${claimUrl}`);
|
|
534
|
+
```
|
|
535
|
+
|
|
536
|
+
**User: Claim proof**
|
|
537
|
+
```typescript
|
|
538
|
+
import { proof } from '@proveanything/smartlinks';
|
|
539
|
+
|
|
540
|
+
const claimed = await proof.claim('TICKET-VIP-001', {
|
|
541
|
+
collectionId: 'event-2026',
|
|
542
|
+
productId: 'vip-ticket'
|
|
543
|
+
});
|
|
544
|
+
|
|
545
|
+
// Proof now associated with user
|
|
546
|
+
console.log('Ticket claimed by:', claimed.userId);
|
|
547
|
+
```
|
|
548
|
+
|
|
549
|
+
### Advantages
|
|
550
|
+
- ✅ Flexible distribution (email, SMS, link)
|
|
551
|
+
- ✅ Can include pre-configured data
|
|
552
|
+
- ✅ Easy to track claim status
|
|
553
|
+
- ✅ Can set claim windows (time-limited)
|
|
554
|
+
|
|
555
|
+
### Disadvantages
|
|
556
|
+
- ❌ Requires pre-creation
|
|
557
|
+
- ❌ Link/ID can be shared
|
|
558
|
+
- ❌ More complex admin workflow
|
|
559
|
+
|
|
560
|
+
---
|
|
561
|
+
|
|
562
|
+
## Method 4: Virtual Proof Claims
|
|
563
|
+
|
|
564
|
+
**Best for:** Digital products, licenses, access codes
|
|
565
|
+
|
|
566
|
+
### How It Works
|
|
567
|
+
|
|
568
|
+
Similar to claimable proofs, but typically used for purely digital products without physical counterpart.
|
|
569
|
+
|
|
570
|
+
### Implementation
|
|
571
|
+
|
|
572
|
+
**Admin: Create virtual proof**
|
|
573
|
+
```typescript
|
|
574
|
+
import { proof } from '@proveanything/smartlinks';
|
|
575
|
+
|
|
576
|
+
const virtualProof = await proof.create({
|
|
577
|
+
collectionId: 'software-licenses',
|
|
578
|
+
productId: 'photo-editor-pro',
|
|
579
|
+
id: 'LICENSE-2026-XYZ',
|
|
580
|
+
admin: true,
|
|
581
|
+
data: {
|
|
582
|
+
virtual: true,
|
|
583
|
+
claimable: true,
|
|
584
|
+
licenseKey: 'XXXX-YYYY-ZZZZ',
|
|
585
|
+
expiresAt: '2027-12-31'
|
|
586
|
+
}
|
|
587
|
+
});
|
|
588
|
+
```
|
|
589
|
+
|
|
590
|
+
**User: Claim virtual proof**
|
|
591
|
+
```typescript
|
|
592
|
+
const claimed = await proof.claim('LICENSE-2026-XYZ', {
|
|
593
|
+
collectionId: 'software-licenses',
|
|
594
|
+
productId: 'photo-editor-pro'
|
|
595
|
+
});
|
|
596
|
+
|
|
597
|
+
console.log('License activated:', claimed.data.licenseKey);
|
|
598
|
+
```
|
|
599
|
+
|
|
600
|
+
### Advantages
|
|
601
|
+
- ✅ Perfect for digital goods
|
|
602
|
+
- ✅ No physical logistics
|
|
603
|
+
- ✅ Instant delivery
|
|
604
|
+
|
|
605
|
+
### Disadvantages
|
|
606
|
+
- ❌ Same as claimable proofs
|
|
607
|
+
- ❌ Codes can be easily shared
|
|
608
|
+
|
|
609
|
+
---
|
|
610
|
+
|
|
611
|
+
## Method 5: Auto-Generated Claims (NEW)
|
|
612
|
+
|
|
613
|
+
**Best for:** Generic products without unique identifiers, post-purchase registration
|
|
614
|
+
|
|
615
|
+
### How It Works
|
|
616
|
+
|
|
617
|
+
1. Admin enables auto-claim on collection/product
|
|
618
|
+
2. User initiates claim without proof ID
|
|
619
|
+
3. System generates unique serial on-the-fly
|
|
620
|
+
4. Proof created immediately and claimed
|
|
621
|
+
|
|
622
|
+
### Proof ID Format
|
|
623
|
+
|
|
624
|
+
```
|
|
625
|
+
Auto-generated Base62 serial
|
|
626
|
+
Example: "a7Bx3", "K9mP2"
|
|
627
|
+
```
|
|
628
|
+
|
|
629
|
+
### Implementation
|
|
630
|
+
|
|
631
|
+
**Admin: Enable auto-claim**
|
|
632
|
+
```typescript
|
|
633
|
+
import { collection } from '@proveanything/smartlinks';
|
|
634
|
+
|
|
635
|
+
// Enable at collection level
|
|
636
|
+
await collection.update('my-collection', {
|
|
637
|
+
allowAutoGenerateClaims: true
|
|
638
|
+
});
|
|
639
|
+
|
|
640
|
+
// Or override at product level
|
|
641
|
+
await product.update('my-collection', 'moisturizer-pro', {
|
|
642
|
+
admin: {
|
|
643
|
+
allowAutoGenerateClaims: true
|
|
644
|
+
}
|
|
645
|
+
});
|
|
646
|
+
```
|
|
647
|
+
|
|
648
|
+
**User: Claim without proof ID**
|
|
649
|
+
```typescript
|
|
650
|
+
import { proof } from '@proveanything/smartlinks';
|
|
651
|
+
|
|
652
|
+
// No proof ID needed!
|
|
653
|
+
const claimed = await proof.claimProduct({
|
|
654
|
+
collectionId: 'beauty-brand',
|
|
655
|
+
productId: 'moisturizer-pro',
|
|
656
|
+
data: {
|
|
657
|
+
purchaseDate: '2026-02-17',
|
|
658
|
+
store: 'Target',
|
|
659
|
+
notes: 'Love this product!'
|
|
660
|
+
}
|
|
661
|
+
});
|
|
662
|
+
|
|
663
|
+
console.log('Your product ID:', claimed.id); // Auto-generated: "a7Bx3"
|
|
664
|
+
```
|
|
665
|
+
|
|
666
|
+
### API Endpoint
|
|
667
|
+
|
|
668
|
+
```
|
|
669
|
+
PUT /public/collection/:collectionId/product/:productId/proof/claim
|
|
670
|
+
```
|
|
671
|
+
|
|
672
|
+
### Configuration Priority
|
|
673
|
+
|
|
674
|
+
1. Product `admin.allowAutoGenerateClaims: false` → ❌ Disabled
|
|
675
|
+
2. Product `admin.allowAutoGenerateClaims: true` → ✅ Enabled
|
|
676
|
+
3. Collection `allowAutoGenerateClaims: true` → ✅ Enabled
|
|
677
|
+
4. Default → ❌ Disabled
|
|
678
|
+
|
|
679
|
+
### Advantages
|
|
680
|
+
- ✅ No pre-generation required
|
|
681
|
+
- ✅ Zero cost (no tags/printing)
|
|
682
|
+
- ✅ Simple user experience
|
|
683
|
+
- ✅ Perfect for non-serialized products
|
|
684
|
+
- ✅ Atomic counter ensures uniqueness
|
|
685
|
+
|
|
686
|
+
### Disadvantages
|
|
687
|
+
- ❌ No proof of physical ownership
|
|
688
|
+
- ❌ Potential for spam/abuse (needs rate limiting)
|
|
689
|
+
- ❌ Users can claim products they don't own
|
|
690
|
+
|
|
691
|
+
### Security Considerations
|
|
692
|
+
|
|
693
|
+
**Rate limiting required:**
|
|
694
|
+
```typescript
|
|
695
|
+
// Limit to 10 auto-claims per hour per user
|
|
696
|
+
if (userClaimCount > 10) {
|
|
697
|
+
throw new Error('Too many claims. Try again later.');
|
|
698
|
+
}
|
|
699
|
+
```
|
|
700
|
+
|
|
701
|
+
**Use cases where this is safe:**
|
|
702
|
+
- Low-value consumer products
|
|
703
|
+
- Products where ownership doesn't matter much
|
|
704
|
+
- Post-purchase registration for benefits
|
|
705
|
+
- Products sold through verified channels
|
|
706
|
+
|
|
707
|
+
**Avoid for:**
|
|
708
|
+
- High-value items
|
|
709
|
+
- Limited editions
|
|
710
|
+
- Products requiring proof of purchase
|
|
711
|
+
- Collectibles
|
|
712
|
+
|
|
713
|
+
---
|
|
714
|
+
|
|
715
|
+
## Choosing the Right Method
|
|
716
|
+
|
|
717
|
+
### Decision Tree
|
|
718
|
+
|
|
719
|
+
```
|
|
720
|
+
Does product have a unique physical identifier?
|
|
721
|
+
├─ YES → Do you need high security?
|
|
722
|
+
│ ├─ YES → NFC Tag (Method 1)
|
|
723
|
+
│ └─ NO → Serial Number (Method 2)
|
|
724
|
+
│
|
|
725
|
+
└─ NO → Is it a digital product?
|
|
726
|
+
├─ YES → Virtual Proof (Method 4)
|
|
727
|
+
└─ NO → Do users need proof IDs in advance?
|
|
728
|
+
├─ YES → Claimable Proof (Method 3)
|
|
729
|
+
└─ NO → Auto-Generated (Method 5)
|
|
730
|
+
```
|
|
731
|
+
|
|
732
|
+
### Use Case Examples
|
|
733
|
+
|
|
734
|
+
| Product Type | Recommended Method | Reason |
|
|
735
|
+
|--------------|-------------------|---------|
|
|
736
|
+
| Wine bottles | NFC Tag | High value, authentication important |
|
|
737
|
+
| Electronics | Serial Number | Already have serial numbers |
|
|
738
|
+
| Event tickets | Claimable Proof | Need to distribute in advance |
|
|
739
|
+
| Software licenses | Virtual Proof | Digital product |
|
|
740
|
+
| Cosmetics | Auto-Generated | Low value, post-purchase registration |
|
|
741
|
+
| Limited sneakers | NFC Tag | High value, prevent fraud |
|
|
742
|
+
| Household goods | Auto-Generated | Simple registration, low fraud risk |
|
|
743
|
+
|
|
744
|
+
---
|
|
745
|
+
|
|
746
|
+
## Combining Methods
|
|
747
|
+
|
|
748
|
+
You can use multiple methods for the same product:
|
|
749
|
+
|
|
750
|
+
```typescript
|
|
751
|
+
// Product supports both serial numbers AND auto-claim
|
|
752
|
+
const product = {
|
|
753
|
+
id: 'premium-headphones',
|
|
754
|
+
serialNumbersEnabled: true, // Traditional serial claiming
|
|
755
|
+
admin: {
|
|
756
|
+
allowAutoGenerateClaims: true // Also allow auto-claim
|
|
757
|
+
}
|
|
758
|
+
};
|
|
759
|
+
|
|
760
|
+
// User path 1: Has serial from box
|
|
761
|
+
await proof.claim('a7Bx3', { collectionId, productId });
|
|
762
|
+
|
|
763
|
+
// User path 2: Lost box, just wants to register
|
|
764
|
+
await proof.claimProduct({ collectionId, productId });
|
|
765
|
+
```
|
|
766
|
+
|
|
767
|
+
---
|
|
768
|
+
|
|
769
|
+
## API Summary
|
|
770
|
+
|
|
771
|
+
### All Claim Methods
|
|
772
|
+
|
|
773
|
+
```typescript
|
|
774
|
+
import { proof } from '@proveanything/smartlinks';
|
|
775
|
+
|
|
776
|
+
// Methods 1-4: Claim with proof ID
|
|
777
|
+
await proof.claim(proofId, {
|
|
778
|
+
collectionId: string,
|
|
779
|
+
productId: string,
|
|
780
|
+
data?: any // Optional additional data
|
|
781
|
+
});
|
|
782
|
+
|
|
783
|
+
// Method 5: Claim without proof ID
|
|
784
|
+
await proof.claimProduct({
|
|
785
|
+
collectionId: string,
|
|
786
|
+
productId: string,
|
|
787
|
+
data?: any // Optional additional data
|
|
788
|
+
});
|
|
789
|
+
```
|
|
790
|
+
|
|
791
|
+
### Check Claim Status
|
|
792
|
+
|
|
793
|
+
```typescript
|
|
794
|
+
const proofData = await proof.get(proofId);
|
|
795
|
+
|
|
796
|
+
if (proofData.claimable && proofData.virtual) {
|
|
797
|
+
console.log('Proof is unclaimed');
|
|
798
|
+
} else if (proofData.userId) {
|
|
799
|
+
console.log('Proof claimed by:', proofData.userId);
|
|
800
|
+
}
|
|
801
|
+
```
|
|
802
|
+
|
|
803
|
+
### Admin: Check Product Settings
|
|
804
|
+
|
|
805
|
+
```typescript
|
|
806
|
+
const product = await product.get(collectionId, productId);
|
|
807
|
+
|
|
808
|
+
// Check what claiming methods are available
|
|
809
|
+
const methods = {
|
|
810
|
+
hasSerialNumbers: product.admin?.lastSerialId > 0,
|
|
811
|
+
allowsAutoClaim: product.admin?.allowAutoGenerateClaims === true,
|
|
812
|
+
hasClaimSets: product.claimSets?.length > 0
|
|
813
|
+
};
|
|
814
|
+
|
|
815
|
+
console.log('Available claiming methods:', methods);
|
|
816
|
+
```
|
|
817
|
+
|
|
818
|
+
---
|
|
819
|
+
|
|
820
|
+
## Migration Guide
|
|
821
|
+
|
|
822
|
+
### Adding Auto-Claim to Existing Products
|
|
823
|
+
|
|
824
|
+
```typescript
|
|
825
|
+
// Enable for all cosmetics products
|
|
826
|
+
const products = await product.list('beauty-collection');
|
|
827
|
+
|
|
828
|
+
for (const prod of products) {
|
|
829
|
+
if (prod.category === 'cosmetics') {
|
|
830
|
+
await product.update('beauty-collection', prod.id, {
|
|
831
|
+
admin: {
|
|
832
|
+
...prod.admin,
|
|
833
|
+
allowAutoGenerateClaims: true
|
|
834
|
+
}
|
|
835
|
+
});
|
|
836
|
+
}
|
|
837
|
+
}
|
|
838
|
+
```
|
|
839
|
+
|
|
840
|
+
### Analytics: Track Claim Method
|
|
841
|
+
|
|
842
|
+
```typescript
|
|
843
|
+
// When creating proof, tag with claim method
|
|
844
|
+
const proof = await proof.create({
|
|
845
|
+
// ... other fields
|
|
846
|
+
metadata: {
|
|
847
|
+
claimMethod: 'auto_generated' | 'serial' | 'nfc' | 'qr' | 'claimable'
|
|
848
|
+
}
|
|
849
|
+
});
|
|
850
|
+
|
|
851
|
+
// Query by method
|
|
852
|
+
const autoClaimedProofs = await firestore
|
|
853
|
+
.collection('ledger')
|
|
854
|
+
.where('metadata.claimMethod', '==', 'auto_generated')
|
|
855
|
+
.get();
|
|
856
|
+
```
|
|
857
|
+
|
|
858
|
+
---
|
|
859
|
+
|
|
860
|
+
## Related Documentation
|
|
861
|
+
|
|
862
|
+
- [Claim Sets](./claim-sets.md) - Managing NFC/QR tag batches
|
|
863
|
+
- [Serial Numbers](./serial-numbers.md) - Cryptographic serial generation
|
|
864
|
+
- [Proof Ownership](./proof-ownership.md) - Managing claimed proofs
|
|
865
|
+
- [User App Data](./app-data-storage.md) - Storing user preferences per proof
|
|
866
|
+
|
|
867
|
+
---
|
|
868
|
+
|
|
869
|
+
**Last Updated:** February 17, 2026
|