@proveanything/smartlinks 1.16.0 → 1.16.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -10,16 +10,29 @@ export declare namespace lots {
10
10
  function create(collectionId: string, lot: LotCreateInput): Promise<Lot>;
11
11
  /** List lots (summary rows; `payload`/`productIds` omitted). Filter by status, search, or containing productId. */
12
12
  function list(collectionId: string, params?: ListLotsParams): Promise<Lot[]>;
13
- /** Get the full lot record. */
14
- function get(collectionId: string, lotId: string): Promise<Lot>;
13
+ /** Get the full lot record. Pass `{ includeDeleted: true }` to fetch a soft-deleted one. */
14
+ function get(collectionId: string, lotId: string, opts?: {
15
+ includeDeleted?: boolean;
16
+ }): Promise<Lot>;
15
17
  /** Look up a lot by its number (case-insensitive) — used by scan/resolver flows. */
16
- function getByNumber(collectionId: string, lotNumber: string): Promise<Lot>;
18
+ function getByNumber(collectionId: string, lotNumber: string, opts?: {
19
+ includeDeleted?: boolean;
20
+ }): Promise<Lot>;
17
21
  /** Update a lot. Re-resolves members if the selector changed (response then carries `diff`). */
18
22
  function update(collectionId: string, lotId: string, lot: LotUpdateInput): Promise<Lot>;
19
- /** Soft-archive a lot (never deletes members). */
23
+ /**
24
+ * Soft-delete a lot — recoverable, and frees its `lotNumber` for reuse. Distinct from
25
+ * {@link archive}. Hidden from reads unless `{ includeDeleted: true }`; undo with {@link restore}.
26
+ */
27
+ function remove(collectionId: string, lotId: string): Promise<{
28
+ success: boolean;
29
+ }>;
30
+ /** Archive a lot — a live lifecycle state (stays visible, keeps its number). Not a delete. */
20
31
  function archive(collectionId: string, lotId: string): Promise<{
21
32
  success: boolean;
22
33
  }>;
34
+ /** Restore a soft-deleted lot. Rejects (409) if a live lot now uses the same number. */
35
+ function restore(collectionId: string, lotId: string): Promise<Lot>;
23
36
  /** Re-resolve members from the current selector; returns the lot + a member diff. */
24
37
  function resolve(collectionId: string, lotId: string): Promise<ResolveLotResponse>;
25
38
  /** Paginated member product summaries. */
package/dist/api/lots.js CHANGED
@@ -22,6 +22,8 @@ export var lots;
22
22
  qs.append('search', params.search);
23
23
  if (params.productId)
24
24
  qs.append('productId', params.productId);
25
+ if (params.includeDeleted)
26
+ qs.append('includeDeleted', 'true');
25
27
  const s = qs.toString();
26
28
  return s ? `?${s}` : '';
27
29
  }
@@ -46,14 +48,16 @@ export var lots;
46
48
  return res.lots;
47
49
  }
48
50
  lots.list = list;
