@01.software/sdk 0.56.1 → 0.65.0

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.
Files changed (63) hide show
  1. package/CHANGELOG.md +186 -0
  2. package/README.md +174 -91
  3. package/dist/client.cjs +136 -7
  4. package/dist/client.cjs.map +1 -1
  5. package/dist/client.d.cts +6 -6
  6. package/dist/client.d.ts +6 -6
  7. package/dist/client.js +136 -7
  8. package/dist/client.js.map +1 -1
  9. package/dist/{collection-client-DxuBDso_.d.cts → collection-client-B7rsTtVj.d.cts} +6 -6
  10. package/dist/{collection-client-Bmr53saa.d.ts → collection-client-C2sMvfme.d.ts} +6 -6
  11. package/dist/{const-Cv1DCsp6.d.cts → const-CRxPTDGt.d.ts} +2 -2
  12. package/dist/{const-BKUqg96Q.d.ts → const-D6ZRQLxI.d.cts} +2 -2
  13. package/dist/{index-DFHLLqYu.d.ts → index-1auUEYrl.d.ts} +3 -3
  14. package/dist/{index-CGsT4AQ8.d.cts → index-Bbpf5t-l.d.cts} +3 -3
  15. package/dist/index.cjs +139 -205
  16. package/dist/index.cjs.map +1 -1
  17. package/dist/index.d.cts +15 -9
  18. package/dist/index.d.ts +15 -9
  19. package/dist/index.js +139 -205
  20. package/dist/index.js.map +1 -1
  21. package/dist/{payload-types-Bk-duc5k.d.cts → payload-types-BnIET0xU.d.cts} +262 -328
  22. package/dist/{payload-types-Bk-duc5k.d.ts → payload-types-BnIET0xU.d.ts} +262 -328
  23. package/dist/query.cjs +48 -2
  24. package/dist/query.cjs.map +1 -1
  25. package/dist/query.d.cts +5 -5
  26. package/dist/query.d.ts +5 -5
  27. package/dist/query.js +48 -2
  28. package/dist/query.js.map +1 -1
  29. package/dist/realtime.d.cts +2 -2
  30. package/dist/realtime.d.ts +2 -2
  31. package/dist/server.cjs +459 -7
  32. package/dist/server.cjs.map +1 -1
  33. package/dist/server.d.cts +13 -8
  34. package/dist/server.d.ts +13 -8
  35. package/dist/server.js +459 -7
  36. package/dist/server.js.map +1 -1
  37. package/dist/{types-D4mjGFj4.d.ts → types-D1y4TgQm.d.ts} +2 -2
  38. package/dist/{types-CEK8uTvU.d.ts → types-DnRC3nLG.d.ts} +256 -9
  39. package/dist/{types-CNdpj2fI.d.cts → types-Dp1oYpD3.d.cts} +2 -2
  40. package/dist/{types-Dhk5NX7Z.d.cts → types-Dzn0LD2N.d.cts} +256 -9
  41. package/dist/ui/form.d.cts +1 -1
  42. package/dist/ui/form.d.ts +1 -1
  43. package/dist/ui/video.cjs +4 -2
  44. package/dist/ui/video.cjs.map +1 -1
  45. package/dist/ui/video.d.cts +4 -2
  46. package/dist/ui/video.d.ts +4 -2
  47. package/dist/ui/video.js +4 -2
  48. package/dist/ui/video.js.map +1 -1
  49. package/dist/webhook.d.cts +5 -5
  50. package/dist/webhook.d.ts +5 -5
  51. package/package.json +8 -28
  52. package/dist/analytics/react.cjs +0 -344
  53. package/dist/analytics/react.cjs.map +0 -1
  54. package/dist/analytics/react.d.cts +0 -24
  55. package/dist/analytics/react.d.ts +0 -24
  56. package/dist/analytics/react.js +0 -319
  57. package/dist/analytics/react.js.map +0 -1
  58. package/dist/analytics.cjs +0 -238
  59. package/dist/analytics.cjs.map +0 -1
  60. package/dist/analytics.d.cts +0 -39
  61. package/dist/analytics.d.ts +0 -39
  62. package/dist/analytics.js +0 -215
  63. package/dist/analytics.js.map +0 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,191 @@
1
1
  # @01.software/sdk
2
2
 
