@proveanything/smartlinks 1.12.0 → 1.13.1

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.
@@ -1,6 +1,6 @@
1
1
  # Smartlinks API Summary
2
2
 
3
- Version: 1.12.0 | Generated: 2026-05-05T10:17:03.386Z
3
+ Version: 1.13.1 | Generated: 2026-05-07T10:59:17.642Z
4
4
 
5
5
  This is a concise summary of all available API functions and types.
6
6
 
@@ -9,26 +9,41 @@ This is a concise summary of all available API functions and types.
9
9
  For detailed guides on specific features:
10
10
 
11
11
  - **[SmartLinks Microapp Overview](overview.md)** - Platform architecture, data model, auth patterns, storage, anti-patterns, and quick-reference for all SDK docs
12
+ - **[Assets](assets.md)** - Asset object, AssetRef, upload, replace, soft-delete, restore, bulk-delete, and public token-based uploads
12
13
  - **[AI & Chat Completions](ai.md)** - Chat completions, RAG (document-grounded Q&A), voice integration, streaming, tool calling, podcast generation
13
14
  - **[Translations](translations.md)** - Runtime translation lookup, browser-side IndexedDB caching, and admin translation management
14
15
  - **[Widgets](widgets.md)** - Embeddable React components for parent applications
15
- - **[Containers](containers.md)** - Building full-app embeddable containers (lazy-loaded)
16
+ - **[Building React Components](building-react-components.md)** - Foundational guide to building React widgets and containers for SmartLinks
17
+ - **[Containers](containers.md)** - Building full-app embeddable containers (lazy-loaded)
18
+ - **[Mobile Admin Container](mobile-admin-container.md)** - Building mobile-optimised operator/admin containers as a separate bundle
19
+ - **[Container Tracking](container-tracking.md)** - Hierarchical physical/logical container groupings with item membership
16
20
  - **[Scanner Containers](scanner-container.md)** - Building scanner microapps for the SmartLinks Scanner Android host (RFID, NFC, QR, key events)
17
21
  - **[Multi-Page App Architecture](mpa.md)** - Vite MPA build pipeline: public/admin entry points, widget/container/executor bundles, content-hashed CDN assets
18
22
  - **[App Configuration Files](app-manifest.md)** - `app.manifest.json` and `app.admin.json` reference — bundles, components, setup questions, import schemas, tunable fields, and metrics
19
23
  - **[Executor Model](executor.md)** - Programmatic JS bundles for AI-driven setup, server-side SEO metadata generation, and LLM content for AI crawlers
20
24
  - **[Realtime](realtime.md)** - Real-time data updates and WebSocket connections
21
25
  - **[iframe Responder](iframe-responder.md)** - iframe integration and cross-origin communication
26
+ - **[iframe Streaming Parent Changes](iframe-streaming-parent-changes.md)** - Parent-side changes required to support AI streaming in iframe proxy mode
22
27
  - **[Utilities](utils.md)** - Helper functions for building portal paths, URLs, and common tasks
28
+ - **[UI Utils](ui-utils.md)** - Reusable, themeable admin UI React component library for microapps
29
+ - **[Caching](caching.md)** - Multi-tier caching strategy (in-memory, SessionStorage, IndexedDB) used by the SDK
30
+ - **[Native Facade](native-facade.md)** - Contract layer for accessing device capabilities (share, NFC, haptics) across host shells
23
31
  - **[i18n](i18n.md)** - Internationalization and localization
24
32
  - **[Liquid Templates](liquid-templates.md)** - Dynamic templating for content generation
25
33
  - **[Theme System](theme.system.md)** - Theme configuration and customization
26
34
  - **[Theme Defaults](theme-defaults.md)** - Default theme values and presets
27
35
  - **[Proof Claiming Methods](proof-claiming-methods.md)** - All methods for claiming/registering product ownership (NFC tags, serial numbers, auto-generated claims)
36
+ - **[Product Facets SDK](PRODUCT_FACETS_SDK.md)** - Admin and public product facet endpoints and TypeScript interfaces
37
+ - **[Attestations](attestations.md)** - Append-only fact log with cryptographic chain integrity, time-series analytics, and public/owner/admin visibility
38
+ - **[Auth Kit](auth-kit.md)** - End-user authentication flows (email/password, magic link, OTP, OAuth) for microapps
28
39
  - **[App Data Storage](app-data-storage.md)** - User-specific and collection-scoped app data storage
