@proveanything/smartlinks 1.7.2 → 1.7.4

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.
@@ -0,0 +1,556 @@
1
+ # Scanner Container SDK
2
+
3
+ > **Version:** 1.0 · **Platform:** SmartLinks R4 · **Last updated:** 2026-03-04
4
+
5
+ This document describes how to build a **Scanner Container** — a SmartLinks microapp that replaces the default scanner UI inside the SmartLinks Scanner host application. Scanner containers receive a unified stream of hardware events (RFID, NFC, QR, key presses) and can implement any domain-specific logic on top of raw scan data.
6
+
7
+ > **See also:** [containers.md](containers.md) covers the other container type — portal-embedded full-app containers that run inside web-based SmartLinks portals. Scanner containers are a distinct interface designed specifically for the Android scanner host; they share the UMD bundle format and the `containers` manifest section, but differ in props, build requirements, and runtime context.
8
+
9
+ ---
10
+
11
+ ## Table of Contents
12
+
13
+ 1. [Overview](#overview)
14
+ 2. [Architecture](#architecture)
15
+ 3. [Manifest Declaration](#manifest-declaration)
16
+ 4. [Container Props](#container-props)
17
+ 5. [Hardware Event Stream](#hardware-event-stream)
18
+ 6. [Event Types Reference](#event-types-reference)
19
+ 7. [Subscribing to Events](#subscribing-to-events)
20
+ 8. [Build & Bundle Requirements](#build--bundle-requirements)
21
+ 9. [Shared Dependencies](#shared-dependencies)
22
+ 10. [Lifecycle](#lifecycle)
23
+ 11. [Example: Minimal Scanner Container](#example-minimal-scanner-container)
24
+ 12. [Best Practices](#best-practices)
25
+
26
+ ---
27
+
28
+ ## Overview
29
+
30
+ The SmartLinks Scanner is a host application that manages hardware readers (RFID, NFC, USB, QR camera) on Android devices. By default it displays scanned tags in a built-in list view with automatic SmartLinks tag resolution.
31
+
32
+ A **Scanner Container** is a microapp that *replaces* this default UI entirely. When a user selects your scanner app, the host:
33
+
34
+ 1. Hides its own tag list and lookup logic
35
+ 2. Loads your UMD container bundle via `<script>` injection
36
+ 3. Renders your exported React component
37
+ 4. Forwards **all** hardware events to your component via a pub/sub subscription
38
+
39
+ Your container has full control over how scans are displayed, resolved, and acted upon. The host continues to manage the hardware readers themselves (start/stop RFID, NFC session handling, etc.).
40
+
41
+ ```
42
+ ┌─────────────────────────────────────────────┐
43
+ │ Scanner Host App │
44
+ │ │
45
+ │ ┌──────────┐ ┌────────────────────────┐ │
46
+ │ │ Hardware │──▶│ Event Dispatcher │ │
47
+ │ │ Bridge │ │ (android-bridge.ts) │ │
48
+ │ └──────────┘ └───────────┬────────────┘ │
49
+ │ │ │
50
+ │ pub/sub │ │
51
+ │ ▼ │
52
+ │ ┌─────────────────────────┐ │
53
+ │ │ Your Scanner │ │
54
+ │ │ Container Component │ │
55
+ │ │ (UMD bundle) │ │
56
+ │ └─────────────────────────┘ │
57
+ └─────────────────────────────────────────────┘
58
+ ```
59
+
60
+ ---
61
+
62
+ ## Architecture
63
+
64
+ ### Host Responsibilities
65
+
66
+ The host application handles:
67
+
68
+ - **Hardware management**: Starting/stopping RFID readers, NFC sessions, QR camera
69
+ - **Key mapping**: Physical trigger buttons (key 293 = scan trigger, key 139 = clear)
70
+ - **Collection context**: User selects a collection before scanning
71
+ - **App discovery**: Fetching widget/container manifests and presenting app selection UI
72
+ - **Bundle loading**: Injecting UMD scripts, resolving exports, managing CSS cleanup
73
+ - **Event forwarding**: Converting raw bridge messages into typed `ScannerEvent` objects
74
+
75
+ ### Container Responsibilities
76
+
77
+ Your container handles:
78
+
79
+ - **Scan processing**: Deciding what to do with each event (resolve tags, build UI, etc.)
80
+ - **Data resolution**: Calling SmartLinks APIs to look up tag/product/proof data
81
+ - **Business logic**: Domain-specific workflows (e.g., cask tracking, inventory, quality control)
82
+ - **UI rendering**: Complete control over the scan interface
83
+
84
+ ### What the Host Does NOT Do When Your App Is Active
85
+
86
+ When a scanner container is selected, the host **bypasses all local processing**:
87
+
88
+ - No local tag list tracking
89
+ - No automatic SmartLinks tag resolution
90
+ - No deduplication or lookup debouncing
91
+ - Raw events are forwarded directly to your subscriber
92
+
93
+ This ensures zero redundant work and gives your container full ownership of the data flow.
94
+
95
+ ---
96
+
97
+ ## Manifest Declaration
98
+
99
+ To be discovered as a scanner container, your app's `app.manifest.json` must declare a container component with `"uiRole": "scanner"`:
100
+
101
+ ```json
102
+ {
103
+ "meta": {
104
+ "name": "My Scanner App",
105
+ "appId": "my-scanner-app",
106
+ "version": "1.0.0"
107
+ },
108
+ "containers": {
109
+ "files": {
110
+ "js": { "umd": "containers.umd.js", "esm": "containers.es.js" },
111
+ "css": null
112
+ },
113
+ "components": [
114
+ {
115
+ "name": "ScannerContainer",
116
+ "description": "Custom scanner interface for cask tracking",
117
+ "uiRole": "scanner",
118
+ "scope": "collection",
119
+ "audience": "admin",
120
+ "settings": {
121
+ "type": "object",
122
+ "properties": {
123
+ "autoResolve": {
124
+ "type": "boolean",
125
+ "title": "Auto-resolve tags",
126
+ "description": "Automatically look up tag metadata on scan",
127
+ "default": true
128
+ }
129
+ }
130
+ }
131
+ }
132
+ ]
133
+ }
134
+ }
135
+ ```
136
+
137
+ ### Key Fields
138
+
139
+ | Field | Required | Description |
140
+ | ------------- | -------- | ----------------------------------------------------------------------- |
141
+ | `name` | ✅ | Export name in the UMD bundle (`window.SmartLinksContainers[name]`) |
142
+ | `uiRole` | ✅ | Must be `"scanner"` for the host to recognize it |
143
+ | `description` | ✅ | Shown in the scanner app picker UI |
144
+ | `scope` | Optional | `"collection"` or `"product"` — the data scope |
145
+ | `audience` | Optional | `"admin"`, `"public"`, or `"both"` |
146
+ | `settings` | Optional | JSON Schema describing configurable props (see Widget Settings Schema) |
147
+
148
+ ---
149
+
150
+ ## Container Props
151
+
152
+ The host renders your container with these props:
153
+
154
+ ```typescript
155
+ interface ScannerContainerProps {
156
+ /** The active collection ID */
157
+ collectionId: string;
158
+
159
+ /** Your app's unique identifier */
160
+ appId: string;
161
+
162
+ /** Whether the current user is an admin */
163
+ isAdmin: boolean;
164
+
165
+ /**
166
+ * Subscribe to all hardware events.
167
+ * Call once in useEffect; returns an unsubscribe function.
168
+ */
169
+ onSubscribeScannerEvents: (
170
+ callback: (event: ScannerEvent) => void
171
+ ) => (() => void);
172
+
173
+ /**
174
+ * @deprecated Use onSubscribeScannerEvents instead.
175
+ * Provided for backward compatibility — identical behavior.
176
+ */
177
+ onSubscribeScanEvents?: (
178
+ callback: (event: ScannerEvent) => void
179
+ ) => (() => void);
180
+ }
181
+ ```
182
+
183
+ > **Note — no `SL` prop:** Unlike portal containers (see [containers.md](containers.md)), scanner containers do not receive an `SL` prop from the host. Instead, the SmartLinks SDK is externalized to `window.SL` and imported directly in your bundle (`import * as SL from '@proveanything/smartlinks'`). See [Shared Dependencies](#shared-dependencies) for the full externals table.
184
+
185
+ ---
186
+
187
+ ## Hardware Event Stream
188
+
189
+ All hardware inputs are normalized into a single **discriminated union** type called `ScannerEvent`. The `type` field tells you the source:
190
+
191
+ ```typescript
192
+ type ScannerEvent =
193
+ | { type: 'rfid'; uid: string; timestamp: number; rssi?: number }
194
+ | { type: 'nfc'; uid: string; timestamp: number; ndef?: string }
195
+ | { type: 'qr'; data: string; timestamp: number }
196
+ | { type: 'key'; keyCode: number; action: 'down' | 'up'; timestamp: number };
197
+ ```
198
+
199
+ ### Why a Unified Stream?
200
+
201
+ Rather than providing four separate subscription channels, a single stream:
202
+
203
+ - Simplifies the container API surface (one `useEffect`, one cleanup)
204
+ - Allows containers to correlate cross-device events (e.g., "trigger key held while RFID scans arrive")
205
+ - Makes it trivial to log or replay all hardware activity
206
+ - Avoids race conditions from multiple independent subscriptions
207
+
208
+ ---
209
+
210
+ ## Event Types Reference
211
+
212
+ ### `rfid` — RFID Tag Scan
213
+
214
+ Emitted when the Chainway UHF RFID reader detects a tag.
215
+
216
+ | Field | Type | Description |
217
+ | ----------- | -------- | ----------------------------------------------- |
218
+ | `type` | `'rfid'` | Discriminant |
219
+ | `uid` | `string` | The EPC (Electronic Product Code) hex string |
220
+ | `timestamp` | `number` | `Date.now()` when the event was created |
221
+ | `rssi` | `number` | Signal strength (optional, reader-dependent) |
222
+
223
+ **Notes:**
224
+ - RFID readers emit continuously while active — expect many events per second
225
+ - The same EPC will appear repeatedly; your container should handle deduplication
226
+ - The host starts/stops the RFID reader via hardware key 293 (trigger button)
227
+
228
+ ### `nfc` — NFC / USB Tag Scan
229
+
230
+ Emitted when the device's built-in NFC reader or an external USB NFC reader scans a tag.
231
+
232
+ | Field | Type | Description |
233
+ | ----------- | -------- | --------------------------------------------------- |
234
+ | `type` | `'nfc'` | Discriminant |
235
+ | `uid` | `string` | The tag's unique ID (hex string, e.g., `04A3B2...`) |
236
+ | `timestamp` | `number` | `Date.now()` when the event was created |
237
+ | `ndef` | `string` | NDEF record content (URL or text), empty if none |
238
+
239
+ **Notes:**
240
+ - NFC scans are one-shot (tap to scan)
241
+ - Both native NFC and USB reader scans are normalized to this type
242
+ - The `ndef` field often contains a SmartLinks URL that can be parsed for context
243
+
244
+ ### `qr` — QR Code Scan
245
+
246
+ Emitted when the native QR code scanner successfully reads a code.
247
+
248
+ | Field | Type | Description |
249
+ | ----------- | ------- | ------------------------------------------ |
250
+ | `type` | `'qr'` | Discriminant |
251
+ | `data` | `string`| The decoded QR code content (URL or text) |
252
+ | `timestamp` | `number`| `Date.now()` when the event was created |
253
+
254
+ **Notes:**
255
+ - Only successful scans are forwarded (cancelled/error scans are filtered)
256
+ - QR scans typically contain SmartLinks URLs or serial numbers
257
+
258
+ ### `key` — Hardware Key Press
259
+
260
+ Emitted when a physical button is pressed or released on the device.
261
+
262
+ | Field | Type | Description |
263
+ | ----------- | ----------------- | ------------------------------------------- |
264
+ | `type` | `'key'` | Discriminant |
265
+ | `keyCode` | `number` | Android key code constant |
266
+ | `action` | `'down' \| 'up'` | Press or release |
267
+ | `timestamp` | `number` | `Date.now()` when the event was created |
268
+
269
+ **Common Key Codes:**
270
+
271
+ | Code | Button | Default Host Behavior |
272
+ | ----- | --------------- | -------------------------------------------- |
273
+ | `293` | Scan trigger | Start RFID on DOWN, stop on UP |
274
+ | `139` | Function/Clear | Clear tag list on DOWN |
275
+
276
+ **Notes:**
277
+ - Key events are forwarded to your container **and** processed by the host simultaneously
278
+ - The host will still start/stop RFID reading on key 293 — your container receives the resulting RFID events
279
+ - You can use key events for custom actions (e.g., confirm selection, switch modes)
280
+
281
+ ---
282
+
283
+ ## Subscribing to Events
284
+
285
+ Use the `onSubscribeScannerEvents` prop in a `useEffect`:
286
+
287
+ ```tsx
288
+ import { useEffect } from 'react';
289
+
290
+ export function ScannerContainer({ onSubscribeScannerEvents, collectionId, appId }) {
291
+ const [scans, setScans] = useState([]);
292
+
293
+ useEffect(() => {
294
+ const unsubscribe = onSubscribeScannerEvents((event) => {
295
+ switch (event.type) {
296
+ case 'rfid':
297
+ console.log('RFID:', event.uid, 'RSSI:', event.rssi);
298
+ // Deduplicate + resolve against SmartLinks
299
+ break;
300
+ case 'nfc':
301
+ console.log('NFC:', event.uid, 'NDEF:', event.ndef);
302
+ break;
303
+ case 'qr':
304
+ console.log('QR:', event.data);
305
+ break;
306
+ case 'key':
307
+ console.log('Key:', event.keyCode, event.action);
308
+ break;
309
+ }
310
+ });
311
+
312
+ return unsubscribe; // Clean up on unmount
313
+ }, [onSubscribeScannerEvents]);
314
+
315
+ return <div>...</div>;
316
+ }
317
+ ```
318
+
319
+ ### Important Patterns
320
+
321
+ 1. **Subscribe once** — The subscriber function is stable (wrapped in `useCallback` by the host). Subscribe in a `useEffect` with `[onSubscribeScannerEvents]` as the dependency.
322
+
323
+ 2. **Use refs for mutable state** — If your event handler needs access to current state, use refs to avoid stale closures:
324
+
325
+ ```tsx
326
+ const scansRef = useRef(new Map());
327
+
328
+ useEffect(() => {
329
+ const unsubscribe = onSubscribeScannerEvents((event) => {
330
+ if (event.type === 'rfid') {
331
+ scansRef.current.set(event.uid, event);
332
+ // Trigger re-render via setState
333
+ }
334
+ });
335
+ return unsubscribe;
336
+ }, [onSubscribeScannerEvents]);
337
+ ```
338
+
339
+ 3. **Batch state updates** — RFID events can arrive at high frequency. Consider debouncing UI updates while accumulating events.
340
+
341
+ ---
342
+
343
+ ## Build & Bundle Requirements
344
+
345
+ Scanner containers must be built as **UMD bundles** that register exports on `window.SmartLinksContainers`:
346
+
347
+ ```typescript
348
+ // vite.config.container.ts
349
+ export default defineConfig({
350
+ build: {
351
+ lib: {
352
+ entry: 'src/containers/index.ts',
353
+ name: 'SmartLinksContainers', // ← window global name
354
+ formats: ['umd'],
355
+ fileName: 'containers',
356
+ },
357
+ rollupOptions: {
358
+ external: [/* shared dependencies — see below */],
359
+ output: {
360
+ globals: {/* dependency → window global mapping */},
361
+ },
362
+ },
363
+ },
364
+ });
365
+ ```
366
+
367
+ ### Export Structure
368
+
369
+ ```typescript
370
+ // src/containers/index.ts
371
+ export { ScannerContainer } from './ScannerContainer';
372
+ ```
373
+
374
+ The host resolves your component by the `name` field in the manifest:
375
+
376
+ ```javascript
377
+ const Component = window.SmartLinksContainers['ScannerContainer'];
378
+ ```
379
+
380
+ ---
381
+
382
+ ## Shared Dependencies
383
+
384
+ The host exposes these libraries on `window` — your container build **must externalize them** (do not bundle):
385
+
386
+ | Import | Window Global |
387
+ | ----------------------------- | ----------------------- |
388
+ | `react` | `window.React` |
389
+ | `react-dom` | `window.ReactDOM` |
390
+ | `react/jsx-runtime` | `window.jsxRuntime` |
391
+ | `@proveanything/smartlinks` | `window.SL` |
392
+ | `class-variance-authority` | `window.CVA` |
393
+ | `react-router-dom` | `window.ReactRouterDOM` |
394
+ | `@tanstack/react-query` | `window.ReactQuery` |
395
+ | `lucide-react` | `window.LucideReact` |
396
+ | `date-fns` | `window.dateFns` |
397
+ | `@radix-ui/react-*` | `window.Radix*` |
398
+
399
+ See the [SmartLinks Microapp Development Guide](../README.md) for the full shared dependencies table with exact global names.
400
+
401
+ Any dependency **not** in this list must be bundled into your container.
402
+
403
+ ---
404
+
405
+ ## Lifecycle
406
+
407
+ ```
408
+ 1. User selects collection
409
+ 2. Host fetches widget/container manifests via SmartLinks API
410
+ 3. Host identifies containers with uiRole === 'scanner'
411
+ 4. User picks your scanner app from the list
412
+ → Selection is persisted in localStorage per collection
413
+ 5. Host loads your UMD bundle via <script> tag
414
+ 6. Host resolves your component from window.SmartLinksContainers
415
+ 7. Host renders <YourComponent ...props />
416
+ 8. Your component subscribes to onSubscribeScannerEvents
417
+ 9. User presses hardware trigger → RFID/NFC/QR events flow to your callback
418
+ 10. User switches away or deselects → component unmounts, script/CSS removed
419
+ ```
420
+
421
+ ### Cleanup
422
+
423
+ The host handles all cleanup when your container is deselected or the collection changes:
424
+
425
+ - Script tag removal
426
+ - Injected CSS/style removal
427
+ - React component unmounting
428
+ - Event listener cleanup (via your returned unsubscribe function)
429
+
430
+ ---
431
+
432
+ ## Example: Minimal Scanner Container
433
+
434
+ ```tsx
435
+ import React, { useState, useEffect, useRef, useCallback } from 'react';
436
+ import * as SL from '@proveanything/smartlinks';
437
+
438
+ // Types — import from your own types file or declare inline
439
+ type ScannerEvent =
440
+ | { type: 'rfid'; uid: string; timestamp: number; rssi?: number }
441
+ | { type: 'nfc'; uid: string; timestamp: number; ndef?: string }
442
+ | { type: 'qr'; data: string; timestamp: number }
443
+ | { type: 'key'; keyCode: number; action: 'down' | 'up'; timestamp: number };
444
+
445
+ interface Props {
446
+ collectionId: string;
447
+ appId: string;
448
+ isAdmin: boolean;
449
+ onSubscribeScannerEvents: (cb: (event: ScannerEvent) => void) => () => void;
450
+ }
451
+
452
+ interface ScannedTag {
453
+ uid: string;
454
+ source: 'rfid' | 'nfc' | 'qr';
455
+ firstSeen: number;
456
+ count: number;
457
+ productName?: string;
458
+ }
459
+
460
+ export function ScannerContainer({ collectionId, appId, isAdmin, onSubscribeScannerEvents }: Props) {
461
+ const [tags, setTags] = useState<Map<string, ScannedTag>>(new Map());
462
+ const tagsRef = useRef(tags);
463
+ tagsRef.current = tags;
464
+
465
+ const resolveTag = useCallback(async (cId: string, uid: string) => {
466
+ try {
467
+ const result = await SL.tags.publicGetByCollection(cId, uid, 'product');
468
+ const product = result.embedded?.products?.[result.tag?.productId!];
469
+ setTags(prev => {
470
+ const next = new Map(prev);
471
+ const tag = next.get(uid);
472
+ if (tag) {
473
+ next.set(uid, { ...tag, productName: product?.name ?? 'Unknown' });
474
+ }
475
+ return next;
476
+ });
477
+ } catch (err) {
478
+ console.error('Tag resolution failed:', uid, err);
479
+ }
480
+ }, []);
481
+
482
+ useEffect(() => {
483
+ const unsubscribe = onSubscribeScannerEvents((event) => {
484
+ if (event.type === 'rfid' || event.type === 'nfc') {
485
+ const uid = event.uid;
486
+ setTags(prev => {
487
+ const next = new Map(prev);
488
+ const existing = next.get(uid);
489
+ if (existing) {
490
+ next.set(uid, { ...existing, count: existing.count + 1 });
491
+ } else {
492
+ next.set(uid, {
493
+ uid,
494
+ source: event.type,
495
+ firstSeen: event.timestamp,
496
+ count: 1,
497
+ });
498
+ // Resolve tag in background
499
+ resolveTag(collectionId, uid);
500
+ }
501
+ return next;
502
+ });
503
+ } else if (event.type === 'qr') {
504
+ console.log('QR scanned:', event.data);
505
+ }
506
+ });
507
+
508
+ return unsubscribe;
509
+ }, [onSubscribeScannerEvents, collectionId, resolveTag]);
510
+
511
+ return (
512
+ <div style={{ padding: 16 }}>
513
+ <h2>Scanned Tags ({tags.size})</h2>
514
+ {Array.from(tags.values()).map(tag => (
515
+ <div key={tag.uid} style={{ padding: 8, borderBottom: '1px solid #eee' }}>
516
+ <strong>{tag.uid}</strong> × {tag.count}
517
+ {tag.productName && <span> — {tag.productName}</span>}
518
+ <span style={{ opacity: 0.5, marginLeft: 8 }}>{tag.source.toUpperCase()}</span>
519
+ </div>
520
+ ))}
521
+ {tags.size === 0 && <p style={{ opacity: 0.5 }}>Waiting for scans...</p>}
522
+ </div>
523
+ );
524
+ }
525
+ ```
526
+
527
+ ---
528
+
529
+ ## Best Practices
530
+
531
+ ### Performance
532
+
533
+ - **Debounce UI updates** for RFID — you may receive 50+ events/second during active scanning
534
+ - **Use refs** for state accessed inside the event callback to avoid stale closures
535
+ - **Batch API calls** — use `SL.tags.lookupTags()` for batch resolution rather than one call per tag
536
+
537
+ ### Data Resolution
538
+
539
+ - **Prefer collection-scoped lookups** — since you receive `collectionId` as a prop, always use `SL.tags.publicGetByCollection(collectionId, tagId)` for fast, direct resolution
540
+ - **Use `SL.tags.resolveTag` only as fallback** — for the rare case where a tag might belong to a different collection
541
+
542
+ ### UX
543
+
544
+ - **Show scan feedback immediately** — display the raw UID before resolution completes
545
+ - **Handle key events thoughtfully** — the host already manages RFID start/stop via key 293; use key events for your own UI actions (confirm, navigate, toggle modes)
546
+ - **Support high-density scanning** — RFID use cases often involve scanning 50–100+ tags in a session
547
+
548
+ ### Error Handling
549
+
550
+ - **Gracefully handle missing tags** — not every scanned EPC/UID will resolve to a SmartLinks tag
551
+ - **Network resilience** — tag resolution may fail; show cached/partial data and retry
552
+
553
+ ### Bundle Size
554
+
555
+ - **Externalize shared dependencies** — never bundle React, SmartLinks SDK, or other shared libs
556
+ - **Keep containers focused** — a scanner container should do one thing well
package/openapi.yaml CHANGED
@@ -2138,6 +2138,39 @@ paths:
2138
2138
  description: Unauthorized
2139
2139
  404:
2140
2140
  description: Not found
2141
+ /admin/collection/{collectionId}/comm.send:
2142
+ post:
2143
+ tags:
2144
+ - comms
2145
+ summary: Send a single transactional message to one contact using a template.
2146
+ operationId: comms_sendTransactional
2147
+ security:
2148
+ - bearerAuth: []
2149
+ parameters:
2150
+ - name: collectionId
2151
+ in: path
2152
+ required: true
2153
+ schema:
2154
+ type: string
2155
+ responses:
2156
+ 200:
2157
+ description: Success
2158
+ content:
2159
+ application/json:
2160
+ schema:
2161
+ $ref: "#/components/schemas/TransactionalSendResult"
2162
+ 400:
2163
+ description: Bad request
2164
+ 401:
2165
+ description: Unauthorized
2166
+ 404:
2167
+ description: Not found
2168
+ requestBody:
2169
+ required: true
2170
+ content:
2171
+ application/json:
2172
+ schema:
2173
+ $ref: "#/components/schemas/TransactionalSendRequest"
2141
2174
  /admin/collection/{collectionId}/comm.settings:
2142
2175
  patch:
2143
2176
  tags:
@@ -2421,39 +2454,6 @@ paths:
2421
2454
  application/json:
2422
2455
  schema:
2423
2456
  $ref: "#/components/schemas/CommsRecipientsWithoutActionQuery"
2424
- /admin/collection/{collectionId}/comm/send:
2425
- post:
2426
- tags:
2427
- - comms
2428
- summary: Send a single transactional message to one contact using a template.
2429
- operationId: comms_sendTransactional
2430
- security:
2431
- - bearerAuth: []
2432
- parameters:
2433
- - name: collectionId
2434
- in: path
2435
- required: true
2436
- schema:
2437
- type: string
2438
- responses:
2439
- 200:
2440
- description: Success
2441
- content:
2442
- application/json:
2443
- schema:
2444
- $ref: "#/components/schemas/TransactionalSendResult"
2445
- 400:
2446
- description: Bad request
2447
- 401:
2448
- description: Unauthorized
2449
- 404:
2450
- description: Not found
2451
- requestBody:
2452
- required: true
2453
- content:
2454
- application/json:
2455
- schema:
2456
- $ref: "#/components/schemas/TransactionalSendRequest"
2457
2457
  /admin/collection/{collectionId}/contacts:
2458
2458
  get:
2459
2459
  tags:
@@ -5203,6 +5203,50 @@ paths:
5203
5203
  description: Unauthorized
5204
5204
  404:
5205
5205
  description: Not found
5206
+ /admin/collection/{collectionId}/products/{productId}/proofs/{proofId}/migrate:
5207
+ post:
5208
+ tags:
5209
+ - proof
5210
+ summary: proof.migrate
5211
+ operationId: proof_migrate
5212
+ security:
5213
+ - bearerAuth: []
5214
+ parameters:
5215
+ - name: collectionId
5216
+ in: path
5217
+ required: true
5218
+ schema:
5219
+ type: string
5220
+ - name: productId
5221
+ in: path
5222
+ required: true
5223
+ schema:
5224
+ type: string
5225
+ - name: proofId
5226
+ in: path
5227
+ required: true
5228
+ schema:
5229
+ type: string
5230
+ responses:
5231
+ 200:
5232
+ description: Success
5233
+ content:
5234
+ application/json:
5235
+ schema:
5236
+ $ref: "#/components/schemas/ProofResponse"
5237
+ 400:
5238
+ description: Bad request
5239
+ 401:
5240
+ description: Unauthorized
5241
+ 404:
5242
+ description: Not found
5243
+ requestBody:
5244
+ required: true
5245
+ content:
5246
+ application/json:
5247
+ schema:
5248
+ type: object
5249
+ additionalProperties: true
5206
5250
  /admin/collection/{collectionId}/proof/findByUser/{userId}:
5207
5251
  get:
5208
5252
  tags:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@proveanything/smartlinks",
3
- "version": "1.7.2",
3
+ "version": "1.7.4",
4
4
  "description": "Official JavaScript/TypeScript SDK for the Smartlinks API",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",