@proveanything/smartlinks 1.15.18 → 1.15.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -41,10 +41,10 @@ If you're new to the SDK, this is the easiest path:
41
41
  - **Fetch collections/products** → see [Quick start](README.md#quick-start)
42
42
  - **Authenticate admins or end users** → see [Authentication](README.md#authentication)
43
43
  - **Upload and manage files** → see [Assets](README.md#assets)
44
- - **Browse the full surface area** → use [API_SUMMARY.md](API_SUMMARY.md) as reference
44
+ - **Browse the full surface area** → use [API_SUMMARY.md](docs/API_SUMMARY.md) as reference
45
45
 
46
46
  For the full list of functions and types, see the API summary:
47
- → [API Summary](API_SUMMARY.md)
47
+ → [API Summary](docs/API_SUMMARY.md)
48
48
 
49
49
  **Documentation:**
50
50
  - [AI & Chat Completions](docs/ai.md) - Chat completions, RAG, voice integration
@@ -1,4 +1,4 @@
1
- import type { AuthLoginResponse, AppleLoginOptions, RefreshResponse, LogoutResponse, PhoneSendCodeResponse, PhoneVerifyResponse, PasswordResetRequestResponse, VerifyResetTokenResponse, PasswordResetCompleteResponse, EmailVerificationActionResponse, EmailVerifyTokenResponse, AuthKitConfig, MagicLinkSendResponse, MagicLinkVerifyResponse, UserProfile, UpdateProfileResponse, ProfileUpdateData, SuccessResponse, SendWhatsAppRequest, SendWhatsAppResponse, ExchangeWhatsAppSessionResponse, VerifyWhatsAppResponse, WhatsAppStatusResponse, SendSmsVerifyRequest, SendSmsVerifyResponse, VerifySmsResponse, UpsertContactRequest, UpsertContactResponse, MfaChallengeSendResponse, MfaFinalizeResponse, MfaEnrollSendResponse, MfaEnrolledResponse, MfaFactorsResponse, TrustedDevice } from "../types/authKit";
1
+ import type { AuthLoginResponse, AppleLoginOptions, RefreshResponse, LogoutResponse, PhoneSendCodeResponse, PhoneVerifyResponse, PasswordResetRequestResponse, VerifyResetTokenResponse, PasswordResetCompleteResponse, EmailVerificationActionResponse, EmailVerifyTokenResponse, AuthKitConfig, AuthKitConfigInput, MagicLinkSendResponse, MagicLinkVerifyResponse, UserProfile, UpdateProfileResponse, ProfileUpdateData, SuccessResponse, SendWhatsAppRequest, SendWhatsAppResponse, ExchangeWhatsAppSessionResponse, VerifyWhatsAppResponse, WhatsAppStatusResponse, SendSmsVerifyRequest, SendSmsVerifyResponse, VerifySmsResponse, UpsertContactRequest, UpsertContactResponse, MfaChallengeSendResponse, MfaFinalizeResponse, MfaEnrollSendResponse, MfaEnrolledResponse, MfaFactorsResponse, TrustedDevice } from "../types/authKit";
2
2
  /**
3
3
  * Namespace containing helper functions for the new AuthKit API.
4
4
  * Legacy collection-based authKit helpers retained (marked as *Legacy*).
@@ -18,6 +18,11 @@ export declare namespace authKit {
18
18
  * `trustDevice: true`), pass it here to skip the challenge entirely as long as it's
19
19
  * still valid. If it's revoked/expired, the server silently falls back to requiring a
20
20
  * fresh challenge — `login()` just returns `MFA_REQUIRED` again, no special handling.
21
+ *
22
+ * Security errors (thrown as `SmartlinksApiError`, see {@link LoginSecurityErrorCode}):
23
+ * - `ACCOUNT_TEMPORARILY_LOCKED` (429) — `err.details.retryAfterSeconds` says how long to wait.
24
+ * - `PASSWORD_EXPIRED` (403) — `err.details.resetToken` is short-lived; route into
25
+ * {@link completePasswordReset} to change the password in place.
21
26
  */
22
27
  function login(clientId: string, email: string, password: string, trustedDeviceToken?: string): Promise<AuthLoginResponse>;
23
28
  /**
@@ -25,6 +30,11 @@ export declare namespace authKit {
25
30
  *
26
31
  * Not gated by step-up MFA — a brand-new user has no enrolled factors yet, so there's
27
32
  * nothing to challenge against.
33
+ *
34
+ * The new password is validated against the collection's `passwordPolicy` — may throw
35
+ * a {@link PasswordPolicyErrorCode} (400). The same validation applies to
36
+ * {@link completePasswordReset} and {@link changePassword}. Read the policy for a live
37
+ * checklist from `authKit.load(clientId)` → `config.security.passwordPolicy`.
28
38
  */
29
39
  function register(clientId: string, data: {
30
40
  email: string;
@@ -221,10 +231,24 @@ export declare namespace authKit {
221
231
  }>;
222
232
  /** Revoke a single trusted device by id (authenticated). */
223
233
  function revokeTrustedDevice(clientId: string, id: string): Promise<SuccessResponse>;
234
+ /**
235
+ * Load the **public** AuthKit config for a client (no auth). Returns branding +
236
+ * the public security subset (`security.passwordPolicy` + `security.session`);
237
+ * `security.lockout` is admin-only and never included here. Use this in the login
238
+ * UI to render password checklists and drive idle sign-out.
239
+ */
224
240
  function load(authKitId: string): Promise<AuthKitConfig>;
241
+ /** Get the full AuthKit config, including admin-only fields like `security.lockout` (admin auth). */
225
242
  function get(collectionId: string, authKitId: string): Promise<AuthKitConfig>;
226
243
  function list(collectionId: string, admin?: boolean): Promise<AuthKitConfig[]>;
227
- function create(collectionId: string, data: any): Promise<AuthKitConfig>;
228
- function update(collectionId: string, authKitId: string, data: any): Promise<AuthKitConfig>;
244
+ /** Create an AuthKit client config (admin). Accepts the account `security` policy — see {@link AuthKitConfigInput}. */
245
+ function create(collectionId: string, data: AuthKitConfigInput): Promise<AuthKitConfig>;
246
+ /**
247
+ * Update an AuthKit client config (admin). This is how the account **security
248
+ * policy** is written — pass a `security` block ({@link AuthKitSecurityConfig}).
249
+ * The server validates it and enforces it; the login UI reads the public subset
250
+ * back via {@link load}.
251
+ */
252
+ function update(collectionId: string, authKitId: string, data: AuthKitConfigInput): Promise<AuthKitConfig>;
229
253
  function remove(collectionId: string, authKitId: string): Promise<void>;
230
254
  }
@@ -22,6 +22,11 @@ export var authKit;
22
22
  * `trustDevice: true`), pass it here to skip the challenge entirely as long as it's
23
23
  * still valid. If it's revoked/expired, the server silently falls back to requiring a
24
24
  * fresh challenge — `login()` just returns `MFA_REQUIRED` again, no special handling.
25
+ *
26
+ * Security errors (thrown as `SmartlinksApiError`, see {@link LoginSecurityErrorCode}):
27
+ * - `ACCOUNT_TEMPORARILY_LOCKED` (429) — `err.details.retryAfterSeconds` says how long to wait.
28
+ * - `PASSWORD_EXPIRED` (403) — `err.details.resetToken` is short-lived; route into
29
+ * {@link completePasswordReset} to change the password in place.
25
30
  */
26
31
  async function login(clientId, email, password, trustedDeviceToken) {
27
32
  const body = { email, password };
@@ -40,6 +45,11 @@ export var authKit;
40
45
  *
41
46
  * Not gated by step-up MFA — a brand-new user has no enrolled factors yet, so there's
42
47
  * nothing to challenge against.
48
+ *
49
+ * The new password is validated against the collection's `passwordPolicy` — may throw
50
+ * a {@link PasswordPolicyErrorCode} (400). The same validation applies to
51
+ * {@link completePasswordReset} and {@link changePassword}. Read the policy for a live
52
+ * checklist from `authKit.load(clientId)` → `config.security.passwordPolicy`.
43
53
  */
44
54
  async function register(clientId, data) {
45
55
  return post(`/authkit/${encodeURIComponent(clientId)}/auth/register`, data);
@@ -434,11 +444,18 @@ export var authKit;
434
444
  /* ===================================
435
445
  * Collection-based AuthKit
436
446
  * =================================== */
447
+ /**
448
+ * Load the **public** AuthKit config for a client (no auth). Returns branding +
449
+ * the public security subset (`security.passwordPolicy` + `security.session`);
450
+ * `security.lockout` is admin-only and never included here. Use this in the login
451
+ * UI to render password checklists and drive idle sign-out.
452
+ */
437
453
  async function load(authKitId) {
438
454
  const path = `/authKit/${encodeURIComponent(authKitId)}/config`;
439
455
  return request(path);
440
456
  }
441
457
  authKit.load = load;
458
+ /** Get the full AuthKit config, including admin-only fields like `security.lockout` (admin auth). */
442
459
  async function get(collectionId, authKitId) {
443
460
  const path = `/admin/collection/${encodeURIComponent(collectionId)}/authKit/${encodeURIComponent(authKitId)}`;
444
461
  return request(path);
@@ -450,11 +467,18 @@ export var authKit;
450
467
  return request(path);
451
468
  }
452
469
  authKit.list = list;
470
+ /** Create an AuthKit client config (admin). Accepts the account `security` policy — see {@link AuthKitConfigInput}. */
453
471
  async function create(collectionId, data) {
454
472
  const path = `/admin/collection/${encodeURIComponent(collectionId)}/authKit`;
455
473
  return post(path, data);
456
474
  }
457
475
  authKit.create = create;
476
+ /**
477
+ * Update an AuthKit client config (admin). This is how the account **security
478
+ * policy** is written — pass a `security` block ({@link AuthKitSecurityConfig}).
479
+ * The server validates it and enforces it; the login UI reads the public subset
480
+ * back via {@link load}.
481
+ */
458
482
  async function update(collectionId, authKitId, data) {
459
483
  const path = `/admin/collection/${encodeURIComponent(collectionId)}/authKit/${encodeURIComponent(authKitId)}`;
460
484
  return put(path, data);
@@ -1,4 +1,4 @@
1
- import { ProofResponse, ProofCreateRequest, ProofUpdateRequest, ProofClaimRequest, ProofGrant, CreateGrantOptions, RedeemGrantOptions, RedeemGrantResult } from "../types/proof";
1
+ import { ProofResponse, ProofCreateRequest, ProofUpdateRequest, 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.
@@ -12,11 +12,34 @@ export declare namespace proof {
12
12
  /**
13
13
  * Create a proof for a product (admin only).
14
14
  * POST /admin/collection/:collectionId/product/:productId/proof
15
+ *
16
+ * Pass the proof's content in a `proof` block, keyed by zone (see {@link ProofWrite}):
17
+ * ```ts
18
+ * proof.create(collectionId, productId, {
19
+ * proof: {
20
+ * values: { colour: 'red' }, // public + owner readable, owner + admin writable
21
+ * data: { serialNo: 1001 }, // public + owner readable, ADMIN-only writable
22
+ * admin: { costPrice: 4.20 }, // admin-only
23
+ * },
24
+ * claimable: true,
25
+ * })
26
+ * ```
27
+ * Note: a top-level `data`/`admin` on the request body is legacy — top-level
28
+ * `data` gets folded into the values bag, so use `proof.data` for `proof.data`.
15
29
  */
16
- function create(collectionId: string, productId: string, values: ProofCreateRequest): Promise<ProofResponse>;
30
+ function create(collectionId: string, productId: string, request: ProofCreateRequest): Promise<ProofResponse>;
17
31
  /**
18
32
  * Update a proof for a product (admin only).
19
33
  * PUT /admin/collection/:collectionId/product/:productId/proof/:proofId
34
+ *
35
+ * Pass the fields to change **at the root**, keyed by zone (see {@link ProofWrite}):
36
+ * ```ts
37
+ * proof.update(collectionId, productId, proofId, {
38
+ * data: { serialNo: 1002 }, // → proof.data (admin-only writable)
39
+ * values: { colour: 'blue' }, // → proof.values
40
+ * })
41
+ * ```
42
+ * Object zones deep-merge, so you can change one field without wiping the rest.
20
43
  */
21
44
  function update(collectionId: string, productId: string, proofId: string, values: ProofUpdateRequest): Promise<ProofResponse>;
22
45
  /**
@@ -122,4 +145,37 @@ export declare namespace proof {
122
145
  * {@link setGrantToken} so subsequent data requests carry the token.
123
146
  */
124
147
  function redeemGrant(collectionId: string, productId: string, proofId: string, token: string, options?: RedeemGrantOptions): Promise<RedeemGrantResult>;
148
+ /**
149
+ * Start a push transfer of a proof (current owner / collection admin only).
150
+ *
151
+ * Directed — hand it to a named recipient who then calls {@link acceptTransfer}:
152
+ * ```ts
153
+ * await proof.transfer(collectionId, productId, proofId, { toEmail: 'buyer@example.com' })
154
+ * ```
155
+ * Open release — make the proof claimable by anyone:
156
+ * ```ts
157
+ * await proof.transfer(collectionId, productId, proofId, { release: true })
158
+ * ```
159
+ */
160
+ function transfer(collectionId: string, productId: string, proofId: string, options: TransferProofOptions): Promise<TransferProofResult>;
161
+ /**
162
+ * Accept a directed transfer (the named recipient only). Completes the ownership
163
+ * move — the proof's `userId` becomes the caller and the previous owner's private
164
+ * data and share grants are cleared/voided.
165
+ */
166
+ function acceptTransfer(collectionId: string, productId: string, proofId: string): Promise<{
167
+ ok: boolean;
168
+ proof: ProofResponse;
169
+ }>;
170
+ /** Cancel a pending push transfer (current owner / collection admin only). */
171
+ function cancelTransfer(collectionId: string, productId: string, proofId: string): Promise<{
172
+ ok: boolean;
173
+ }>;
174
+ /**
175
+ * Get the active transfer/status for a proof (owner, collection admin, or the
176
+ * named recipient). Returns `{ transfer: null }` when nothing is in flight.
177
+ */
178
+ function getTransfer(collectionId: string, productId: string, proofId: string): Promise<{
179
+ transfer: ProofTransfer | null;
180
+ }>;
125
181
  }
package/dist/api/proof.js CHANGED
@@ -26,15 +26,38 @@ export var proof;
26
26
  /**
27
27
  * Create a proof for a product (admin only).
28
28
  * POST /admin/collection/:collectionId/product/:productId/proof
29
+ *
30
+ * Pass the proof's content in a `proof` block, keyed by zone (see {@link ProofWrite}):
31
+ * ```ts
32
+ * proof.create(collectionId, productId, {
33
+ * proof: {
34
+ * values: { colour: 'red' }, // public + owner readable, owner + admin writable
35
+ * data: { serialNo: 1001 }, // public + owner readable, ADMIN-only writable
36
+ * admin: { costPrice: 4.20 }, // admin-only
37
+ * },
38
+ * claimable: true,
39
+ * })
40
+ * ```
41
+ * Note: a top-level `data`/`admin` on the request body is legacy — top-level
42
+ * `data` gets folded into the values bag, so use `proof.data` for `proof.data`.
29
43
  */
30
- async function create(collectionId, productId, values) {
44
+ async function create(collectionId, productId, request) {
31
45
  const path = `/admin/collection/${encodeURIComponent(collectionId)}/product/${encodeURIComponent(productId)}/proof`;
32
- return post(path, values);
46
+ return post(path, request);
33
47
  }
34
48
  proof.create = create;
35
49
  /**
36
50
  * Update a proof for a product (admin only).
37
51
  * PUT /admin/collection/:collectionId/product/:productId/proof/:proofId
52
+ *
53
+ * Pass the fields to change **at the root**, keyed by zone (see {@link ProofWrite}):
54
+ * ```ts
55
+ * proof.update(collectionId, productId, proofId, {
56
+ * data: { serialNo: 1002 }, // → proof.data (admin-only writable)
57
+ * values: { colour: 'blue' }, // → proof.values
58
+ * })
59
+ * ```
60
+ * Object zones deep-merge, so you can change one field without wiping the rest.
38
61
  */
39
62
  async function update(collectionId, productId, proofId, values) {
40
63
  const path = `/admin/collection/${encodeURIComponent(collectionId)}/product/${encodeURIComponent(productId)}/proof/${encodeURIComponent(proofId)}`;
@@ -200,4 +223,53 @@ export var proof;
200
223
  return post(`${grantBase(collectionId, productId, proofId)}/redeem`, body);
201
224
  }
202
225
  proof.redeemGrant = redeemGrant;
226
+ // ---------------------------------------------------------------------------
227
+ // Ownership transfer — moving a proof's single owner from A to B
228
+ //
229
+ // A proof always has exactly one owner (`proof.userId`). A transfer moves that
230
+ // owner with the current owner's consent: either DIRECTED to a named recipient
231
+ // (who accepts) or an OPEN RELEASE (the proof becomes claimable by anyone).
232
+ // Contested pull-claims + dispute resolution are a later addition.
233
+ // ---------------------------------------------------------------------------
234
+ function transferBase(collectionId, productId, proofId) {
235
+ return `/public/collection/${encodeURIComponent(collectionId)}/product/${encodeURIComponent(productId)}/proof/${encodeURIComponent(proofId)}/transfer`;
236
+ }
237
+ /**
238
+ * Start a push transfer of a proof (current owner / collection admin only).
239
+ *
240
+ * Directed — hand it to a named recipient who then calls {@link acceptTransfer}:
241
+ * ```ts
242
+ * await proof.transfer(collectionId, productId, proofId, { toEmail: 'buyer@example.com' })
243
+ * ```
244
+ * Open release — make the proof claimable by anyone:
245
+ * ```ts
246
+ * await proof.transfer(collectionId, productId, proofId, { release: true })
247
+ * ```
248
+ */
249
+ async function transfer(collectionId, productId, proofId, options) {
250
+ return post(transferBase(collectionId, productId, proofId), Object.assign({}, options));
251
+ }
252
+ proof.transfer = transfer;
253
+ /**
254
+ * Accept a directed transfer (the named recipient only). Completes the ownership
255
+ * move — the proof's `userId` becomes the caller and the previous owner's private
256
+ * data and share grants are cleared/voided.
257
+ */
258
+ async function acceptTransfer(collectionId, productId, proofId) {
259
+ return post(`${transferBase(collectionId, productId, proofId)}/accept`, {});
260
+ }
261
+ proof.acceptTransfer = acceptTransfer;
262
+ /** Cancel a pending push transfer (current owner / collection admin only). */
263
+ async function cancelTransfer(collectionId, productId, proofId) {
264
+ return post(`${transferBase(collectionId, productId, proofId)}/cancel`, {});
265
+ }
266
+ proof.cancelTransfer = cancelTransfer;
267
+ /**
268
+ * Get the active transfer/status for a proof (owner, collection admin, or the
269
+ * named recipient). Returns `{ transfer: null }` when nothing is in flight.
270
+ */
271
+ async function getTransfer(collectionId, productId, proofId) {
272
+ return request(transferBase(collectionId, productId, proofId));
273
+ }
274
+ proof.getTransfer = getTransfer;
203
275
  })(proof || (proof = {}));
@@ -1,6 +1,6 @@
1
1
  # Smartlinks API Summary
2
2
 
3
- Version: 1.15.18 | Generated: 2026-08-19T06:40:47.505Z
3
+ Version: 1.15.20 | Generated: 2026-08-25T07:03:45.544Z
4
4
 
5
5
  This is a concise summary of all available API functions and types.
6
6
 
@@ -3464,6 +3464,55 @@ interface AuthKitConfig {
3464
3464
  supportEmail?: string
3465
3465
  redirectUrl?: string
3466
3466
  updatedAt?: string
3467
+ * Per-collection security policy. On the public config endpoint only
3468
+ * `passwordPolicy` + `session` are returned (the client renders password
3469
+ * checklists / idle sign-out from them); `lockout` is admin-only and enforced
3470
+ * server-side. See {@link AuthKitSecurityConfig}.
3471
+ security?: AuthKitSecurityConfig
3472
+ }
3473
+ ```
3474
+
3475
+ **AuthKitSecurityConfig** (interface)
3476
+ ```typescript
3477
+ interface AuthKitSecurityConfig {
3478
+ passwordPolicy?: AuthKitPasswordPolicy
3479
+ session?: AuthKitSessionPolicy
3480
+ lockout?: AuthKitLockoutPolicy
3481
+ }
3482
+ ```
3483
+
3484
+ **AuthKitPasswordPolicy** (interface)
3485
+ ```typescript
3486
+ interface AuthKitPasswordPolicy {
3487
+ minLength?: number
3488
+ requireUppercase?: boolean
3489
+ requireLowercase?: boolean
3490
+ requireNumber?: boolean
3491
+ requireSymbol?: boolean
3492
+ blockCommonPasswords?: boolean
3493
+ expiryDays?: number
3494
+ historyCount?: number
3495
+ }
3496
+ ```
3497
+
3498
+ **AuthKitSessionPolicy** (interface)
3499
+ ```typescript
3500
+ interface AuthKitSessionPolicy {
3501
+ inactivityTimeoutMinutes?: number
3502
+ inactivityWarningSeconds?: number
3503
+ absoluteTimeoutHours?: number
3504
+ rememberMe?: boolean
3505
+ }
3506
+ ```
3507
+
3508
+ **AuthKitLockoutPolicy** (interface)
3509
+ ```typescript
3510
+ interface AuthKitLockoutPolicy {
3511
+ enabled?: boolean
3512
+ maxFailedAttempts?: number
3513
+ attemptWindowMinutes?: number
3514
+ lockoutMinutes?: number
3515
+ notifyUserOnLockout?: boolean
3467
3516
  }
3468
3517
  ```
3469
3518
 
@@ -3475,6 +3524,12 @@ interface AuthKitConfig {
3475
3524
 
3476
3525
  **VerifyStatus** = `'pending' | 'verified' | 'failed' | 'expired' | 'unknown'`
3477
3526
 
3527
+ **AuthKitConfigInput** = ``
3528
+
3529
+ **PasswordPolicyErrorCode** = ``
3530
+
3531
+ **LoginSecurityErrorCode** = ``
3532
+
3478
3533
  ### batch
3479
3534
 
3480
3535
  **FirebaseTimestamp** (interface)
@@ -7279,14 +7334,36 @@ interface Proof {
7279
7334
  }
7280
7335
  ```
7281
7336
 
7282
- **ProofCreateRequest** (interface)
7337
+ **ProofWrite** (interface)
7283
7338
  ```typescript
7284
- interface ProofCreateRequest {
7285
- values: ProofValues
7339
+ interface ProofWrite {
7340
+ * Choose the proof's ID (serial, NFC id, etc.). Honoured **on create only** —
7341
+ * the ledger doc becomes `{productId}-{id}`. Omit to auto-generate. Ignored on
7342
+ * update (a proof's ID is immutable).
7343
+ id?: string
7344
+ values?: ProofValues
7286
7345
  data?: Record<string, JsonValue>
7287
7346
  admin?: Record<string, JsonValue>
7347
+ owner?: Record<string, JsonValue>
7348
+ claimable?: boolean
7349
+ [key: string]: JsonValue | Record<string, JsonValue> | ProofValues | undefined
7350
+ }
7351
+ ```
7352
+
7353
+ **ProofCreateRequest** (interface)
7354
+ ```typescript
7355
+ interface ProofCreateRequest {
7356
+ * The proof to create, by zone (mirrors the proof document). This is the clear,
7357
+ * recommended shape — `create(collectionId, productId, { proof: {...} })`.
7358
+ proof?: ProofWrite
7359
+ values?: ProofValues
7288
7360
  claimable?: boolean
7289
7361
  virtual?: boolean
7362
+ core?: ProofWrite
7363
+ * @deprecated On the request body this is folded into the **values bag**
7364
+ * (public + owner-writable) — NOT `proof.data`. Use `proof.data`.
7365
+ data?: Record<string, JsonValue>
7366
+ admin?: Record<string, JsonValue>
7290
7367
  }
7291
7368
  ```
7292
7369
 
@@ -7341,9 +7418,51 @@ interface RedeemGrantOptions {
7341
7418
  }
7342
7419
  ```
7343
7420
 
7421
+ **ProofTransfer** (interface)
7422
+ ```typescript
7423
+ interface ProofTransfer {
7424
+ id: string
7425
+ proofId: string
7426
+ productId?: string | null
7427
+ type: ProofTransferType
7428
+ state: ProofTransferState
7429
+ fromUserId?: string | null
7430
+ toUserId?: string | null
7431
+ toEmail?: string | null
7432
+ initiatedByUserId?: string | null
7433
+ initiatedByRole?: 'owner' | 'claimant' | 'admin' | null
7434
+ disputeReason?: string | null
7435
+ disputeDeadline?: string | null
7436
+ completedAt?: string | null
7437
+ createdAt: string
7438
+ updatedAt: string
7439
+ }
7440
+ ```
7441
+
7442
+ **TransferProofOptions** (interface)
7443
+ ```typescript
7444
+ interface TransferProofOptions {
7445
+ toEmail?: string
7446
+ toUserId?: string
7447
+ toName?: string
7448
+ release?: boolean
7449
+ message?: string
7450
+ notify?: boolean
7451
+ }
7452
+ ```
7453
+
7454
+ **TransferProofResult** (interface)
7455
+ ```typescript
7456
+ interface TransferProofResult {
7457
+ ok: boolean
7458
+ mode: 'directed' | 'open_release'
7459
+ transfer: ProofTransfer
7460
+ }
7461
+ ```
7462
+
7344
7463
  **ProofResponse** = `Proof`
7345
7464
 
7346
- **ProofUpdateRequest** = `Partial<ProofCreateRequest>`
7465
+ **ProofUpdateRequest** = `Partial<ProofWrite> & { proof?: ProofWrite }`
7347
7466
 
7348
7467
  **ProofClaimRequest** = `Record<string, any>`
7349
7468
 
@@ -7355,6 +7474,10 @@ interface RedeemGrantOptions {
7355
7474
 
7356
7475
  **RedeemGrantResult** = ``
7357
7476
 
7477
+ **ProofTransferType** = `'directed' | 'open_release' | 'contested'`
7478
+
7479
+ **ProofTransferState** = ``
7480
+
7358
7481
  ### qr
7359
7482
 
7360
7483
  **QrShortCodeLookupResponse** (interface)
@@ -8748,10 +8871,10 @@ Gets current account information for the logged in user. Returns user, owner, ac
8748
8871
  ### authKit
8749
8872
 
8750
8873
  **login**(clientId: string, email: string, password: string, trustedDeviceToken?: string) → `Promise<AuthLoginResponse>`
8751
- Login with email + password (public). When the client's MFA policy requires a step-up, the server returns **403 `MFA_REQUIRED`** instead of a session — `login()` throws a `SmartlinksApiError` with `err.errorResponse?.errorCode === 'MFA_REQUIRED'` and the challenge details in `err.details` (see {@link MfaRequiredDetails}). Route the caller to {@link mfaChallengeSend} on that error; this method's return type is unchanged. returned one (via {@link mfaChallengeVerify}/{@link mfaRecoveryCode} with `trustDevice: true`), pass it here to skip the challenge entirely as long as it's still valid. If it's revoked/expired, the server silently falls back to requiring a fresh challenge — `login()` just returns `MFA_REQUIRED` again, no special handling.
8874
+ Login with email + password (public). When the client's MFA policy requires a step-up, the server returns **403 `MFA_REQUIRED`** instead of a session — `login()` throws a `SmartlinksApiError` with `err.errorResponse?.errorCode === 'MFA_REQUIRED'` and the challenge details in `err.details` (see {@link MfaRequiredDetails}). Route the caller to {@link mfaChallengeSend} on that error; this method's return type is unchanged. returned one (via {@link mfaChallengeVerify}/{@link mfaRecoveryCode} with `trustDevice: true`), pass it here to skip the challenge entirely as long as it's still valid. If it's revoked/expired, the server silently falls back to requiring a fresh challenge — `login()` just returns `MFA_REQUIRED` again, no special handling. Security errors (thrown as `SmartlinksApiError`, see {@link LoginSecurityErrorCode}): - `ACCOUNT_TEMPORARILY_LOCKED` (429) — `err.details.retryAfterSeconds` says how long to wait. - `PASSWORD_EXPIRED` (403) — `err.details.resetToken` is short-lived; route into {@link completePasswordReset} to change the password in place.
8752
8875
 
8753
8876
  **register**(clientId: string, data: { email: string; password: string; displayName?: string; accountData?: Record<string, any> }) → `Promise<AuthLoginResponse>`
8754
- Register a new user (public). Not gated by step-up MFA — a brand-new user has no enrolled factors yet, so there's nothing to challenge against.
8877
+ Register a new user (public). Not gated by step-up MFA — a brand-new user has no enrolled factors yet, so there's nothing to challenge against. The new password is validated against the collection's `passwordPolicy` — may throw a {@link PasswordPolicyErrorCode} (400). The same validation applies to {@link completePasswordReset} and {@link changePassword}. Read the policy for a live checklist from `authKit.load(clientId)` → `config.security.passwordPolicy`.
8755
8878
 
8756
8879
  **googleLogin**(clientId: string, idToken: string, trustedDeviceToken?: string) → `Promise<AuthLoginResponse>`
8757
8880
  Google OAuth login via ID token (public). Gated by step-up MFA — see {@link login} for the `MFA_REQUIRED` error shape. {@link mfaChallengeVerify}/{@link mfaRecoveryCode} (with `trustDevice: true`) to skip the challenge on this device, same as {@link login}.
@@ -8877,22 +9000,22 @@ List devices trusted to skip MFA challenges for the current user (authenticated)
8877
9000
  Revoke a single trusted device by id (authenticated).
8878
9001
 
8879
9002
  **load**(authKitId: string) → `Promise<AuthKitConfig>`
8880
- Revoke a single trusted device by id (authenticated).
9003
+ Load the **public** AuthKit config for a client (no auth). Returns branding + the public security subset (`security.passwordPolicy` + `security.session`); `security.lockout` is admin-only and never included here. Use this in the login UI to render password checklists and drive idle sign-out.
8881
9004
 
8882
9005
  **get**(collectionId: string, authKitId: string) → `Promise<AuthKitConfig>`
8883
- Revoke a single trusted device by id (authenticated).
9006
+ Get the full AuthKit config, including admin-only fields like `security.lockout` (admin auth).
8884
9007
 
8885
9008
  **list**(collectionId: string, admin?: boolean) → `Promise<AuthKitConfig[]>`
8886
- Revoke a single trusted device by id (authenticated).
9009
+ Get the full AuthKit config, including admin-only fields like `security.lockout` (admin auth).
8887
9010
 
8888
- **create**(collectionId: string, data: any) → `Promise<AuthKitConfig>`
8889
- Revoke a single trusted device by id (authenticated).
9011
+ **create**(collectionId: string, data: AuthKitConfigInput) → `Promise<AuthKitConfig>`
9012
+ Create an AuthKit client config (admin). Accepts the account `security` policy — see {@link AuthKitConfigInput}.
8890
9013
 
8891
- **update**(collectionId: string, authKitId: string, data: any) → `Promise<AuthKitConfig>`
8892
- Revoke a single trusted device by id (authenticated).
9014
+ **update**(collectionId: string, authKitId: string, data: AuthKitConfigInput) → `Promise<AuthKitConfig>`
9015
+ Update an AuthKit client config (admin). This is how the account **security policy** is written — pass a `security` block ({@link AuthKitSecurityConfig}). The server validates it and enforces it; the login UI reads the public subset back via {@link load}.
8893
9016
 
8894
9017
  **remove**(collectionId: string, authKitId: string) → `Promise<void>`
8895
- Revoke a single trusted device by id (authenticated).
9018
+ Update an AuthKit client config (admin). This is how the account **security policy** is written — pass a `security` block ({@link AuthKitSecurityConfig}). The server validates it and enforces it; the login UI reads the public subset back via {@link load}.
8896
9019
 
8897
9020
  ### batch
8898
9021
 
@@ -9952,14 +10075,14 @@ List all Proofs for a Collection.
9952
10075
 
9953
10076
  **create**(collectionId: string,
9954
10077
  productId: string,
9955
- values: ProofCreateRequest) → `Promise<ProofResponse>`
9956
- Create a proof for a product (admin only). POST /admin/collection/:collectionId/product/:productId/proof
10078
+ request: ProofCreateRequest) → `Promise<ProofResponse>`
10079
+ Create a proof for a product (admin only). POST /admin/collection/:collectionId/product/:productId/proof Pass the proof's content in a `proof` block, keyed by zone (see {@link ProofWrite}): ```ts proof.create(collectionId, productId, { proof: { values: { colour: 'red' }, // public + owner readable, owner + admin writable data: { serialNo: 1001 }, // public + owner readable, ADMIN-only writable admin: { costPrice: 4.20 }, // admin-only }, claimable: true, }) ``` Note: a top-level `data`/`admin` on the request body is legacy — top-level `data` gets folded into the values bag, so use `proof.data` for `proof.data`.
9957
10080
 
9958
10081
  **update**(collectionId: string,
9959
10082
  productId: string,
9960
10083
  proofId: string,
9961
10084
  values: ProofUpdateRequest) → `Promise<ProofResponse>`
9962
- Update a proof for a product (admin only). PUT /admin/collection/:collectionId/product/:productId/proof/:proofId
10085
+ 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.
9963
10086
 
9964
10087
  **claim**(collectionId: string,
9965
10088
  productId: string,
@@ -10026,6 +10149,27 @@ Revoke a grant by id (owner / collection admin only). Takes effect immediately.
10026
10149
  options?: RedeemGrantOptions) → `Promise<RedeemGrantResult>`
10027
10150
  Redeem a grant token (anonymous or signed-in). Records the redemption and returns the granted scope, or — for a `verify_owner` grant — an ownership assertion (never the account). After redeeming, call {@link setGrantToken} so subsequent data requests carry the token.
10028
10151
 
10152
+ **transfer**(collectionId: string,
10153
+ productId: string,
10154
+ proofId: string,
10155
+ options: TransferProofOptions) → `Promise<TransferProofResult>`
10156
+ Start a push transfer of a proof (current owner / collection admin only). Directed — hand it to a named recipient who then calls {@link acceptTransfer}: ```ts await proof.transfer(collectionId, productId, proofId, { toEmail: 'buyer@example.com' }) ``` Open release — make the proof claimable by anyone: ```ts await proof.transfer(collectionId, productId, proofId, { release: true }) ```
10157
+
10158
+ **acceptTransfer**(collectionId: string,
10159
+ productId: string,
10160
+ proofId: string) → `Promise<`
10161
+ Accept a directed transfer (the named recipient only). Completes the ownership move — the proof's `userId` becomes the caller and the previous owner's private data and share grants are cleared/voided.
10162
+
10163
+ **cancelTransfer**(collectionId: string,
10164
+ productId: string,
10165
+ proofId: string) → `Promise<`
10166
+ Cancel a pending push transfer (current owner / collection admin only).
10167
+
10168
+ **getTransfer**(collectionId: string,
10169
+ productId: string,
10170
+ proofId: string) → `Promise<`
10171
+ Get the active transfer/status for a proof (owner, collection admin, or the named recipient). Returns `{ transfer: null }` when nothing is in flight.
10172
+
10029
10173
  ### publicClient
10030
10174
 
10031
10175
  **chat**(collectionId: string,