40
+ - **[Forms](forms.md)** - Platform-managed form definitions, submissions, and schema-driven React form UI
29
41
  - **[App Objects: Cases, Threads & Records](app-objects.md)** - Generic app-scoped building blocks for support cases, discussions, bookings, registrations, and more
42
+ - **[App Records Pattern](app-records-pattern.md)** - Canonical pattern for storing per-product, per-facet, or rule-targeted app data
30
43
  - **[Communications](comms.md)** - Transactional sends, multi-channel broadcasts, consent management, push registration, and analytics
31
44
  - **[Interactions & Event Tracking](interactions.md)** - Log user events, count outcomes, query history, and define interaction types with permissions
45
+ - **[Analytics](analytics.md)** - Web analytics, link-click tracking, QR/tag scan telemetry, and event reporting
46
+ - **[Analytics Metadata Conventions](analytics-metadata-conventions.md)** - Standard recommended keys and conventions for analytics metadata fields
32
47
  - **[Loyalty: Points, Members & Earning Rules](loyalty.md)** - Loyalty schemes, automatic point earning via interaction rules, member balances, transaction history, and manual adjustments
33
48
  - **[Deep Link Discovery](deep-link-discovery.md)** - Registering and discovering navigable app states for portal menus and AI orchestration
34
49
  - **[AI-Native App Manifests](manifests.md)** - How AI workflows discover, configure, and import apps via structured manifests and prose guides