3
+ ## 0.65.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 49d3944: Add optional image/video gallery item descriptors to storefront content reads, with safe thumbnails and explicit ready, processing, or unavailable states. Keep existing image responses and gallery ordering compatible; video playback continues through the server-only admission helper.
8
+
9
+ ### Patch Changes
10
+
11
+ - 210e4a8: Sync generated tenant types with the optional Console menu display-name preference. Saved preferences remain restricted to Console operators.
12
+
13
+ ## 0.64.1
14
+
15
+ ### Patch Changes
16
+
17
+ - 94917e1: Align the SDK's Payload development dependencies with the stable 3.90.0 security fixes used by Console.
18
+ - 72131ac: Refresh the generated video types to reflect the chapter editor's placement in the General tab. Existing chapter data and playback behavior are unchanged.
19
+
20
+ ## 0.64.0
21
+
22
+ ### Minor Changes
23
+
24
+ - 6ec64b3: Preserve collection filter meaning for browser and server `find`/`count` and
25
+ server bulk mutations. Null equality filters were not filtered correctly before
26
+ for fields such as text: null became an empty string. `equals: null` and
27
+ `not_equals: null` now retain null through Payload's native JSON `where` query
28
+ parameter; date null filters, including the documented `publishedAt` example,
29
+ remain supported. Bulk mutations now also accept these null equality filters.
30
+
31
+ BREAKING (0.x minor): reads now share bulk's pre-request `ValidationError`
32
+ rejection. Newly rejected read inputs explicitly include `where: {}`,
33
+ `and: []`, `or: []`, an empty condition object such as `{ id: {} }`,
34
+ `not_in: []`, `in: []`, and undefined filter values. Nesting beyond 10 and
35
+ arrays above 1000 elements are also rejected. Null outside equality operators
36
+ remains rejected. This narrows the accepted read inputs, including empty forms
37
+ that previously succeeded. When no conditions remain, omit `where` instead of
38
+ passing `where: {}`. Remove undefined conditions explicitly or return an empty
39
+ result for an empty ID selection. Collection access rules stay unchanged.
40
+
41
+ ### Patch Changes
42
+
43
+ - 9a86409: Expose the server-only Membership renewal benefit hold field in generated collection types.
44
+ - e4d1efe: Fix collection updateMany and removeMany filters to use Payload query encoding.
45
+ Send update fields directly as JSON so authorized bulk mutations reach the existing
46
+ collection access checks and hooks.
47
+
48
+ Calls that previously failed with 400 now actually run.
49
+ Review existing bulk callers before upgrading. Filters with null/undefined values,
50
+ empty condition objects, empty arrays, nesting beyond 10 levels, or arrays over
51
+ 1000 elements are rejected
52
+ before sending a mutation.
53
+
54
+ - 745bd40: Classify unapplied membership paid evidence as an internal Console collection.
55
+ - 7caa6cc: Document unapplied membership payment evidence in generated payload types.
56
+
57
+ ## 0.63.0
58
+
59
+ ### Minor Changes
60
+
61
+ - a8f7691: Add `customer.auth.withdraw(currentPassword)` for password-reauthenticated self-withdrawal. Successful withdrawal archives the account and clears the local SDK session; active memberships and passwordless-only accounts return explicit refusal reasons.
62
+
63
+ ### Patch Changes
64
+
65
+ - 741b7d8: Synchronize the generated Payload type description for customer consent receipts.
66
+ - a33e6ea: Clarify in the generated Link field documentation that expiration removes a link from storefront lists while leaving stored state and direct collection reads unchanged.
67
+ - 4e47268: Clarify the generated audit-log payload documentation: audit summaries contain structural evidence, not personal-data values, secrets or request/response bodies.
68
+ - 0e80819: Synchronize generated internal usage types with the Console's separate email observation field. Public collection buckets and billing meters remain unchanged.
69
+
70
+ ## 0.62.2
71
+
72
+ ### Patch Changes
73
+
74
+ - e10f77f: Sync generated tenant customer-auth settings and the internal hosted consent receipt type. The receipt collection remains outside published collection clients and HTTP contracts.
75
+ - ee4176a: Clarify generated Customer consent field documentation as tenant self-reports while the hosted server receipt remains the authority for hosted reconsent.
76
+
77
+ ## 0.62.1
78
+
79
+ ### Patch Changes
80
+
81
+ - 676c69f: Add nullable usage invoice minimum-charge exemption evidence to generated types.
82
+ - bb4d0e2: Update API key scope guidance and generated types after retiring the stored web analytics scope.
83
+
84
+ ## 0.62.0
85
+
86
+ ### Minor Changes
87
+
88
+ - 4c023aa: BREAKING (0.x minor): Orders raw bulk updates now fail before document mutation.
89
+ Generic Orders `updateMany` and Admin Panel bulk edit are rejected. Keep top-ID
90
+ updates for unconditional edits; conditional callers must preserve the complete
91
+ where query and an at-most-one bound using raw HTTP or the Console Local API.
92
+ Document native admission/failure codes and retain the SDK's terminal HTTP 400
93
+ handling. Other collections and SDK transport signatures are unchanged.
94
+ Transaction-disabled Orders updates without a live external transaction also
95
+ fail before mutation with a localized HTTP 400; normal and sequential external
96
+ transactions continue to work.
97
+
98
+ ### Patch Changes
99
+
100
+ - 5a372d8: Correct the form submissions collection description to match its available actions.
101
+ - 87b450e: Synchronize generated Payload types for internal personal saved order views. Published collection addressability is unchanged.
102
+ - 2bcf3ae: Sync generated customer OAuth client types with implemented-only social provider
103
+ selection and validation in Console. Previously saved unimplemented providers
104
+ remain readable but are omitted from hosted login.
105
+
106
+ ## 0.61.1
107
+
108
+ ### Patch Changes
109
+
110
+ - 4283bcd: Synchronize generated Payload type documentation for hidden event locations. SDK field shapes remain unchanged.
111
+
112
+ ## 0.61.0
113
+
114
+ ### Minor Changes
115
+
116
+ - 22061d6: BREAKING (0.x minor): `useCustomerLogout()` now awaits direct refresh-family revocation and can report a server failure after local credentials are cleared. Use `customer.auth.logoutAsync()` for server logout; the synchronous `logout()` remains local-only compatibility.
117
+ - 418cdca: Add server-only external Customer link helpers with bounded previous-state snapshots, version-fenced mutations, and explicit request replay identity.
118
+
119
+ ### Patch Changes
120
+
121
+ - 0b1704a: Expose the existing billing custody method on the server client factory and withMarket return types.
122
+
123
+ ## 0.60.0
124
+
125
+ ### Minor Changes
126
+
127
+ - 1fea398: Add server-only delegated Membership ciphertext custody operations.
128
+ - cad9245: Add a server-only API for purpose-bound tenant-encrypted installation credentials.
129
+
130
+ ## 0.59.2
131
+
132
+ ### Patch Changes
133
+
134
+ - 936c20a: Include the tenant password-registration setting in generated Payload types. The existing Customer registration method preserves the new `registration_disabled` API reason.
135
+
136
+ ## 0.59.1
137
+
138
+ ### Patch Changes
139
+
140
+ - 259d977: Sync the internal generated Payload result type for the reconciled ticket payment outcome. No public SDK API changes.
141
+
142
+ ## 0.59.0
143
+
144
+ ### Minor Changes
145
+
146
+ - ad385ac: Add the server-only external-effect claims API for tenant-owned provider work. Require redacted report evidence and a remote operation identity before reporting no effect.
147
+
148
+ ## 0.58.0
149
+
150
+ ### Minor Changes
151
+
152
+ - d558727: Add server Customer delegation inputs for Customer Group restricted paid admission purchases across Cart, Checkout, and PaymentSession APIs.
153
+ - 6ce7cf3: BREAKING (0.x minor): Remove the three retired analytics collection slugs from
154
+ the exported `INTERNAL_COLLECTIONS` list and `InternalCollection` union, along
155
+ with their generated SDK types.
156
+
157
+ ### Patch Changes
158
+
159
+ - 26f94eb: Expose the tenant-wide timezone field in generated Payload collection types.
160
+
161
+ ## 0.57.0
162
+
163
+ ### Minor Changes
164
+
165
+ - 1b0deb7: BREAKING (0.x minor): Remove the SDK `createAnalytics`, `Analytics`, and `AnalyticsConfig` root exports and the `@01.software/sdk/analytics` and `/analytics/react` subpaths. Existing integrations should use an external analytics tool such as GA4, Plausible, or Vercel Analytics instead. The init CLI no longer generates or advertises an analytics helper.
166
+ - b91e56a: Add server-only `video.playback(videoId)` and `VideoPlayer.streamUrl` for signed,
167
+ quota-metered VOD playback. BREAKING for assets explicitly migrated to signed
168
+ playback: a stored legacy `muxPlaybackId` remains historical usage evidence,
169
+ but raw Mux HLS, thumbnail, and MP4 URLs built from it stop working. Existing
170
+ unmigrated paid assets retain their public playback path.
171
+
172
+ ### Patch Changes
173
+
174
+ - 6ab5331: Synchronize generated Payload types and collection ordering for the internal MCP daily usage collection.
175
+ - eb1e49f: Sync internal generated Payload types for the objectless ticket payment kernel result. No public SDK API changes.
176
+
177
+ ## 0.56.3
178
+
179
+ ### Patch Changes
180
+
181
+ - ef686ff: Synchronize generated entitlement types for explicit tenant-local Project Free grants and their server-owned activation key.
182
+
183
+ ## 0.56.2
184
+
185
+ ### Patch Changes
186
+
187
+ - ee75768: Synchronize the SDK Payload types with the TicketPurchase kernel obligation schema.
188
+
3
189
  ## 0.56.1