49
- /** Get the full lot record. */
50
- async function get(collectionId, lotId) {
51
- return request(`${adminBase(collectionId)}/${encodeURIComponent(lotId)}`);
51
+ /** Get the full lot record. Pass `{ includeDeleted: true }` to fetch a soft-deleted one. */
52
+ async function get(collectionId, lotId, opts = {}) {
53
+ const qs = opts.includeDeleted ? '?includeDeleted=true' : '';
54
+ return request(`${adminBase(collectionId)}/${encodeURIComponent(lotId)}${qs}`);
52
55
  }
53
56
  lots.get = get;
54
57
  /** Look up a lot by its number (case-insensitive) — used by scan/resolver flows. */
55
- async function getByNumber(collectionId, lotNumber) {
56
- return request(`${adminBase(collectionId)}/by-number/${encodeURIComponent(lotNumber)}`);
58
+ async function getByNumber(collectionId, lotNumber, opts = {}) {
59
+ const qs = opts.includeDeleted ? '?includeDeleted=true' : '';
60
+ return request(`${adminBase(collectionId)}/by-number/${encodeURIComponent(lotNumber)}${qs}`);
57
61
  }
58
62
  lots.getByNumber = getByNumber;
59
63
  /** Update a lot. Re-resolves members if the selector changed (response then carries `diff`). */
@@ -61,11 +65,24 @@ export var lots;
61
65
  return put(`${adminBase(collectionId)}/${encodeURIComponent(lotId)}`, lot);
62
66
  }
63
67
  lots.update = update;
64
- /** Soft-archive a lot (never deletes members). */
65
- async function archive(collectionId, lotId) {
68
+ /**
69
+ * Soft-delete a lot — recoverable, and frees its `lotNumber` for reuse. Distinct from
70
+ * {@link archive}. Hidden from reads unless `{ includeDeleted: true }`; undo with {@link restore}.
71
+ */
72
+ async function remove(collectionId, lotId) {
66
73
  return del(`${adminBase(collectionId)}/${encodeURIComponent(lotId)}`);
67
74
  }
75
+ lots.remove = remove;
76
+ /** Archive a lot — a live lifecycle state (stays visible, keeps its number). Not a delete. */
77
+ async function archive(collectionId, lotId) {
78
+ return post(`${adminBase(collectionId)}/${encodeURIComponent(lotId)}/archive`, {});
79
+ }
68
80
  lots.archive = archive;
81
+ /** Restore a soft-deleted lot. Rejects (409) if a live lot now uses the same number. */
82
+ async function restore(collectionId, lotId) {
83
+ return post(`${adminBase(collectionId)}/${encodeURIComponent(lotId)}/restore`, {});
84
+ }
85
+ lots.restore = restore;
69
86
  /** Re-resolve members from the current selector; returns the lot + a member diff. */
70
87
  async function resolve(collectionId, lotId) {
71
88
  return post(`${adminBase(collectionId)}/${encodeURIComponent(lotId)}/resolve`, {});
@@ -1,4 +1,4 @@
1
- import { ProofResponse, ProofCreateRequest, ProofUpdateRequest, ProofClaimRequest, ProofGrant, CreateGrantOptions, RedeemGrantOptions, RedeemGrantResult, ProofTransfer, TransferProofOptions, TransferProofResult } from "../types/proof";
1
+ import { ProofResponse, ProofCreateRequest, ProofUpdateRequest, ProofValuesUpdateRequest, ProofClaimRequest, ProofGrant, CreateGrantOptions, RedeemGrantOptions, RedeemGrantResult, ProofTransfer, TransferProofOptions, TransferProofResult } from "../types/proof";
2
2
  export declare namespace proof {
3
3
  /**
4
4
  * Retrieves a single Proof by Collection ID, Product ID, and Proof ID.
@@ -42,6 +42,27 @@ export declare namespace proof {
42
42
  * Object zones deep-merge, so you can change one field without wiping the rest.
43
43
  */
44
44
  function update(collectionId: string, productId: string, proofId: string, values: ProofUpdateRequest): Promise<ProofResponse>;
45
+ /**
46
+ * Owner self-service update of a proof's owner-writable data.
47
+ * PUT /public/collection/:collectionId/product/:productId/proof/:proofId/values
48
+ *
49
+ * The public counterpart to admin `update` — the current OWNER (or a collection
50
+ * admin) editing their own proof, no admin credentials required. Only owner-writable
51
+ * zones are honoured (see {@link ProofValuesUpdateRequest}):
52
+ * ```ts
53
+ * proof.updateValues(collectionId, productId, proofId, {
54
+ * colour: 'blue', // → proof.values.colour (public)
55
+ * owner: { warranty: '2y' }, // → proof.values.owner (owner-scoped)
56
+ * personal: { nickname: 'Bo' }, // → proof.values.personal[callerUid] (private, own slot only)
57
+ * })
58
+ * ```
59
+ * `personal` always targets the caller's OWN slot — you cannot write another
60
+ * user's personal data, even as an admin. Object zones deep-merge (owner/personal
61
+ * merge field-by-field), so you can change one field without wiping the rest.
62
+ * Business-only zones (`data`/`admin`/`private`) are not writable here — use the
63
+ * admin `update` for those.
64
+ */
65
+ function updateValues(collectionId: string, productId: string, proofId: string, values: ProofValuesUpdateRequest): Promise<ProofResponse>;
45
66
  /**
46
67
  * Claim a proof for a product using a proof ID (serial number, NFC tag, etc.).
47
68
  * PUT /public/collection/:collectionId/product/:productId/proof/:proofId/claim
package/dist/api/proof.js CHANGED
@@ -64,6 +64,31 @@ export var proof;
64
64
  return put(path, values);
65
65
  }
66
66
  proof.update = update;
67
+ /**
68
+ * Owner self-service update of a proof's owner-writable data.
69
+ * PUT /public/collection/:collectionId/product/:productId/proof/:proofId/values
70
+ *
71
+ * The public counterpart to admin `update` — the current OWNER (or a collection
72
+ * admin) editing their own proof, no admin credentials required. Only owner-writable
73
+ * zones are honoured (see {@link ProofValuesUpdateRequest}):
74
+ * ```ts
75
+ * proof.updateValues(collectionId, productId, proofId, {
76
+ * colour: 'blue', // → proof.values.colour (public)
77
+ * owner: { warranty: '2y' }, // → proof.values.owner (owner-scoped)
78
+ * personal: { nickname: 'Bo' }, // → proof.values.personal[callerUid] (private, own slot only)
79
+ * })
80
+ * ```
81
+ * `personal` always targets the caller's OWN slot — you cannot write another
82
+ * user's personal data, even as an admin. Object zones deep-merge (owner/personal
83
+ * merge field-by-field), so you can change one field without wiping the rest.
84
+ * Business-only zones (`data`/`admin`/`private`) are not writable here — use the
85
+ * admin `update` for those.
86
+ */
87
+ async function updateValues(collectionId, productId, proofId, values) {
88
+ const path = `/public/collection/${encodeURIComponent(collectionId)}/product/${encodeURIComponent(productId)}/proof/${encodeURIComponent(proofId)}/values`;
89
+ return put(path, values);
90
+ }
91
+ proof.updateValues = updateValues;
67
92
  /**
68
93
  * Claim a proof for a product using a proof ID (serial number, NFC tag, etc.).
69
94
  * PUT /public/collection/:collectionId/product/:productId/proof/:proofId/claim
@@ -1,6 +1,6 @@
1
1
  # Smartlinks API Summary
2
2
 
3
- Version: 1.16.0 | Generated: 2026-09-01T12:28:06.435Z
3
+ Version: 1.16.2 | Generated: 2026-09-05T10:46:06.239Z
4
4
 
5
5
  This is a concise summary of all available API functions and types.
6
6
 
@@ -3549,20 +3549,25 @@ interface FirebaseTimestamp {
3549
3549
  ```typescript
3550
3550
  interface BatchResponse {
3551
3551
  id: string // Batch ID
3552
- name?: string // Batch name
3553
- expiryDate?: FirebaseTimestamp | string // Firebase timestamp or ISO 8601 date
3554
- productId?: string // Product ID (for collection-level searches)
3552
+ name?: string | null // Batch name
3553
+ expiryDate?: string | null
3554
+ productId?: string // Product ID
3555
3555
  collectionId?: string // Collection ID
3556
- [key: string]: any // Additional batch fields
3556
+ createdAt?: string // ISO 8601
3557
+ updatedAt?: string // ISO 8601
3558
+ deleted?: boolean
3559
+ deletedAt?: string // ISO 8601
3560
+ admin?: Record<string, any>
3561
+ [key: string]: any // Additional (schemaless) batch fields
3557
3562
  }
3558
3563
  ```
3559
3564
 
3560
3565
  **BatchCreateRequest** (interface)
3561
3566
  ```typescript
3562
3567
  interface BatchCreateRequest {
3563
- id: string // Batch ID
3564
- name?: string // Batch name
3565
- expiryDate?: FirebaseTimestamp | string // Firebase timestamp or ISO 8601 date
3568
+ id?: string
3569
+ name?: string
3570
+ expiryDate?: FirebaseTimestamp | string | Date
3566
3571
  [key: string]: any // Additional batch fields
3567
3572
  }
3568
3573
  ```
@@ -3570,8 +3575,8 @@ interface BatchCreateRequest {
3570
3575
  **BatchUpdateRequest** (interface)
3571
3576
  ```typescript
3572
3577
  interface BatchUpdateRequest {
3573
- name?: string // Batch name
3574
- expiryDate?: FirebaseTimestamp | string // Firebase timestamp or ISO 8601 date
3578
+ name?: string
3579
+ expiryDate?: FirebaseTimestamp | string | Date
3575
3580
  [key: string]: any // Additional batch fields
3576
3581
  }
3577
3582
  ```
@@ -6389,6 +6394,7 @@ interface Lot {
6389
6394
  updatedBy?: string | null
6390
6395
  createdAt: string
6391
6396
  updatedAt: string
6397
+ deletedAt?: string | null
6392
6398
  }
6393
6399
  ```
6394
6400
 
@@ -6412,6 +6418,7 @@ interface ListLotsParams {
6412
6418
  status?: LotStatus
6413
6419
  search?: string
6414
6420
  productId?: string
6421
+ includeDeleted?: boolean
6415
6422
  }
6416
6423
  ```
6417
6424
 
@@ -7431,6 +7438,9 @@ interface ProductFieldsConfig {
7431
7438
  ```typescript
7432
7439
  interface ProofValues {
7433
7440
  [key: string]: JsonValue | Record<string, JsonValue> | Record<string, Record<string, JsonValue>> | undefined
7441
+ * Owner-scoped: read/write by business + current owner; transfers with ownership.
7442
+ * Read exception: while the proof is `claimable`, this bag is also readable by everyone
7443
+ * (so a prospective claimer sees pre-set owner data); it reverts to owner-only once claimed.
7434
7444
  owner?: Record<string, JsonValue>
7435
7445
  personal?: Record<string, Record<string, JsonValue>>
7436
7446
  }
@@ -7489,6 +7499,15 @@ interface ProofCreateRequest {
7489
7499
  }
7490
7500
  ```
7491
7501
 
7502
+ **ProofValuesUpdateRequest** (interface)
7503
+ ```typescript
7504
+ interface ProofValuesUpdateRequest {
7505
+ [key: string]: JsonValue | Record<string, JsonValue> | undefined
7506
+ owner?: Record<string, JsonValue>
7507
+ personal?: Record<string, JsonValue>
7508
+ }
7509
+ ```
7510
+
7492
7511
  **ProofFieldsConfig** (interface)
7493
7512
  ```typescript
7494
7513
  interface ProofFieldsConfig {
@@ -8151,11 +8170,38 @@ interface TranslationUpdateRequest {
8151
8170
 
8152
8171
  ### variant
8153
8172
 
8154
- **VariantResponse** = `any`
8173
+ **VariantResponse** (interface)
8174
+ ```typescript
8175
+ interface VariantResponse {
8176
+ id: string
8177
+ name?: string | null
8178
+ productId?: string
8179
+ collectionId?: string
8180
+ createdAt?: string // ISO 8601
8181
+ updatedAt?: string // ISO 8601
8182
+ deleted?: boolean
8183
+ deletedAt?: string // ISO 8601
8184
+ admin?: Record<string, any>
8185
+ [key: string]: any // Additional (schemaless) variant fields
8186
+ }
8187
+ ```
8155
8188
 
8156
- **VariantCreateRequest** = `any`
8189
+ **VariantCreateRequest** (interface)
8190
+ ```typescript
8191
+ interface VariantCreateRequest {
8192
+ id?: string
8193
+ name?: string
8194
+ [key: string]: any
8195
+ }
8196
+ ```
8157
8197
 
8158
- **VariantUpdateRequest** = `any`
8198
+ **VariantUpdateRequest** (interface)
8199
+ ```typescript
8200
+ interface VariantUpdateRequest {
8201
+ name?: string
8202
+ [key: string]: any
8203
+ }
8204
+ ```
8159
8205
 
8160
8206
  ### widgets
8161
8207
 
@@ -9898,17 +9944,23 @@ Create a lot (resolves its selector into members).
9898
9944
  **list**(collectionId: string, params: ListLotsParams = {}) → `Promise<Lot[]>`
9899
9945
  List lots (summary rows; `payload`/`productIds` omitted). Filter by status, search, or containing productId.
9900
9946
 
9901
- **get**(collectionId: string, lotId: string) → `Promise<Lot>`
9902
- Get the full lot record.
9947
+ **get**(collectionId: string, lotId: string, opts: { includeDeleted?: boolean } = {}) → `Promise<Lot>`
9948
+ Get the full lot record. Pass `{ includeDeleted: true }` to fetch a soft-deleted one.
9903
9949
 
9904
- **getByNumber**(collectionId: string, lotNumber: string) → `Promise<Lot>`
9950
+ **getByNumber**(collectionId: string, lotNumber: string, opts: { includeDeleted?: boolean } = {}) → `Promise<Lot>`
9905
9951
  Look up a lot by its number (case-insensitive) — used by scan/resolver flows.
9906
9952
 
9907
9953
  **update**(collectionId: string, lotId: string, lot: LotUpdateInput) → `Promise<Lot>`
9908
9954
  Update a lot. Re-resolves members if the selector changed (response then carries `diff`).
9909
9955
 
9956
+ **remove**(collectionId: string, lotId: string) → `Promise<`
9957
+ Soft-delete a lot — recoverable, and frees its `lotNumber` for reuse. Distinct from {@link archive}. Hidden from reads unless `{ includeDeleted: true }`; undo with {@link restore}.
9958
+
9910
9959
  **archive**(collectionId: string, lotId: string) → `Promise<`
9911
- Soft-archive a lot (never deletes members).
9960
+ Archive a lot — a live lifecycle state (stays visible, keeps its number). Not a delete.
9961
+
9962
+ **restore**(collectionId: string, lotId: string) → `Promise<Lot>`
9963
+ Restore a soft-deleted lot. Rejects (409) if a live lot now uses the same number.
9912
9964
 
9913
9965
  **resolve**(collectionId: string, lotId: string) → `Promise<ResolveLotResponse>`
9914
9966
  Re-resolve members from the current selector; returns the lot + a member diff.
@@ -10301,6 +10353,12 @@ Create a proof for a product (admin only). POST /admin/collection/:collectionId/
10301
10353
  values: ProofUpdateRequest) → `Promise<ProofResponse>`
10302
10354
  Update a proof for a product (admin only). PUT /admin/collection/:collectionId/product/:productId/proof/:proofId Pass the fields to change **at the root**, keyed by zone (see {@link ProofWrite}): ```ts proof.update(collectionId, productId, proofId, { data: { serialNo: 1002 }, // → proof.data (admin-only writable) values: { colour: 'blue' }, // → proof.values }) ``` Object zones deep-merge, so you can change one field without wiping the rest.
10303
10355
 
10356
+ **updateValues**(collectionId: string,
10357
+ productId: string,
10358
+ proofId: string,
10359
+ values: ProofValuesUpdateRequest) → `Promise<ProofResponse>`
10360
+ Owner self-service update of a proof's owner-writable data. PUT /public/collection/:collectionId/product/:productId/proof/:proofId/values The public counterpart to admin `update` — the current OWNER (or a collection admin) editing their own proof, no admin credentials required. Only owner-writable zones are honoured (see {@link ProofValuesUpdateRequest}): ```ts proof.updateValues(collectionId, productId, proofId, { colour: 'blue', // → proof.values.colour (public) owner: { warranty: '2y' }, // → proof.values.owner (owner-scoped) personal: { nickname: 'Bo' }, // → proof.values.personal[callerUid] (private, own slot only) }) ``` `personal` always targets the caller's OWN slot — you cannot write another user's personal data, even as an admin. Object zones deep-merge (owner/personal merge field-by-field), so you can change one field without wiping the rest. Business-only zones (`data`/`admin`/`private`) are not writable here — use the admin `update` for those.
10361
+
10304
10362
  **claim**(collectionId: string,
10305
10363
  productId: string,
10306
10364
  proofId: string,
package/dist/docs/lots.md CHANGED
@@ -18,9 +18,22 @@ truth for its shared data.
18
18
  - `{ mode: 'products', productIds: [...] }` — an explicit list.
19
19
  - **`productIds` / `productCount`** — the materialised snapshot of resolved members (re-resolved on create, on selector change, and on demand via `resolve`).
20
20
  - **`payload`** — shared lot data (dates, supplier ref, custom fields). Lives only on the lot.
21
- - **`status`** — `open` → `closed` → `recalled` → `archived`. Archiving never deletes anything.
21
+ - **`status`** — `open` → `closed` → `recalled` → `archived`. A live lifecycle state, distinct from deletion.
22
22
  - **`destination`** — optional lot-level redirect; wins over the product's on a lot-scoped scan.
23
23
 
24
+ ### Archive vs delete
25
+
26
+ Two separate ideas — mirroring the platform's `deletedAt` convention:
27
+
28
+ | | `archive` (`status: 'archived'`) | `remove` (soft-delete, `deletedAt`) |
29
+ |---|---|---|
30
+ | Record | stays **live** & visible | **hidden** from reads unless `includeDeleted: true` |
31
+ | `lotNumber` | **stays reserved** | **freed** for reuse by a new lot |
32
+ | AI(10) scan | not resolved (excluded) | not resolved |
33
+ | Reversible | change status back | `restore` (409 if the number was taken by a live lot) |
34
+
35
+ Nothing is ever hard-deleted (joins/history stay intact). Use **archive** for "this run is done, keep it around"; use **remove** for "this was a mistake, and I want the number back."
36
+
24
37
  ---
25
38
 
26
39
  ## SDK — `SL.lots.*`
@@ -50,7 +63,11 @@ const containing = await lots.list(collectionId, { productId: 'prd_abc' }) // r
50
63
  const updated = await lots.update(collectionId, lot.id, { status: 'closed' })
51
64
  const { diff } = await lots.resolve(collectionId, lot.id) // { added, removed }
52
65
  const members = await lots.listProducts(collectionId, lot.id, { page: 1, limit: 50 })
53
- await lots.archive(collectionId, lot.id)
66
+
67
+ await lots.archive(collectionId, lot.id) // live, keeps its number
68
+ await lots.remove(collectionId, lot.id) // soft-delete, frees the number
69
+ const withDeleted = await lots.list(collectionId, { includeDeleted: true })
70
+ const restored = await lots.restore(collectionId, lot.id) // undo a soft-delete
54
71
 
55
72
  // Cross-app reads
56
73
  const publicLots = await lots.publicList(collectionId)
@@ -37,7 +37,8 @@ proof.admin = { … } // BUSINESS-ONLY → read: business, write: b
37
37
  proof.values = { // consumer bag — owner + business-writable
38
38
  <anyKey>: …, // PUBLIC → read: everyone,
39
39
  // write: business + current owner
40
- owner: { … }, // OWNER-SCOPED → read: business + current owner,
40
+ owner: { … }, // OWNER-SCOPED → read: business + current owner
41
+ // (also everyone while `claimable`, see below),
41
42
  // write: business + current owner,
42
43
  // transfers with ownership
43
44
  personal: { // PER-USER → read/write: only the specific user,
@@ -75,10 +76,12 @@ The field-config editor and SDK write helpers MUST reject attempts to create top
75
76
  | `proof.data.*` | ✅ | ✅ | ✅ | ✅ |
76
77
  | `proof.admin.*` | ❌ | ❌ | ❌ | ✅ |
77
78
  | `proof.values.<publicKey>` | ✅ | ✅ | ✅ | ✅ |
78
- | `proof.values.owner.*` | | | ✅ | ✅ |
79
+ | `proof.values.owner.*` | ❌ † | | ✅ | ✅ |
79
80
  | `proof.values.personal[me].*` | ❌ | ✅ (own slot only) | ✅ (own slot) | ❌ (see note) |
80
81
  | `proof.values.personal[other].*` | ❌ | ❌ | ❌ | ❌ (see note) |
81
82
 
83
+ > **† Claimable exception (`proof.values.owner.*`):** while a proof is **claimable** (`proof.claimable === true` *or* `proof.values.claimable === true`), its `owner` bag is returned to **everyone** — public/anonymous and authenticated non-owners included — so a prospective claimer can see pre-set owner data before claiming. Once the proof is claimed (`claimable` flips off) it reverts to owner-only (unless re-marked claimable). This is **read-only** exposure — write authority is unchanged (still owner + business). Implication: don't put anything in `values.owner` on a claimable proof that shouldn't be visible before it's claimed.
84
+ >
82
85
  > **Note on `personal`:** the default rule is that `personal` slots are readable *only* by the user whose `userId` matches the slot key — not even business admins. If the platform ever needs an admin-visible variant, it should be a separate mechanism, not a relaxation of this rule.
83
86
 
84
87
  ## Who writes what (authority matrix)
package/dist/openapi.yaml CHANGED
@@ -4996,6 +4996,38 @@ paths:
4996
4996
  description: Unauthorized
4997
4997
  404:
4998
4998
  description: Not found
4999
+ /admin/collection/{collectionId}/lots/{lotId}/restore:
5000
+ post:
5001
+ tags:
5002
+ - lots
5003
+ summary: Restore a soft-deleted lot.
5004
+ operationId: lots_restore
5005
+ security:
5006
+ - bearerAuth: []
5007
+ parameters:
5008
+ - name: collectionId
5009
+ in: path
5010
+ required: true
5011
+ schema:
5012
+ type: string
5013
+ - name: lotId
5014
+ in: path
5015
+ required: true
5016
+ schema:
5017
+ type: string
5018
+ responses:
5019
+ 200:
5020
+ description: Success
5021
+ content:
5022
+ application/json:
5023
+ schema:
5024
+ $ref: "#/components/schemas/Lot"
5025
+ 400:
5026
+ description: Bad request
5027
+ 401:
5028
+ description: Unauthorized
5029
+ 404:
5030
+ description: Not found
4999
5031
  /admin/collection/{collectionId}/lots{params}:
5000
5032
  get:
5001
5033
  tags:
@@ -12611,6 +12643,48 @@ paths:
12611
12643
  description: Unauthorized
12612
12644
  404:
12613
12645
  description: Not found
12646
+ /public/collection/{collectionId}/product/{productId}/proof/{proofId}/values:
12647
+ put:
12648
+ tags:
12649
+ - proof
12650
+ summary: proof.updateValues
12651
+ operationId: proof_updateValues
12652
+ security: []
12653
+ parameters:
12654
+ - name: collectionId
12655
+ in: path
12656
+ required: true
12657
+ schema:
12658
+ type: string
12659
+ - name: productId
12660
+ in: path
12661
+ required: true
12662
+ schema:
12663
+ type: string
12664
+ - name: proofId
12665
+ in: path
12666
+ required: true
12667
+ schema:
12668
+ type: string
12669
+ responses:
12670
+ 200:
12671
+ description: Success
12672
+ content:
12673
+ application/json:
12674
+ schema:
12675
+ $ref: "#/components/schemas/ProofResponse"
12676
+ 400:
12677
+ description: Bad request
12678
+ 401:
12679
+ description: Unauthorized
12680
+ 404:
12681
+ description: Not found
12682
+ requestBody:
12683
+ required: true
12684
+ content:
12685
+ application/json:
12686
+ schema:
12687
+ $ref: "#/components/schemas/ProofValuesUpdateRequest"
12614
12688
  /public/collection/{collectionId}/products/{productId}/createClaim:
12615
12689
  post:
12616
12690
  tags:
@@ -19810,12 +19884,22 @@ components:
19810
19884
  name:
19811
19885
  type: string
19812
19886
  expiryDate:
19813
- type: object
19814
- additionalProperties: true
19887
+ type: string
19815
19888
  productId:
19816
19889
  type: string
19817
19890
  collectionId:
19818
19891
  type: string
19892
+ createdAt:
19893
+ type: string
19894
+ updatedAt:
19895
+ type: string
19896
+ deleted:
19897
+ type: boolean
19898
+ deletedAt:
19899
+ type: string
19900
+ admin:
19901
+ type: object
19902
+ additionalProperties: true
19819
19903
  required:
19820
19904
  - id
19821
19905
  BatchCreateRequest:
@@ -19828,8 +19912,6 @@ components:
19828
19912
  expiryDate:
19829
19913
  type: object
19830
19914
  additionalProperties: true
19831
- required:
19832
- - id
19833
19915
  BatchUpdateRequest:
19834
19916
  type: object
19835
19917
  properties:
@@ -23737,6 +23819,8 @@ components:
23737
23819
  type: string
23738
23820
  updatedAt:
23739
23821
  type: string
23822
+ deletedAt:
23823
+ type: string
23740
23824
  required:
23741
23825
  - id
23742
23826
  - collectionId
@@ -23777,6 +23861,8 @@ components:
23777
23861
  type: string
23778
23862
  productId:
23779
23863
  type: string
23864
+ includeDeleted:
23865
+ type: boolean
23780
23866
  ListLotsResponse:
23781
23867
  type: object
23782
23868
  properties:
@@ -25620,6 +25706,17 @@ components:
25620
25706
  type: object
25621
25707
  additionalProperties:
25622
25708
  $ref: "#/components/schemas/JsonValue"
25709
+ ProofValuesUpdateRequest:
25710
+ type: object
25711
+ properties:
25712
+ owner:
25713
+ type: object
25714
+ additionalProperties:
25715
+ $ref: "#/components/schemas/JsonValue"
25716
+ personal:
25717
+ type: object
25718
+ additionalProperties:
25719
+ $ref: "#/components/schemas/JsonValue"
25623
25720
  ProofFieldsConfig:
25624
25721
  type: object
25625
25722
  properties:
@@ -26654,6 +26751,42 @@ components:
26654
26751
  metadata:
26655
26752
  type: object
26656
26753
  additionalProperties: true
26754
+ VariantResponse:
26755
+ type: object
26756
+ properties:
26757
+ id:
26758
+ type: string
26759
+ name:
26760
+ type: string
26761
+ productId:
26762
+ type: string
26763
+ collectionId:
26764
+ type: string
26765
+ createdAt:
26766
+ type: string
26767
+ updatedAt:
26768
+ type: string
26769
+ deleted:
26770
+ type: boolean
26771
+ deletedAt:
26772
+ type: string
26773
+ admin:
26774
+ type: object
26775
+ additionalProperties: true
26776
+ required:
26777
+ - id
26778
+ VariantCreateRequest:
26779
+ type: object
26780
+ properties:
26781
+ id:
26782
+ type: string
26783
+ name:
26784
+ type: string
26785
+ VariantUpdateRequest:
26786
+ type: object
26787
+ properties:
26788
+ name:
26789
+ type: string
26657
26790
  NavigationRequest:
26658
26791
  type: object
26659
26792
  properties:
@@ -1,28 +1,39 @@
1
1
  /**
2
- * Firebase Timestamp object.
2
+ * @deprecated Batches moved to Postgres — dates now come back as ISO 8601 strings, never
3
+ * Firestore Timestamp objects. Kept only so request bodies can still pass a legacy value.
3
4
  */
4
5
  export interface FirebaseTimestamp {
5
6
  seconds: number;
6
7
  nanoseconds?: number;
7
8
  }
8
9
  /**
9
- * Represents a Batch object.
10
+ * Represents a Batch object. Dates are **ISO 8601 strings**.
10
11
  */
11
12
  export interface BatchResponse {
12
13
  id: string;
13
- name?: string;
14
- expiryDate?: FirebaseTimestamp | string;
14
+ name?: string | null;
15
+ /** ISO 8601 date-time (was a Firebase Timestamp; now normalised to a string). */
16
+ expiryDate?: string | null;
15
17
  productId?: string;
16
18
  collectionId?: string;
19
+ createdAt?: string;
20
+ updatedAt?: string;
21
+ /** Present only on soft-deleted batches. */
22
+ deleted?: boolean;
23
+ deletedAt?: string;
24
+ /** Admin-only zone (e.g. `lastSerialId`). Returned on admin reads only — never on public reads. */
25
+ admin?: Record<string, any>;
17
26
  [key: string]: any;
18
27
  }
19
28
  /**
20
29
  * Request payload for creating a new batch.
21
30
  */
22
31
  export interface BatchCreateRequest {
23
- id: string;
32
+ /** @deprecated Ignored — the server generates the batch id. */
33
+ id?: string;
24
34
  name?: string;
25
- expiryDate?: FirebaseTimestamp | string;
35
+ /** A `Date`, ISO 8601 string, or legacy Firebase Timestamp — all accepted. */
36
+ expiryDate?: FirebaseTimestamp | string | Date;
26
37
  [key: string]: any;
27
38
  }
28
39
  /**
@@ -30,7 +41,7 @@ export interface BatchCreateRequest {
30
41
  */
31
42
  export interface BatchUpdateRequest {
32
43
  name?: string;
33
- expiryDate?: FirebaseTimestamp | string;
44
+ expiryDate?: FirebaseTimestamp | string | Date;
34
45
  [key: string]: any;
35
46
  }
36
47
  /**
@@ -39,6 +39,8 @@ export interface Lot {
39
39
  updatedBy?: string | null;
40
40
  createdAt: string;
41
41
  updatedAt: string;
42
+ /** Soft-delete timestamp (recoverable). `null` for live lots. Distinct from `status:'archived'`. */
43
+ deletedAt?: string | null;
42
44
  }
43
45
  export interface LotCreateInput {
44
46
  lotNumber: string;
@@ -57,6 +59,8 @@ export interface ListLotsParams {
57
59
  search?: string;
58
60
  /** Reverse lookup — lots containing this product id. */
59
61
  productId?: string;
62
+ /** Admin only: include soft-deleted lots (default false). */
63
+ includeDeleted?: boolean;
60
64
  }
61
65
  export interface ListLotsResponse {
62
66
  lots: Lot[];
@@ -7,7 +7,11 @@ import { JsonValue, ScopedFieldDef } from './product';
7
7
  */
8
8
  export interface ProofValues {
9
9
  [key: string]: JsonValue | Record<string, JsonValue> | Record<string, Record<string, JsonValue>> | undefined;
10
- /** Owner-scoped: read/write by business + current owner; transfers with ownership. */
10
+ /**
11
+ * Owner-scoped: read/write by business + current owner; transfers with ownership.
12
+ * Read exception: while the proof is `claimable`, this bag is also readable by everyone
13
+ * (so a prospective claimer sees pre-set owner data); it reverts to owner-only once claimed.
14
+ */
11
15
  owner?: Record<string, JsonValue>;
12
16
  /** Per-user: read/write only by the matching userId; not visible to the next owner, not even business admins. */
13
17
  personal?: Record<string, Record<string, JsonValue>>;
@@ -111,6 +115,27 @@ export interface ProofCreateRequest {
111
115
  export type ProofUpdateRequest = Partial<ProofWrite> & {
112
116
  proof?: ProofWrite;
113
117
  };
118
+ /**
119
+ * Body for the owner self-service values write (`proof.updateValues`), the
120
+ * public counterpart to the admin `update`. The caller must be the current
121
+ * owner (or a collection admin). Only owner-writable zones are honoured:
122
+ *
123
+ * - flat keys → `proof.values.<key>` (public data)
124
+ * - `owner` → merged into `proof.values.owner` (owner-scoped)
125
+ * - `personal` → merged into `proof.values.personal[callerUid]` — the
126
+ * caller's OWN private slot only, never another user's
127
+ * (owner-only, non-transferring; not even admins can
128
+ * write someone else's slot)
129
+ *
130
+ * `private` / `proof` sub-keys are business-only and are ignored here.
131
+ */
132
+ export interface ProofValuesUpdateRequest {
133
+ [key: string]: JsonValue | Record<string, JsonValue> | undefined;
134
+ /** Owner-scoped data, merged into `proof.values.owner`. */
135
+ owner?: Record<string, JsonValue>;
136
+ /** The caller's own private slot, merged into `proof.values.personal[callerUid]`. */
137
+ personal?: Record<string, JsonValue>;
138
+ }
114
139
  export type ProofClaimRequest = Record<string, any>;
115
140
  /**
116
141
  * `'public'` (default, omitted) reads/writes `proof.values[key]`.
@@ -1,12 +1,33 @@
1
1
  /**
2
- * Represents a Variant object.
2
+ * Represents a Variant object. Dates are ISO 8601 strings (Postgres-backed).
3
3
  */
4
- export type VariantResponse = any;
4
+ export interface VariantResponse {
5
+ id: string;
6
+ name?: string | null;
7
+ productId?: string;
8
+ collectionId?: string;
9
+ createdAt?: string;
10
+ updatedAt?: string;
11
+ /** Present only on soft-deleted variants. */
12
+ deleted?: boolean;
13
+ deletedAt?: string;
14
+ /** Admin-only zone (e.g. `lastSerialId`). Admin reads only — never on public reads. */
15
+ admin?: Record<string, any>;
16
+ [key: string]: any;
17
+ }
5
18
  /**
6
19
  * Request payload for creating a new variant.
7
20
  */
8
- export type VariantCreateRequest = any;
21
+ export interface VariantCreateRequest {
22
+ /** @deprecated Ignored — the server generates the variant id. (Use PUT with an id to choose one.) */
23
+ id?: string;
24
+ name?: string;
25
+ [key: string]: any;
26
+ }
9
27
  /**
10
- * Request payload for updating an existing variant.
28
+ * Request payload for updating a variant. PUT with a new id creates it (upsert).
11
29
  */
12
- export type VariantUpdateRequest = any;
30
+ export interface VariantUpdateRequest {
31
+ name?: string;
32
+ [key: string]: any;
33
+ }
@@ -110,13 +110,9 @@ export function buildPortalPath(params) {
110
110
  // Batch object - extract id and expiryDate
111
111
  extractedBatchId = batch.id;
112
112
  if (batch.expiryDate) {
113
- // Handle Firebase timestamp or Date
114
- if (typeof batch.expiryDate === 'object' && 'seconds' in batch.expiryDate) {
115
- expiryDate = new Date(batch.expiryDate.seconds * 1000);
116
- }
117
- else {
118
- expiryDate = batch.expiryDate;
119
- }
113
+ // Now an ISO string, but stay defensive about a legacy Firebase Timestamp object.
114
+ const exp = batch.expiryDate;
115
+ expiryDate = (exp && typeof exp === 'object' && 'seconds' in exp) ? new Date(exp.seconds * 1000) : exp;
120
116
  }
121
117
  }
122
118
  else if (batchId) {
@@ -1,6 +1,6 @@
1
1
  # Smartlinks API Summary
2
2
 
3
- Version: 1.16.0 | Generated: 2026-09-01T12:28:06.435Z
3
+ Version: 1.16.2 | Generated: 2026-09-05T10:46:06.239Z
4
4
 
5
5
  This is a concise summary of all available API functions and types.
6
6
 
@@ -3549,20 +3549,25 @@ interface FirebaseTimestamp {
3549
3549
  ```typescript
3550
3550
  interface BatchResponse {
3551
3551
  id: string // Batch ID
3552
- name?: string // Batch name
3553
- expiryDate?: FirebaseTimestamp | string // Firebase timestamp or ISO 8601 date
3554
- productId?: string // Product ID (for collection-level searches)
3552
+ name?: string | null // Batch name
3553
+ expiryDate?: string | null
3554
+ productId?: string // Product ID
3555
3555
  collectionId?: string // Collection ID
3556
- [key: string]: any // Additional batch fields
3556
+ createdAt?: string // ISO 8601
3557
+ updatedAt?: string // ISO 8601
3558
+ deleted?: boolean
3559
+ deletedAt?: string // ISO 8601
3560
+ admin?: Record<string, any>
3561
+ [key: string]: any // Additional (schemaless) batch fields
3557
3562
  }
3558
3563
  ```
3559
3564
 
3560
3565
  **BatchCreateRequest** (interface)
3561
3566
  ```typescript
3562
3567
  interface BatchCreateRequest {
3563
- id: string // Batch ID
3564
- name?: string // Batch name
3565
- expiryDate?: FirebaseTimestamp | string // Firebase timestamp or ISO 8601 date
3568
+ id?: string
3569
+ name?: string
3570
+ expiryDate?: FirebaseTimestamp | string | Date
3566
3571
  [key: string]: any // Additional batch fields
3567
3572
  }
3568
3573
  ```
@@ -3570,8 +3575,8 @@ interface BatchCreateRequest {
3570
3575
  **BatchUpdateRequest** (interface)
3571
3576
  ```typescript
3572
3577
  interface BatchUpdateRequest {
3573
- name?: string // Batch name
3574
- expiryDate?: FirebaseTimestamp | string // Firebase timestamp or ISO 8601 date
3578
+ name?: string
3579
+ expiryDate?: FirebaseTimestamp | string | Date
3575
3580
  [key: string]: any // Additional batch fields
3576
3581
  }
3577
3582
  ```
@@ -6389,6 +6394,7 @@ interface Lot {
6389
6394
  updatedBy?: string | null
6390
6395
  createdAt: string
6391
6396
  updatedAt: string
6397
+ deletedAt?: string | null
6392
6398
  }
6393
6399
  ```
6394
6400
 
@@ -6412,6 +6418,7 @@ interface ListLotsParams {
6412
6418
  status?: LotStatus
6413
6419
  search?: string
6414
6420
  productId?: string
6421
+ includeDeleted?: boolean
6415
6422
  }
6416
6423
  ```
6417
6424
 
@@ -7431,6 +7438,9 @@ interface ProductFieldsConfig {
7431
7438
  ```typescript
7432
7439
  interface ProofValues {
7433
7440
  [key: string]: JsonValue | Record<string, JsonValue> | Record<string, Record<string, JsonValue>> | undefined
7441
+ * Owner-scoped: read/write by business + current owner; transfers with ownership.
7442
+ * Read exception: while the proof is `claimable`, this bag is also readable by everyone
7443
+ * (so a prospective claimer sees pre-set owner data); it reverts to owner-only once claimed.
7434
7444
  owner?: Record<string, JsonValue>
7435
7445
  personal?: Record<string, Record<string, JsonValue>>
7436
7446
  }
@@ -7489,6 +7499,15 @@ interface ProofCreateRequest {
7489
7499
  }
7490
7500
  ```
7491
7501
 
7502
+ **ProofValuesUpdateRequest** (interface)
7503
+ ```typescript
7504
+ interface ProofValuesUpdateRequest {
7505
+ [key: string]: JsonValue | Record<string, JsonValue> | undefined
7506
+ owner?: Record<string, JsonValue>
7507
+ personal?: Record<string, JsonValue>
7508
+ }
7509
+ ```
7510
+
7492
7511
  **ProofFieldsConfig** (interface)
7493
7512
  ```typescript
7494
7513
  interface ProofFieldsConfig {
@@ -8151,11 +8170,38 @@ interface TranslationUpdateRequest {
8151
8170
 
8152
8171
  ### variant
8153
8172
 
8154
- **VariantResponse** = `any`
8173
+ **VariantResponse** (interface)
8174
+ ```typescript
8175
+ interface VariantResponse {
8176
+ id: string
8177
+ name?: string | null
8178
+ productId?: string
8179
+ collectionId?: string
8180
+ createdAt?: string // ISO 8601
8181
+ updatedAt?: string // ISO 8601
8182
+ deleted?: boolean
8183
+ deletedAt?: string // ISO 8601
8184
+ admin?: Record<string, any>
8185
+ [key: string]: any // Additional (schemaless) variant fields
8186
+ }
8187
+ ```
8155
8188
 
8156
- **VariantCreateRequest** = `any`
8189
+ **VariantCreateRequest** (interface)
8190
+ ```typescript
8191
+ interface VariantCreateRequest {
8192
+ id?: string
8193
+ name?: string
8194
+ [key: string]: any
8195
+ }
8196
+ ```
8157
8197
 
8158
- **VariantUpdateRequest** = `any`
8198
+ **VariantUpdateRequest** (interface)
8199
+ ```typescript
8200
+ interface VariantUpdateRequest {
8201
+ name?: string
8202
+ [key: string]: any
8203
+ }
8204
+ ```
8159
8205
 
8160
8206
  ### widgets
8161
8207
 
@@ -9898,17 +9944,23 @@ Create a lot (resolves its selector into members).
9898
9944
  **list**(collectionId: string, params: ListLotsParams = {}) → `Promise<Lot[]>`
9899
9945
  List lots (summary rows; `payload`/`productIds` omitted). Filter by status, search, or containing productId.
9900
9946
 
9901
- **get**(collectionId: string, lotId: string) → `Promise<Lot>`
9902
- Get the full lot record.
9947
+ **get**(collectionId: string, lotId: string, opts: { includeDeleted?: boolean } = {}) → `Promise<Lot>`
9948
+ Get the full lot record. Pass `{ includeDeleted: true }` to fetch a soft-deleted one.
9903
9949
 
9904
- **getByNumber**(collectionId: string, lotNumber: string) → `Promise<Lot>`
9950
+ **getByNumber**(collectionId: string, lotNumber: string, opts: { includeDeleted?: boolean } = {}) → `Promise<Lot>`
9905
9951
  Look up a lot by its number (case-insensitive) — used by scan/resolver flows.
9906
9952
 
9907
9953
  **update**(collectionId: string, lotId: string, lot: LotUpdateInput) → `Promise<Lot>`
9908
9954
  Update a lot. Re-resolves members if the selector changed (response then carries `diff`).
9909
9955
 
9956
+ **remove**(collectionId: string, lotId: string) → `Promise<`
9957
+ Soft-delete a lot — recoverable, and frees its `lotNumber` for reuse. Distinct from {@link archive}. Hidden from reads unless `{ includeDeleted: true }`; undo with {@link restore}.
9958
+
9910
9959
  **archive**(collectionId: string, lotId: string) → `Promise<`
9911
- Soft-archive a lot (never deletes members).
9960
+ Archive a lot — a live lifecycle state (stays visible, keeps its number). Not a delete.
9961
+
9962
+ **restore**(collectionId: string, lotId: string) → `Promise<Lot>`
9963
+ Restore a soft-deleted lot. Rejects (409) if a live lot now uses the same number.
9912
9964
 
9913
9965
  **resolve**(collectionId: string, lotId: string) → `Promise<ResolveLotResponse>`
9914
9966
  Re-resolve members from the current selector; returns the lot + a member diff.
@@ -10301,6 +10353,12 @@ Create a proof for a product (admin only). POST /admin/collection/:collectionId/
10301
10353
  values: ProofUpdateRequest) → `Promise<ProofResponse>`
10302
10354
  Update a proof for a product (admin only). PUT /admin/collection/:collectionId/product/:productId/proof/:proofId Pass the fields to change **at the root**, keyed by zone (see {@link ProofWrite}): ```ts proof.update(collectionId, productId, proofId, { data: { serialNo: 1002 }, // → proof.data (admin-only writable) values: { colour: 'blue' }, // → proof.values }) ``` Object zones deep-merge, so you can change one field without wiping the rest.
10303
10355
 
10356
+ **updateValues**(collectionId: string,
10357
+ productId: string,
10358
+ proofId: string,
10359
+ values: ProofValuesUpdateRequest) → `Promise<ProofResponse>`
10360
+ Owner self-service update of a proof's owner-writable data. PUT /public/collection/:collectionId/product/:productId/proof/:proofId/values The public counterpart to admin `update` — the current OWNER (or a collection admin) editing their own proof, no admin credentials required. Only owner-writable zones are honoured (see {@link ProofValuesUpdateRequest}): ```ts proof.updateValues(collectionId, productId, proofId, { colour: 'blue', // → proof.values.colour (public) owner: { warranty: '2y' }, // → proof.values.owner (owner-scoped) personal: { nickname: 'Bo' }, // → proof.values.personal[callerUid] (private, own slot only) }) ``` `personal` always targets the caller's OWN slot — you cannot write another user's personal data, even as an admin. Object zones deep-merge (owner/personal merge field-by-field), so you can change one field without wiping the rest. Business-only zones (`data`/`admin`/`private`) are not writable here — use the admin `update` for those.
10361
+
10304
10362
  **claim**(collectionId: string,
10305
10363
  productId: string,
10306
10364
  proofId: string,
package/docs/lots.md CHANGED
@@ -18,9 +18,22 @@ truth for its shared data.
18
18
  - `{ mode: 'products', productIds: [...] }` — an explicit list.
19
19
  - **`productIds` / `productCount`** — the materialised snapshot of resolved members (re-resolved on create, on selector change, and on demand via `resolve`).
20
20
  - **`payload`** — shared lot data (dates, supplier ref, custom fields). Lives only on the lot.
21
- - **`status`** — `open` → `closed` → `recalled` → `archived`. Archiving never deletes anything.
21
+ - **`status`** — `open` → `closed` → `recalled` → `archived`. A live lifecycle state, distinct from deletion.
22
22
  - **`destination`** — optional lot-level redirect; wins over the product's on a lot-scoped scan.
23
23
 
24
+ ### Archive vs delete
25
+
26
+ Two separate ideas — mirroring the platform's `deletedAt` convention:
27
+
28
+ | | `archive` (`status: 'archived'`) | `remove` (soft-delete, `deletedAt`) |
29
+ |---|---|---|
30
+ | Record | stays **live** & visible | **hidden** from reads unless `includeDeleted: true` |
31
+ | `lotNumber` | **stays reserved** | **freed** for reuse by a new lot |
32
+ | AI(10) scan | not resolved (excluded) | not resolved |
33
+ | Reversible | change status back | `restore` (409 if the number was taken by a live lot) |
34
+
35
+ Nothing is ever hard-deleted (joins/history stay intact). Use **archive** for "this run is done, keep it around"; use **remove** for "this was a mistake, and I want the number back."
36
+
24
37
  ---
25
38
 
26
39
  ## SDK — `SL.lots.*`
@@ -50,7 +63,11 @@ const containing = await lots.list(collectionId, { productId: 'prd_abc' }) // r
50
63
  const updated = await lots.update(collectionId, lot.id, { status: 'closed' })
51
64
  const { diff } = await lots.resolve(collectionId, lot.id) // { added, removed }
52
65
  const members = await lots.listProducts(collectionId, lot.id, { page: 1, limit: 50 })
53
- await lots.archive(collectionId, lot.id)
66
+
67
+ await lots.archive(collectionId, lot.id) // live, keeps its number
68
+ await lots.remove(collectionId, lot.id) // soft-delete, frees the number
69
+ const withDeleted = await lots.list(collectionId, { includeDeleted: true })
70
+ const restored = await lots.restore(collectionId, lot.id) // undo a soft-delete
54
71
 
55
72
  // Cross-app reads
56
73
  const publicLots = await lots.publicList(collectionId)
@@ -37,7 +37,8 @@ proof.admin = { … } // BUSINESS-ONLY → read: business, write: b
37
37
  proof.values = { // consumer bag — owner + business-writable
38
38
  <anyKey>: …, // PUBLIC → read: everyone,
39
39
  // write: business + current owner
40
- owner: { … }, // OWNER-SCOPED → read: business + current owner,
40
+ owner: { … }, // OWNER-SCOPED → read: business + current owner
41
+ // (also everyone while `claimable`, see below),
41
42
  // write: business + current owner,
42
43
  // transfers with ownership
43
44
  personal: { // PER-USER → read/write: only the specific user,
@@ -75,10 +76,12 @@ The field-config editor and SDK write helpers MUST reject attempts to create top
75
76
  | `proof.data.*` | ✅ | ✅ | ✅ | ✅ |
76
77
  | `proof.admin.*` | ❌ | ❌ | ❌ | ✅ |
77
78
  | `proof.values.<publicKey>` | ✅ | ✅ | ✅ | ✅ |
78
- | `proof.values.owner.*` | | | ✅ | ✅ |
79
+ | `proof.values.owner.*` | ❌ † | | ✅ | ✅ |
79
80
  | `proof.values.personal[me].*` | ❌ | ✅ (own slot only) | ✅ (own slot) | ❌ (see note) |
80
81
  | `proof.values.personal[other].*` | ❌ | ❌ | ❌ | ❌ (see note) |
81
82
 
83
+ > **† Claimable exception (`proof.values.owner.*`):** while a proof is **claimable** (`proof.claimable === true` *or* `proof.values.claimable === true`), its `owner` bag is returned to **everyone** — public/anonymous and authenticated non-owners included — so a prospective claimer can see pre-set owner data before claiming. Once the proof is claimed (`claimable` flips off) it reverts to owner-only (unless re-marked claimable). This is **read-only** exposure — write authority is unchanged (still owner + business). Implication: don't put anything in `values.owner` on a claimable proof that shouldn't be visible before it's claimed.
84
+ >
82
85
  > **Note on `personal`:** the default rule is that `personal` slots are readable *only* by the user whose `userId` matches the slot key — not even business admins. If the platform ever needs an admin-visible variant, it should be a separate mechanism, not a relaxation of this rule.
83
86
 
84
87
  ## Who writes what (authority matrix)
package/openapi.yaml CHANGED
@@ -4996,6 +4996,38 @@ paths:
4996
4996
  description: Unauthorized
4997
4997
  404:
4998
4998
  description: Not found
4999
+ /admin/collection/{collectionId}/lots/{lotId}/restore:
5000
+ post:
5001
+ tags:
5002
+ - lots
5003
+ summary: Restore a soft-deleted lot.
5004
+ operationId: lots_restore
5005
+ security:
5006
+ - bearerAuth: []
5007
+ parameters:
5008
+ - name: collectionId
5009
+ in: path
5010
+ required: true
5011
+ schema:
5012
+ type: string
5013
+ - name: lotId
5014
+ in: path
5015
+ required: true
5016
+ schema:
5017
+ type: string
5018
+ responses:
5019
+ 200:
5020
+ description: Success
5021
+ content:
5022
+ application/json:
5023
+ schema:
5024
+ $ref: "#/components/schemas/Lot"
5025
+ 400:
5026
+ description: Bad request
5027
+ 401:
5028
+ description: Unauthorized
5029
+ 404:
5030
+ description: Not found
4999
5031
  /admin/collection/{collectionId}/lots{params}:
5000
5032
  get:
5001
5033
  tags:
@@ -12611,6 +12643,48 @@ paths:
12611
12643
  description: Unauthorized
12612
12644
  404:
12613
12645
  description: Not found
12646
+ /public/collection/{collectionId}/product/{productId}/proof/{proofId}/values:
12647
+ put:
12648
+ tags:
12649
+ - proof
12650
+ summary: proof.updateValues
12651
+ operationId: proof_updateValues
12652
+ security: []
12653
+ parameters:
12654
+ - name: collectionId
12655
+ in: path
12656
+ required: true
12657
+ schema:
12658
+ type: string
12659
+ - name: productId
12660
+ in: path
12661
+ required: true
12662
+ schema:
12663
+ type: string
12664
+ - name: proofId
12665
+ in: path
12666
+ required: true
12667
+ schema:
12668
+ type: string
12669
+ responses:
12670
+ 200:
12671
+ description: Success
12672
+ content:
12673
+ application/json:
12674
+ schema:
12675
+ $ref: "#/components/schemas/ProofResponse"
12676
+ 400:
12677
+ description: Bad request
12678
+ 401:
12679
+ description: Unauthorized
12680
+ 404:
12681
+ description: Not found
12682
+ requestBody:
12683
+ required: true
12684
+ content:
12685
+ application/json:
12686
+ schema:
12687
+ $ref: "#/components/schemas/ProofValuesUpdateRequest"
12614
12688
  /public/collection/{collectionId}/products/{productId}/createClaim:
12615
12689
  post:
12616
12690
  tags:
@@ -19810,12 +19884,22 @@ components:
19810
19884
  name:
19811
19885
  type: string
19812
19886
  expiryDate:
19813
- type: object
19814
- additionalProperties: true
19887
+ type: string
19815
19888
  productId:
19816
19889
  type: string
19817
19890
  collectionId:
19818
19891
  type: string
19892
+ createdAt:
19893
+ type: string
19894
+ updatedAt:
19895
+ type: string
19896
+ deleted:
19897
+ type: boolean
19898
+ deletedAt:
19899
+ type: string
19900
+ admin:
19901
+ type: object
19902
+ additionalProperties: true
19819
19903
  required:
19820
19904
  - id
19821
19905
  BatchCreateRequest:
@@ -19828,8 +19912,6 @@ components:
19828
19912
  expiryDate:
19829
19913
  type: object
19830
19914
  additionalProperties: true
19831
- required:
19832
- - id
19833
19915
  BatchUpdateRequest:
19834
19916
  type: object
19835
19917
  properties:
@@ -23737,6 +23819,8 @@ components:
23737
23819
  type: string
23738
23820
  updatedAt:
23739
23821
  type: string
23822
+ deletedAt:
23823
+ type: string
23740
23824
  required:
23741
23825
  - id
23742
23826
  - collectionId
@@ -23777,6 +23861,8 @@ components:
23777
23861
  type: string
23778
23862
  productId:
23779
23863
  type: string
23864
+ includeDeleted:
23865
+ type: boolean
23780
23866
  ListLotsResponse:
23781
23867
  type: object
23782
23868
  properties:
@@ -25620,6 +25706,17 @@ components:
25620
25706
  type: object
25621
25707
  additionalProperties:
25622
25708
  $ref: "#/components/schemas/JsonValue"
25709
+ ProofValuesUpdateRequest:
25710
+ type: object
25711
+ properties:
25712
+ owner:
25713
+ type: object
25714
+ additionalProperties:
25715
+ $ref: "#/components/schemas/JsonValue"
25716
+ personal:
25717
+ type: object
25718
+ additionalProperties:
25719
+ $ref: "#/components/schemas/JsonValue"
25623
25720
  ProofFieldsConfig:
25624
25721
  type: object
25625
25722
  properties:
@@ -26654,6 +26751,42 @@ components:
26654
26751
  metadata:
26655
26752
  type: object
26656
26753
  additionalProperties: true
26754
+ VariantResponse:
26755
+ type: object
26756
+ properties:
26757
+ id:
26758
+ type: string
26759
+ name:
26760
+ type: string
26761
+ productId:
26762
+ type: string
26763
+ collectionId:
26764
+ type: string
26765
+ createdAt:
26766
+ type: string
26767
+ updatedAt:
26768
+ type: string
26769
+ deleted:
26770
+ type: boolean
26771
+ deletedAt:
26772
+ type: string
26773
+ admin:
26774
+ type: object
26775
+ additionalProperties: true
26776
+ required:
26777
+ - id
26778
+ VariantCreateRequest:
26779
+ type: object
26780
+ properties:
26781
+ id:
26782
+ type: string
26783
+ name:
26784
+ type: string
26785
+ VariantUpdateRequest:
26786
+ type: object
26787
+ properties:
26788
+ name:
26789
+ type: string
26657
26790
  NavigationRequest:
26658
26791
  type: object
26659
26792
  properties:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@proveanything/smartlinks",
3
- "version": "1.16.0",
3
+ "version": "1.16.2",
4
4
  "description": "Official JavaScript/TypeScript SDK for the Smartlinks API",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",