@@ -2245,28 +2260,71 @@ interface PublicCreateBranch {
2245
2260
 
2246
2261
  ### asset
2247
2262
 
2263
+ **AssetRef** (interface)
2264
+ ```typescript
2265
+ interface AssetRef {
2266
+ id: string
2267
+ url: string
2268
+ thumbnail: string | null
2269
+ }
2270
+ ```
2271
+
2272
+ **AssetVersion** (interface)
2273
+ ```typescript
2274
+ interface AssetVersion {
2275
+ url: string
2276
+ mimeType: string | null
2277
+ fileType: string | null
2278
+ size: number | null
2279
+ hash: string | null
2280
+ thumbnail: string | null
2281
+ replacedAt: string
2282
+ replacedBy: string | null
2283
+ }
2284
+ ```
2285
+
2248
2286
  **Asset** (interface)
2249
2287
  ```typescript
2250
2288
  interface Asset {
2251
2289
  id: string
2290
+ collectionId: string
2291
+ site: string
2292
+ productId: string | null
2293
+ proofId: string | null
2294
+ appId: string | null
2252
2295
  url: string
2296
+ * CDN URL of the WebP thumbnail (max 512px longest edge, no crop).
2297
+ * Always .webp — null until thumbnail generation has run.
2298
+ thumbnail: string | null
2253
2299
  name: string
2254
- mimeType?: string
2255
- size?: number
2256
- createdAt?: string
2257
- metadata?: Record<string, any>
2258
- assetType?: string
2259
- type?: string
2260
- collectionId?: string
2261
- hash?: string
2300
+ cleanName: string | null
2301
+ assetType: 'Image' | 'Video' | 'Audio' | 'Document'
2302
+ fileType: string | null
2303
+ type: string | null
2304
+ mimeType: string | null
2305
+ contentType: string | null
2306
+ size: number | null
2307
+ width: number | null
2308
+ height: number | null
2309
+ hash: string | null
2310
+ labels: string[]
2311
+ metadata: Record<string, any>
2312
+ versions: AssetVersion[]
2313
+ uploadedBy: string | null
2314
+ uploaderContactId: string | null
2315
+ uploadTokenId: string | null
2316
+ uploaderIp: string | null
2317
+ status: 'active' | 'pending_review' | 'deleted'
2318
+ createdAt: string
2319
+ updatedAt: string
2320
+ deletedAt: string | null
2321
+ * @deprecated Use `thumbnail` instead. Legacy multi-size thumbnail map.
2262
2322
  thumbnails?: {
2263
2323
  x100?: string
2264
2324
  x200?: string
2265
2325
  x512?: string
2266
2326
  [key: string]: string | undefined
2267
2327
  }
2268
- site?: string
2269
- cleanName?: string
2270
2328
  }
2271
2329
  ```
2272
2330
 
@@ -2337,6 +2395,117 @@ interface RemoveAssetOptions {
2337
2395
  }
2338
2396
  ```
2339
2397
 
2398
+ **AdminListAssetsOptions** (interface)
2399
+ ```typescript
2400
+ interface AdminListAssetsOptions {
2401
+ collectionId: string
2402
+ productId?: string
2403
+ proofId?: string
2404
+ appId?: string
2405
+ assetType?: 'Image' | 'Video' | 'Audio' | 'Document'
2406
+ labels?: string
2407
+ sort?: 'createdAt' | 'name' | 'size' | 'assetType'
2408
+ order?: 'asc' | 'desc'
2409
+ limit?: number
2410
+ offset?: number
2411
+ }
2412
+ ```
2413
+
2414
+ **AdminListAssetsResponse** (interface)
2415
+ ```typescript
2416
+ interface AdminListAssetsResponse {
2417
+ data: Asset[]
2418
+ total: number
2419
+ limit: number
2420
+ offset: number
2421
+ }
2422
+ ```
2423
+
2424
+ **UpdateAssetOptions** (interface)
2425
+ ```typescript
2426
+ interface UpdateAssetOptions {
2427
+ collectionId: string
2428
+ assetId: string
2429
+ name?: string
2430
+ appId?: string
2431
+ labels?: string[]
2432
+ metadata?: Record<string, any>
2433
+ thumbnail?: string
2434
+ }
2435
+ ```
2436
+
2437
+ **ReplaceAssetFileOptions** (interface)
2438
+ ```typescript
2439
+ interface ReplaceAssetFileOptions {
2440
+ collectionId: string
2441
+ assetId: string
2442
+ file: File
2443
+ onProgress?: (percent: number) => void
2444
+ }
2445
+ ```
2446
+
2447
+ **DeleteAssetOptions** (interface)
2448
+ ```typescript
2449
+ interface DeleteAssetOptions {
2450
+ collectionId: string
2451
+ assetId: string
2452
+ graceDays?: number
2453
+ }
2454
+ ```
2455
+
2456
+ **BulkDeleteAssetsOptions** (interface)
2457
+ ```typescript
2458
+ interface BulkDeleteAssetsOptions {
2459
+ collectionId: string
2460
+ assetIds: string[]
2461
+ graceDays?: number
2462
+ }
2463
+ ```
2464
+
2465
+ **RequestUploadTokenOptions** (interface)
2466
+ ```typescript
2467
+ interface RequestUploadTokenOptions {
2468
+ collectionId: string
2469
+ appId: string
2470
+ contactId?: string
2471
+ productId?: string
2472
+ proofId?: string
2473
+ }
2474
+ ```
2475
+
2476
+ **UploadTokenPolicy** (interface)
2477
+ ```typescript
2478
+ interface UploadTokenPolicy {
2479
+ requireLevel: 'anonymous' | 'contact' | 'owner'
2480
+ allowedMimeTypes: string[]
2481
+ maxFileSizeBytes: number
2482
+ reviewRequired: boolean
2483
+ productId: string | null
2484
+ proofId: string | null
2485
+ }
2486
+ ```
2487
+
2488
+ **UploadTokenResponse** (interface)
2489
+ ```typescript
2490
+ interface UploadTokenResponse {
2491
+ tokenId: string
2492
+ expiresAt: string
2493
+ policy: UploadTokenPolicy
2494
+ }
2495
+ ```
2496
+
2497
+ **PublicTokenUploadOptions** (interface)
2498
+ ```typescript
2499
+ interface PublicTokenUploadOptions {
2500
+ collectionId: string
2501
+ tokenId: string
2502
+ file: File
2503
+ name?: string
2504
+ metadata?: Record<string, any>
2505
+ onProgress?: (percent: number) => void
2506
+ }
2507
+ ```
2508
+
2340
2509
  **AssetResponse** = `Asset`
2341
2510
 
2342
2511
  ### attestation
@@ -3190,22 +3359,8 @@ interface Collection {
3190
3359
  id: string
3191
3360
  title: string
3192
3361
  description: string
3193
- headerImage?: {
3194
- url: string
3195
- thumbnails: {
3196
- x100: string
3197
- x200: string
3198
- x512: string
3199
- }
3200
- }
3201
- logoImage?: {
3202
- url: string
3203
- thumbnails: {
3204
- x100: string
3205
- x200: string
3206
- x512: string
3207
- }
3208
- }
3362
+ headerImage?: AssetRef | null
3363
+ logoImage?: AssetRef | null
3209
3364
  loaderImage?: {
3210
3365
  overwriteName: string
3211
3366
  name: string
@@ -6276,37 +6431,6 @@ interface ProductKey {
6276
6431
  }
6277
6432
  ```
6278
6433
 
6279
- **ProductImageThumbnails** (interface)
6280
- ```typescript
6281
- interface ProductImageThumbnails {
6282
- x100?: string
6283
- x200?: string
6284
- x512?: string
6285
- }
6286
- ```
6287
-
6288
- **ProductImage** (interface)
6289
- ```typescript
6290
- interface ProductImage {
6291
- id?: string
6292
- collectionId?: string
6293
- productId?: string
6294
- site?: string
6295
- name?: string
6296
- cleanName?: string
6297
- assetType?: string
6298
- type?: string
6299
- url?: string
6300
- thumbnails?: ProductImageThumbnails
6301
- contentType?: string
6302
- size?: string | number
6303
- hash?: string
6304
- createdAt?: ISODateString | null
6305
- updatedAt?: ISODateString | null
6306
- deletedAt?: ISODateString | null
6307
- }
6308
- ```
6309
-
6310
6434
  **ProductImageUrlInput** (interface)
6311
6435
  ```typescript
6312
6436
  interface ProductImageUrlInput {
@@ -6378,14 +6502,21 @@ interface ProductWriteInput {
6378
6502
  name: string
6379
6503
  description?: string | null
6380
6504
  gtin?: string | null
6381
- ownGtin?: boolean | null
6505
+ ownGtin?: string | null
6382
6506
  additionalGtins?: AdditionalGtin[]
6383
6507
  sku?: string | null
6508
+ schemaType?: string | null
6384
6509
  label?: string | null
6385
6510
  status?: string | null
6386
6511
  sortOrder?: number | null
6387
- heroImage?: ProductImage | ProductImageUrlInput | string | null
6512
+ * Pass the existing `AssetRef` unchanged to keep the current image,
6513
+ * or a URL string / `{ url }` object to import a new file.
6514
+ heroImage?: AssetRef | ProductImageUrlInput | string | null
6515
+ * Pass existing `AssetRef` entries unchanged; replace entries with a URL string
6516
+ * or `{ url }` object to import new files.
6517
+ additionalImages?: Array<AssetRef | ProductImageUrlInput | string>
6388
6518
  facets?: ProductFacetMap
6519
+ tags?: Record<string, boolean>
6389
6520
  data?: Record<string, JsonValue>
6390
6521
  admin?: Record<string, JsonValue>
6391
6522
  extra?: Record<string, JsonValue>
@@ -7634,6 +7765,33 @@ Get an asset by id within a scope (public)
7634
7765
  **remove**(options: RemoveAssetOptions) → `Promise<void>`
7635
7766
  Remove an asset by id within a scope (admin)
7636
7767
 
7768
+ **listAdmin**(options: AdminListAssetsOptions) → `Promise<AdminListAssetsResponse>`
7769
+ List assets for a collection with full filtering options.
7770
+
7771
+ **getAdmin**(collectionId: string, assetId: string) → `Promise<Asset>`
7772
+ Get a single asset by ID (admin).
7773
+
7774
+ **updateAdmin**(options: UpdateAssetOptions) → `Promise<Asset>`
7775
+ Update asset metadata (admin). Use `replaceFile` to swap the file.
7776
+
7777
+ **replaceFile**(options: ReplaceAssetFileOptions) → `Promise<Asset>`
7778
+ Replace the file of an existing asset. The previous file URL is snapshotted into `versions[]` on the asset.
7779
+
7780
+ **deleteAdmin**(options: DeleteAssetOptions) → `Promise<`
7781
+ Soft-delete an asset. Schedules CDN purge after `graceDays` (default 30). Recoverable via `restoreAdmin` until purge runs.
7782
+
7783
+ **restoreAdmin**(collectionId: string, assetId: string) → `Promise<Asset>`
7784
+ Restore a soft-deleted asset (clears `deletedAt`).
7785
+
7786
+ **bulkDelete**(options: BulkDeleteAssetsOptions) → `Promise<`
7787
+ Soft-delete multiple assets in one request.
7788
+
7789
+ **requestUploadToken**(options: RequestUploadTokenOptions) → `Promise<UploadTokenResponse>`
7790
+ Request a single-use upload token for a public (unauthenticated) upload. The token encodes the upload policy (allowed types, max size, review requirement). ```typescript const { tokenId, policy } = await asset.requestUploadToken({ collectionId: 'my-collection', appId: 'user-gallery', contactId: contact.id, }) const uploaded = await asset.publicUploadWithToken({ collectionId: 'my-collection', tokenId, file: selectedFile, }) ```
7791
+
7792
+ **publicUploadWithToken**(options: PublicTokenUploadOptions) → `Promise<Asset>`
7793
+ Upload a file using a single-use upload token (no admin auth required). Assets are created with `status: 'pending_review'` when the token policy has `reviewRequired: true`.
7794
+
7637
7795
  ### async
7638
7796
 
7639
7797
  **enqueueAsyncJob**(collectionId: string,
package/docs/assets.md ADDED
@@ -0,0 +1,310 @@
1
+ # Assets
2
+
3
+ Reference for asset types, endpoints, and public (token-based) uploads.
4
+
5
+ ---
6
+
7
+ ## Asset object
8
+
9
+ ```typescript
10
+ interface Asset {
11
+ // Identity
12
+ id: string // Postgres UUID — stable permanent identifier
13
+ collectionId: string // owning collection
14
+ site: string // alias for collectionId (compat)
15
+ productId: string | null // set when scoped to a product
16
+ proofId: string | null // set when scoped to a proof (ledger entry)
17
+ appId: string | null // app that owns this asset, e.g. 'homepage'
18
+
19
+ // File
20
+ url: string // CDN URL of the original file
21
+ thumbnail: string | null // CDN URL of WebP thumbnail (max 512px longest edge, no crop)
22
+ // Always .webp — null until thumbnail generation has run
23
+ name: string // original filename
24
+ cleanName: string | null // filename without extension
25
+ assetType: 'Image' | 'Video' | 'Audio' | 'Document'
26
+ fileType: string | null // file extension, e.g. 'jpg'
27
+ type: string | null // alias for fileType (compat)
28
+ mimeType: string | null // e.g. 'image/jpeg'
29
+ contentType: string | null // alias for mimeType (compat)
30
+ size: number | null // bytes
31
+ width: number | null // pixels (images only)
32
+ height: number | null // pixels (images only)
33
+ hash: string | null // SHA-256 of file content
34
+
35
+ // Organisation
36
+ labels: string[] // arbitrary string labels for filtering
37
+ metadata: Record<string, any>
38
+ versions: AssetVersion[] // previous file versions (populated by replace)
39
+
40
+ // Upload provenance
41
+ uploadedBy: string | null // Firebase UID of admin uploader
42
+ uploaderContactId: string | null // contact ID for public/token uploads
43
+ uploadTokenId: string | null // upload token used (public uploads)
44
+ uploaderIp: string | null
45
+
46
+ // Lifecycle
47
+ status: 'active' | 'pending_review' | 'deleted'
48
+ createdAt: string // ISO 8601
49
+ updatedAt: string
50
+ deletedAt: string | null
51
+ }
52
+
53
+ interface AssetVersion {
54
+ url: string
55
+ mimeType: string | null
56
+ fileType: string | null
57
+ size: number | null
58
+ hash: string | null
59
+ thumbnail: string | null
60
+ replacedAt: string
61
+ replacedBy: string | null
62
+ }
63
+ ```
64
+
65
+ ### Thumbnail spec
66
+
67
+ - Format: WebP, quality 82
68
+ - Max 512px on the longest edge — never upscales
69
+ - Fit: `inside` (letterbox, no crop)
70
+ - SVGs are skipped — `thumbnail` stays null
71
+ - URL always ends in `_thumb.webp`
72
+
73
+ ---
74
+
75
+ ## AssetRef — slim embedded shape
76
+
77
+ When an asset is referenced inside a product or collection document (e.g. `heroImage`), only three fields are stored to keep documents lean:
78
+
79
+ ```typescript
80
+ interface AssetRef {
81
+ id: string // Postgres UUID — use to fetch the full Asset if needed
82
+ url: string // CDN URL of the original file
83
+ thumbnail: string | null // WebP thumbnail URL, or null if not yet generated
84
+ }
85
+ ```
86
+
87
+ Render the thumbnail wherever a compact preview is needed; fall back to `url` if null:
88
+
89
+ ```typescript
90
+ function getHeroImageUrl(product: Product): string | null {
91
+ const ref = product.heroImage
92
+ if (!ref) return null
93
+ return ref.thumbnail ?? ref.url
94
+ }
95
+ ```
96
+
97
+ ---
98
+
99
+ ## Admin endpoints
100
+
101
+ All admin endpoints require authentication. Base path: `/api/admin/collection/:collectionId`
102
+
103
+ ### List assets
104
+
105
+ ```
106
+ GET /assets
107
+ ```
108
+
109
+ | Parameter | Type | Description |
110
+ |-------------|--------|-------------|
111
+ | `productId` | string | Filter to a specific product |
112
+ | `proofId` | string | Filter to a specific proof |
113
+ | `appId` | string | Filter by owning app |
114
+ | `assetType` | string | `Image`, `Video`, `Audio`, `Document` |
115
+ | `labels` | string | Comma-separated label filter (any match) |
116
+ | `sort` | string | `createdAt` (default), `name`, `size`, `assetType` |
117
+ | `order` | string | `desc` (default), `asc` |
118
+ | `limit` | number | Max results (default 50, max 200) |
119
+ | `offset` | number | Pagination offset |
120
+
121
+ **Response:**
122
+
123
+ ```typescript
124
+ { data: Asset[], total: number, limit: number, offset: number }
125
+ ```
126
+
127
+ **SDK:**
128
+
129
+ ```typescript
130
+ const { data, total } = await Api.asset.listAdmin({
131
+ collectionId: 'my-collection',
132
+ assetType: 'Image',
133
+ labels: 'hero,banner',
134
+ limit: 20,
135
+ })
136
+ ```
137
+
138
+ ---
139
+
140
+ ### Get asset
141
+
142
+ ```
143
+ GET /assets/:assetId
144
+ ```
145
+
146
+ Returns `Asset` or `404`.
147
+
148
+ **SDK:** `Api.asset.getAdmin(collectionId, assetId)`
149
+
150
+ ---
151
+
152
+ ### Upload asset
153
+
154
+ ```
155
+ POST /assets
156
+ ```
157
+
158
+ Use the existing `Api.asset.upload()` method (file) or `Api.asset.uploadFromUrl()` (URL import).
159
+
160
+ ---
161
+
162
+ ### Update asset metadata
163
+
164
+ ```
165
+ PUT /assets/:assetId
166
+ ```
167
+
168
+ Updates metadata only. Use `/replace` to swap the file.
169
+
170
+ ```typescript
171
+ await Api.asset.updateAdmin({
172
+ collectionId: 'my-collection',
173
+ assetId: 'abc123',
174
+ name: 'New display name',
175
+ labels: ['hero', 'featured'],
176
+ metadata: { altText: 'A product photo' },
177
+ })
178
+ ```
179
+
180
+ ---
181
+
182
+ ### Replace file
183
+
184
+ ```
185
+ POST /assets/:assetId/replace
186
+ ```
187
+
188
+ Replaces the file; the previous URL is snapshotted into `versions[]`.
189
+
190
+ ```typescript
191
+ await Api.asset.replaceFile({
192
+ collectionId: 'my-collection',
193
+ assetId: 'abc123',
194
+ file: newFile,
195
+ onProgress: (pct) => console.log(pct),
196
+ })
197
+ ```
198
+
199
+ ---
200
+
201
+ ### Delete asset (soft)
202
+
203
+ ```
204
+ DELETE /assets/:assetId?graceDays=30
205
+ ```
206
+
207
+ Sets `deletedAt` and schedules CDN purge after `graceDays` (default 30). Recoverable until purge.
208
+
209
+ ```typescript
210
+ await Api.asset.deleteAdmin({ collectionId: 'my-collection', assetId: 'abc123', graceDays: 7 })
211
+ ```
212
+
213
+ ---
214
+
215
+ ### Restore asset
216
+
217
+ ```
218
+ POST /assets/:assetId/restore
219
+ ```
220
+
221
+ Clears `deletedAt`. Asset becomes active again.
222
+
223
+ ```typescript
224
+ await Api.asset.restoreAdmin('my-collection', 'abc123')
225
+ ```
226
+
227
+ ---
228
+
229
+ ### Bulk delete
230
+
231
+ ```
232
+ POST /assets/bulk-delete
233
+ ```
234
+
235
+ ```typescript
236
+ await Api.asset.bulkDelete({
237
+ collectionId: 'my-collection',
238
+ assetIds: ['abc123', 'def456'],
239
+ graceDays: 14,
240
+ })
241
+ // Returns { deleted: 2 }
242
+ ```
243
+
244
+ ---
245
+
246
+ ## Public (token-based) uploads
247
+
248
+ For anonymous or contact-initiated uploads from the portal — no admin auth required.
249
+
250
+ ### 1. Request an upload token
251
+
252
+ ```typescript
253
+ const { tokenId, expiresAt, policy } = await Api.asset.requestUploadToken({
254
+ collectionId: 'my-collection',
255
+ appId: 'user-gallery',
256
+ contactId: contact.id, // required when policy requireLevel = 'contact'
257
+ })
258
+ ```
259
+
260
+ The `policy` describes what the token allows:
261
+
262
+ ```typescript
263
+ interface UploadTokenPolicy {
264
+ requireLevel: 'anonymous' | 'contact' | 'owner'
265
+ allowedMimeTypes: string[]
266
+ maxFileSizeBytes: number
267
+ reviewRequired: boolean // when true, asset is created as 'pending_review'
268
+ productId: string | null
269
+ proofId: string | null
270
+ }
271
+ ```
272
+
273
+ Tokens are **single-use**, TTL 900 s.
274
+
275
+ ### 2. Upload with token
276
+
277
+ ```typescript
278
+ const asset = await Api.asset.publicUploadWithToken({
279
+ collectionId: 'my-collection',
280
+ tokenId,
281
+ file: selectedFile,
282
+ onProgress: (pct) => setProgress(pct),
283
+ })
284
+ ```
285
+
286
+ Assets are created with `status: 'pending_review'` when `reviewRequired: true`. An admin must review and set `status` to `'active'` before the asset appears publicly.
287
+
288
+ ---
289
+
290
+ ## Writing image fields on products and collections
291
+
292
+ `heroImage`, `additionalImages` (products), `logoImage`, and `headerImage` (collections) all use the slim `AssetRef` shape when read.
293
+
294
+ When writing:
295
+
296
+ - **Pass the `AssetRef` back unchanged** to keep the current image (server detects the UUID and skips re-processing).
297
+ - **Pass a URL string or `{ url }`** to import a new file — the server fetches/registers it and returns the resulting `AssetRef`.
298
+
299
+ ```typescript
300
+ // Keep existing image
301
+ await Api.products.update(collectionId, productId, {
302
+ name: 'New name',
303
+ heroImage: product.heroImage, // AssetRef — not re-processed
304
+ })
305
+
306
+ // Import a new image
307
+ await Api.products.update(collectionId, productId, {
308
+ heroImage: 'https://example.com/new-image.jpg',
309
+ })
310
+ ```
@@ -95,9 +95,7 @@ A **Product** represents a type or definition of a physical or digital item. Pro
95
95
  | `product.gtin` | string | Global Trade Item Number |
96
96
  | `product.type` | string | Product type from standard types |
97
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 |
98
+ | `product.heroImage.thumbnail` | string | WebP thumbnail URL (max 512px longest edge), or null if not yet generated |
101
99
  | `product.tags` | object | Tag map with boolean values |
102
100
  | `product.data` | object | Flexible key-value data map |
103
101
  | `product.admin` | object | Admin-only configuration |
package/docs/overview.md CHANGED
@@ -48,6 +48,7 @@ The SmartLinks SDK (`@proveanything/smartlinks`) includes comprehensive document
48
48
  | Topic | File | When to Use |
49
49
  |-------|------|-------------|
50
50
  | **API Reference** | `docs/API_SUMMARY.md` | Complete SDK function reference, types, error handling |
51
+ | **Assets** | `docs/assets.md` | Asset object, AssetRef, upload, replace, soft-delete, restore, bulk-delete, public token-based uploads |
51
52
  | **Building React Components** | `docs/building-react-components.md` | **READ THIS FIRST** — Dual-mode rendering, router rules, useAppContext pattern |
52
53
  | **Multi-Page Architecture** | `docs/mpa.md` | Build pipeline, entry points, multi-page setup, content hashing |
53
54
  | **AI & Chat** | `docs/ai.md` | Chat completions, RAG, streaming, tool calling, voice, podcasts, TTS |