4
190
 
5
191
  ### Patch Changes
package/README.md CHANGED
@@ -50,7 +50,6 @@ import { receiveEmbeddedAdminSession } from '@01.software/sdk/embedded-admin'
50
50
  import { verifyEmbeddedAdminSession } from '@01.software/sdk/embedded-admin/server'
51
51
 
52
52
  // Components - sub-path imports per domain
53
- import { Analytics } from '@01.software/sdk/analytics/react'
54
53
  import { RichTextContent } from '@01.software/sdk/ui/rich-text'
55
54
  import { Image } from '@01.software/sdk/ui/image'
56
55
  import { FormRenderer } from '@01.software/sdk/ui/form'
@@ -70,7 +69,6 @@ entry.
70
69
  | `@01.software/sdk/client` | browser-safe `createClient` entry and SDK error guards | none |
71
70
  | `@01.software/sdk/server` | `createServerClient`, server-only APIs, and SDK error guards | none; keep `secretKey` code on the server |
72
71
  | `@01.software/sdk/errors` | SDK error classes and guards | none |
73
- | `@01.software/sdk/analytics` | browser analytics client and typed event helpers | none |
74
72
  | `@01.software/sdk/metadata` | SEO metadata extraction and generation helpers | none |
75
73
  | `@01.software/sdk/webhook` | webhook handlers, event guards, and webhook types | none |
76
74
  | `@01.software/sdk/query` | React Query hooks, cache helpers, `getQueryClient` | `@tanstack/react-query`, `react`, `react-dom` |
@@ -78,7 +76,6 @@ entry.
78
76
  | `@01.software/sdk/storefront-cache` | product storefront cache resource name helpers | none |
79
77
  | `@01.software/sdk/embedded-admin` | embedded app browser handshake and shared protocol types | none |
80
78
  | `@01.software/sdk/embedded-admin/server` | assertion verification and JWKS loading | none; keep receiver session creation on the server |
81
- | `@01.software/sdk/analytics/react` | `<Analytics />` | `react`, `react-dom` |
82
79
  | `@01.software/sdk/ui/rich-text` | `RichTextContent`, `StyledRichTextContent` | `react`, `react-dom`, `@payloadcms/richtext-lexical` |
83
80
  | `@01.software/sdk/ui/form` | `FormRenderer` | `react`, `react-dom` |
84
81
  | `@01.software/sdk/ui/code-block` | `CodeBlock`, `highlight` | `react`, `react-dom`, `shiki`, `hast-util-to-jsx-runtime` |
@@ -87,6 +84,18 @@ entry.
87
84
  | `@01.software/sdk/ui/video` | `VideoPlayer` | `react`, `react-dom`, `@mux/mux-player-react` |
88
85
  | `@01.software/sdk/ui/image` | `Image` | `react`, `react-dom` |
89
86
 
87
+ ### Video playback admission
88
+
89
+ On your server, call `createServerClient(...).video.playback(video.id)` after
90
+ applying your viewer's content-access policy. Pass its `streamUrl` to
91
+ `<VideoPlayer video={video} streamUrl={streamUrl} />`.
92
+ The bearer URL expires after two hours; refresh it before the deadline during
93
+ long playback or after a long pause. Free playback uses a quota-metered gateway and can return 403 when the
94
+ monthly allowance is exhausted or 503 when admission cannot be verified.
95
+ `muxPlaybackId`, `getVideoStreamUrl`, and raw Mux thumbnail/MP4 helpers are
96
+ legacy public-ID paths for unmigrated paid assets. A signed asset's retained
97
+ `muxPlaybackId` is historical usage evidence and is not a playable URL.
98
+
90
99
  `CanvasRenderer` renders reserved `shape` nodes (`terminator`, `process`,
91
100
  `decision`, `io`, `subprocess`) with first-party SVG geometry, without a tenant
92
101
  catalog row or QuickJS template.
@@ -162,91 +171,7 @@ Migration quick reference:
162
171
  self-contained and must not import private contracts; Console code should
163
172
  import shared helpers from contracts directly.
164
173
 
165
- ### Analytics
166
-
167
- ```typescript
168
- // Pageviews + custom events (browser)
169
- import { createAnalytics } from '@01.software/sdk/analytics'
170
-
171
- const analytics = createAnalytics({ publishableKey: 'pk01_xxx' })
172
- // auto-tracks pageviews; call analytics.pageview('/custom-path') manually if needed
173
- analytics.track('signup', { plan: 'pro', trial: false })
174
- ```
175
-
176
- **Custom events must be registered first.** Each custom event name, its
177
- dimensions, and any enum/boolean value sets are defined per workspace in
178
- Console → Analytics. An unregistered or mistyped event is accepted by the
179
- browser (200/204) and then **silently dropped server-side** — no client error.
180
- Register the event before firing it.
181
-
182
- **Typed events (compile-time safety).** Declare an event map as a plain `type`
183
- (do **not** `extends AnalyticsEventMap` — an index signature would defeat
184
- typo detection) and pass it as the generic. Mistyped names and out-of-enum
185
- values then fail to compile:
186
-
187
- ```typescript
188
- import {
189
- createAnalytics,
190
- defineAnalyticsEvents,
191
- } from '@01.software/sdk/analytics'
192
-
193
- type ShopEvents = {
194
- signup: { plan: 'free' | 'pro'; trial: boolean }
195
- add_to_cart: { productId: string; price: number }
196
- checkout_start: undefined // no props
197
- }
198
-
199
- const analytics = createAnalytics<ShopEvents>({ publishableKey: 'pk01_xxx' })
200
- analytics.track('signup', { plan: 'pro', trial: false }) // ✅
201
- analytics.track('checkout_start') // ✅ no props
202
- // analytics.track('signpu', { plan: 'pro', trial: false }) // ❌ unknown event
203
- // analytics.track('signup', { plan: 'team', trial: false }) // ❌ enum violation
204
-
205
- // Bind the map once to avoid repeating the generic:
206
- const create = defineAnalyticsEvents<ShopEvents>()
207
- const a2 = create({ publishableKey: 'pk01_xxx' })
208
- ```
209
-
210
- **React.** Use `AnalyticsProvider` + `useAnalytics()` to fire events from
211
- components. The provider auto-tracks pageviews like `<Analytics/>` and owns one
212
- instance for its subtree:
213
-
214
- Reusing the `ShopEvents` type from above:
215
-
216
- ```tsx
217
- import {
218
- AnalyticsProvider,
219
- useAnalytics,
220
- } from '@01.software/sdk/analytics/react'
221
-
222
- function Root() {
223
- return (
224
- <AnalyticsProvider>
225
- <App />
226
- </AnalyticsProvider>
227
- )
228
- }
229
-
230
- function SignupButton() {
231
- const { track, pageview } = useAnalytics<ShopEvents>()
232
- // pageview(path?) is available for manual SPA pageviews; the provider already auto-tracks pageviews
233
- return (
234
- <button onClick={() => track('signup', { plan: 'pro', trial: false })}>
235
- Sign up
236
- </button>
237
- )
238
- }
239
- ```
240
-
241
- `<Analytics />` remains available as a pageview-only mount helper for apps that
242
- do not fire custom events from components.
243
-
244
- **Send mode.** `mode: 'auto'` (default) suppresses sends on local hosts
245
- (`localhost` / `127.0.0.1` / `*.local`); `'production'` always sends, even
246
- locally; `'development'` never sends. The hosted `<script>` snippet uses the
247
- same vocabulary via `window.__01_analytics__.mode` or `data-mode`; its legacy
248
- `captureOnLocalhost: true` flag is equivalent to `mode: 'production'` and is
249
- honored only when `mode` is unset.
174
+ For website analytics, integrate an external tool such as GA4, Plausible, or Vercel Analytics in your own frontend. The SDK no longer provides analytics collection helpers.
250
175
 
251
176
  ## Getting Started
252
177
 
@@ -390,11 +315,26 @@ storefronts can render time-sensitive copy.
390
315
 
391
316
  `content.galleryItems.list()` calls `GET /api/gallery-items/storefront`;
392
317
  gallery item DTOs include title, description, content, gallery reference, and
393
- image media. Tenant, metadata, draft status, and storage/provider internals are
318
+ optional `mediaType` (`image` or `video`). Treat an omitted `mediaType` as `image`
319
+ for older responses. Image items retain `image` media. Video items include a
320
+ safe `video` descriptor with `availability: 'ready' | 'processing' | 'unavailable'`,
321
+ and may include a video document `id` and uploaded `thumbnail`. Disabled or
322
+ plan-blocked video features, missing/deleted targets, and failed videos return
323
+ only `{ availability: 'unavailable' }`, preserving each item's position and
324
+ pagination. The image relationship is not used as a video poster.
325
+ Tenant, metadata, draft status, and storage/provider internals are
394
326
  omitted. Gallery item reads require either `gallerySlug` or `galleryId`; prefer
395
327
  `gallerySlug` for storefront routes. The default gallery item sort is `manual`,
396
328
  matching curator order from the Console.
397
329
 
330
+ The descriptor's `ready` state is preparation metadata, not playback authority.
331
+ Your application server must verify that the requested video belongs to the
332
+ published gallery/item and apply its viewer policy before calling
333
+ `server.video.playback(video.id)`. Deliver that response's `streamUrl` to the
334
+ player; playback rechecks the current tenant, video state, and feature/plan.
335
+ Keep server credentials on the application server. Gallery responses do not
336
+ include playback URLs, provider IDs, or tokens.
337
+
398
338
  Both helpers return Payload-style pagination (`docs`, `totalDocs`, `page`,
399
339
  `limit`, etc.). `limit` is bounded server-side to `1..100`; invalid query,
400
340
  publishable-key, feature, rate-limit, and server errors surface through the
@@ -866,6 +806,50 @@ Keep these calls in trusted server code. Do not mutate `customers.groups`
866
806
  directly: after source-authority activation it is a read-compatible projection
867
807
  and direct writes return `customer_groups_direct_write_retired`.
868
808
 
809
+ ### External Customer links (server only)
810
+
811
+ `server.externalCustomerLinks` connects a platform Customer to an external
812
+ Customer ID within the namespace and environment derived from an active
813
+ integration registration. Your app supplies a normalized stable `accountScope`
814
+ to reserve identity; namespace-bound keys authorize all accounts in that
815
+ namespace, and account scope is not an account ACL. All six methods (`connect`,
816
+ `get`, `list`,
817
+ `unlink`, `capturePreviousState`, `clearPreviousState`) require server
818
+ credentials and Customer read or write scope.
819
+
820
+ ```typescript
821
+ const link = await server.externalCustomerLinks.connect({
822
+ integrationId,
823
+ customerId,
824
+ accountScope,
825
+ externalCustomerId: 'contact-123',
826
+ requestId: crypto.randomUUID(),
827
+ })
828
+ const capture = {
829
+ integrationId,
830
+ customerId,
831
+ linkId: link.id,
832
+ requestId: crypto.randomUUID(),
833
+ expectedVersion: link.version,
834
+ previousState: { tier: 'silver' },
835
+ }
836
+ await server.externalCustomerLinks.capturePreviousState(capture)
837
+ // After an interrupted response, retry this exact request, including expectedVersion.
838
+ ```
839
+
840
+ The bounded previous state accepts up to 16 flat string/number/boolean/null
841
+ fields, 512 characters per string, and 2048 UTF-8 JSON bytes. It records only
842
+ what your server supplies; the platform performs no external provider I/O.
843
+ Before restoring provider state, check the current active owner with
844
+ `list({ integrationId, accountScope, externalCustomerId, state: 'active' })`
845
+ and evaluate whether the restoration is still appropriate. Lists accept optional Customer,
846
+ account scope, external ID, and state filters (external ID requires account
847
+ scope), `limit` (default 25, maximum 100), and `afterId`;
848
+ use `nextCursor` for the next page. Unlink and clear also require a fresh
849
+ request UUID and the current version; exact retries retain the original values.
850
+ A privacy-erased snapshot has state `erased`, null `externalCustomerId`, and
851
+ null `previousState`. Keep these calls and snapshots in trusted server code.
852
+
869
853
  ## Storefront performance defaults
870
854
 
871
855
  - **PLP:** prefer `commerce.product.listingPage()` or `useProductListingPage()` (GET `/api/products/listing-groups/query/catalog`, CDN-cacheable, card-ready). Use `listingGroupsCatalog({ productIds })` only when IDs are already known. Treat the full listing-groups response shape as a server-auth escape hatch because it can include operational stock fields. Avoid fetching a product list and then calling `detail()` per card.
@@ -1137,6 +1121,49 @@ await server.collections.from('articles').updateMany(where, data)
1137
1121
  await server.collections.from('articles').removeMany(where)
1138
1122
  ```
1139
1123
 
1124
+ Collection `find`, `count`, `updateMany`, and `removeMany` share filter validation.
1125
+ `equals: null` and `not_equals: null` retain their null meaning through Payload
1126
+ REST, including text fields; null-containing filters use Payload's native JSON
1127
+ `where` query parameter. Other filters keep bracketed encoding. The SDK throws
1128
+ `ValidationError` before a request for undefined values, null outside those two
1129
+ operators, empty condition objects, empty arrays (including `and`/`or`), nesting
1130
+ beyond 10 levels, and arrays with more than 1000 elements. Omit `where` for an
1131
+ unfiltered read. For a dynamically empty ID list, return an empty result before
1132
+ calling the SDK rather than passing `in: []`. False, zero, empty strings, and
1133
+ Date conditions remain valid.
1134
+
1135
+ Check your bulk filters carefully: calls that previously failed with HTTP 400
1136
+ now perform updates or deletions. Large `in`/`not_in`
1137
+ lists can exceed URL length limits (around 250 UUIDs); split work into smaller
1138
+ batches. For `not_in`, keep the full exclusion condition in every batch rather
1139
+ than splitting the exclusions across separate mutations.
1140
+
1141
+ ### Orders update compatibility
1142
+
1143
+ Orders accept one-document updates only. Admin Panel bulk edits and generic
1144
+ `server.collections.from('orders').updateMany(...)` are rejected before any
1145
+ Order changes with HTTP 400 (`errors[0].data.code: order_bulk_update_unsupported`).
1146
+ Use `update(orderId, data)` for an unconditional edit. For a conditional update,
1147
+ keep the entire where predicate in the raw `PATCH /api/orders` query and set
1148
+ `limit=1`; send the field data as the body. Local API callers use numeric
1149
+ `limit: 1`, or a strict UUID `id.equals` that bounds the complete predicate.
1150
+ An ID in only one OR branch is insufficient. SDK `updateMany` has no limit
1151
+ option and is not a supported Orders CAS path.
1152
+
1153
+ A conditional miss returns `docs: [], errors: []`. A collected hook failure
1154
+ instead returns HTTP 500 (`errors[0].data.code: order_update_failed`), with a
1155
+ localized message and rollback of the supported single operation. Access and
1156
+ tenant checks remain in force. Do not replace a conditional update with an
1157
+ unconditional ID update or automatically retry payment capture. Separate
1158
+ per-order edits do not promise atomic success across the whole selection.
1159
+
1160
+ Console Local API Orders updates also require a live transaction. Setting
1161
+ `disableTransaction: true` without a live external transaction rejects both
1162
+ top-ID and where updates before writes with HTTP 400
1163
+ (`errors[0].data.code: order_transaction_required`). Remove that option to
1164
+ use Payload's normal transaction, or pass a live external transaction;
1165
+ a request transaction ID by itself is not sufficient.
1166
+
1140
1167
  ### API Response Types (Payload Native)
1141
1168
 
1142
1169
  The SDK returns Payload CMS native response types without wrapping:
@@ -1339,7 +1366,6 @@ const { token, customer } = await client.customer.auth.login({
1339
1366
  // Profile & token management
1340
1367
  const profile = await client.customer.auth.me()
1341
1368
  client.customer.auth.isAuthenticated()
1342
- client.customer.auth.logout()
1343
1369
 
1344
1370
  // Authenticated customer's own orders (Client-only)
1345
1371
  const orders = await client.commerce.orders.listMine({
@@ -1348,12 +1374,22 @@ const orders = await client.commerce.orders.listMine({
1348
1374
  financialStatus: 'paid',
1349
1375
  })
1350
1376
 
1377
+ await client.customer.auth.logoutAsync()
1378
+
1351
1379
  // Password
1352
1380
  await client.customer.auth.forgotPassword('john@example.com')
1353
1381
  await client.customer.auth.resetPassword(token, newPassword)
1354
1382
  await client.customer.auth.changePassword(currentPassword, newPassword)
1355
1383
  ```
1356
1384
 
1385
+ `logoutAsync()` revokes the direct refresh-token family on the server and clears
1386
+ local credentials even when the request fails. Handle a rejection as incomplete
1387
+ server revocation. The older synchronous `logout(): void` clears local state
1388
+ immediately but cannot revoke the server session. `useCustomerLogout()` awaits
1389
+ the server request and clears authenticated customer query data on success or
1390
+ failure. An issued access token can remain valid for up to 15 minutes; legacy
1391
+ bearer-only refresh remains a separate compatibility path.
1392
+
1357
1393
  `forgotPassword()` keeps the same SDK signature in both delivery modes. A
1358
1394
  tenant administrator chooses either backward-compatible signed webhook
1359
1395
  delivery or platform-managed email in the Console customer-authentication
@@ -1362,6 +1398,31 @@ the platform appends the `token` query parameter and sends through the verified
1362
1398
  tenant sender or the platform fallback. Do not add reset tokens or dynamic
1363
1399
  redirect URLs to the SDK call.
1364
1400
 
1401
+ #### Self-withdrawal
1402
+
1403
+ A signed-in customer with a local password can withdraw using their current
1404
+ password. With an authenticated client:
1405
+
1406
+ ```ts
1407
+ const { withdrawnAt } = await client.customer.auth.withdraw(currentPassword)
1408
+ ```
1409
+
1410
+ This calls `POST /api/customers/withdraw` with the publishable key and Customer
1411
+ JWT. Success archives the account immediately and clears the SDK's local
1412
+ credentials. Clear any application-owned session cookies and customer cache as
1413
+ well. Archived accounts cannot use existing sessions or authenticate again.
1414
+ Withdrawal preserves order, payment and membership-charge history; it does not
1415
+ erase personal data or free the email for a new registration.
1416
+
1417
+ Branch on `SDKError.apiReason`: `active_membership_exists` (409) requires the
1418
+ membership to end first, including scheduled cancellations still in an active
1419
+ status; `reauthentication_unavailable` (409) means no local password is set and
1420
+ the customer should contact the tenant administrator. Incorrect passwords use
1421
+ the shared login attempt counter and temporary lock (`account_locked`, 423).
1422
+ Refusal leaves the SDK credentials intact. Server keys cannot withdraw on a
1423
+ customer's behalf. Operator restore continues to follow the existing archive
1424
+ rules; the withdrawal timestamp is retained.
1425
+
1365
1426
  ### Market discovery
1366
1427
 
1367
1428
  Discover a tenant's active buyer markets and resolve the default market. These
@@ -2013,6 +2074,28 @@ await server.community.moderation.banCustomer({ customerId, isPermanent?, banned
2013
2074
  await server.community.moderation.unbanCustomer({ customerId })
2014
2075
  ```
2015
2076
 
2077
+ ### Installation credentials (server only)
2078
+
2079
+ `createServerClient().installationCredentials` stores one bounded ciphertext
2080
+ for a registered non-payment OAuth installation. A tenant administrator must
2081
+ register the installation and explicitly bind the server key; an ordinary
2082
+ `sk01_` key or external-effect claim grant is insufficient. Encrypt before
2083
+ `create`, decrypt only in your server, and keep OAuth and provider calls there.
2084
+
2085
+ ```typescript
2086
+ const current = await server.installationCredentials.get({ installationId })
2087
+ const next = await server.installationCredentials.replace({
2088
+ installationId,
2089
+ expectedVersion: current.version,
2090
+ ciphertext: newlyEncryptedCredential,
2091
+ })
2092
+ ```
2093
+
2094
+ Replacement uses a version condition; after an uncertain result, read back
2095
+ before retrying. `dispose` clears the platform copy but does not revoke a
2096
+ credential already read by your server. A platform database restore requires
2097
+ re-authorization before older installations become readable again.
2098
+
2016
2099
  ### Webhook
2017
2100
 
2018
2101
  Webhooks created in Console start inactive. Use this setup order so no event is
@@ -2286,7 +2369,7 @@ SOFTWARE_SECRET_KEY=sk01_... # Server only — opaque API key from Console
2286
2369
 
2287
2370
  > `secretKey` may be an `sk01_...` API key or a `pat01_...` personal access token. Server SDK calls must also send the matching `publishableKey`; PAT tenant selection is pinned server-side, and callers must not send `X-Tenant-Id`.
2288
2371
 
2289
- API keys created without explicit scopes use the default `['read', 'write']`. Console API-key creation can request narrower `scopes[]`; in the first explicit-scope slice tenant admins may grant only `read` and `write`. Higher-authority scopes such as `webhook`, `analytics`, and `super-admin` require platform authority and otherwise return `scope_grant_forbidden`.
2372
+ API keys created without explicit scopes use the default `['read', 'write']`. Console API-key creation can request narrower `scopes[]`; tenant admins may grant only `read` and `write`. `webhook` and `super-admin` require a super-admin session. Requests containing the retired `analytics` scope, alone or mixed, are rejected for every role. Previously issued keys keep their identity; an analytics-only key has no effective access, while a mixed key keeps its other scopes.
2290
2373
 
2291
2374
  > **SDK 0.9.0**: Server auth now uses opaque bearer tokens (`sk01_...`). Generate API keys from the Console. `createServerToken`, `createApiKey`, and `parseApiKey` are no longer part of the SDK surface.
2292
2375