@vuoma/sdk 0.1.1 → 0.2.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.
package/LICENSE ADDED
@@ -0,0 +1,45 @@
1
+ Vuoma Connected Application License
2
+ Version 1.0 — October 19, 2026
3
+
4
+ Copyright (c) 2026 UPPP LLC, doing business as Vuoma.
5
+
6
+ Permission is granted, without a separate software license fee, to use, copy,
7
+ modify and distribute this package and its documentation (the Software) solely
8
+ to develop, test, operate and distribute applications that integrate with the
9
+ Vuoma service through authorized Vuoma APIs. This includes commercial,
10
+ studio-branded applications, websites, and development or test versions using
11
+ mocked Vuoma responses. You may share the Software with your developers and
12
+ contractors for these purposes and include it in source or compiled application
13
+ distributions, subject to this license.
14
+
15
+ You must retain this license and the copyright notice in copies or substantial
16
+ portions of the Software, including an application's third-party notices. Your
17
+ modifications must remain subject to these permitted-use limits. You may not
18
+ use or distribute the Software as a general-purpose library for applications
19
+ unconnected to Vuoma, or to implement or operate a replacement for the Vuoma
20
+ service, without separate written permission from UPPP LLC.
21
+
22
+ This license applies only to the Software included in this package. It does
23
+ not license Vuoma's backend, databases, customer data, studio branding,
24
+ third-party assets or trademarks. Separately identified third-party software
25
+ remains subject to its own license. You retain any rights you otherwise hold
26
+ in your application code and content.
27
+
28
+ This license does not grant API credentials, access to any account or data,
29
+ hosting, support, or a right to continued service. Access to Vuoma remains
30
+ subject to applicable service agreements, permissions and charges. Ending
31
+ service does not transfer the backend or authorize continued API access. You
32
+ may retain archival copies and use the Software in development intended for an
33
+ authorized Vuoma integration; operating it for an unrelated replacement service
34
+ requires separate permission.
35
+
36
+ All rights not expressly granted are reserved. Use outside these terms is not
37
+ licensed. This is a source-available, restricted-use license, not an open-source
38
+ license. Separate written agreements with UPPP LLC may grant additional rights.
39
+
40
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
41
+ IMPLIED, INCLUDING MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
42
+ NONINFRINGEMENT. TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE COPYRIGHT HOLDER
43
+ IS NOT LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY ARISING FROM THE
44
+ SOFTWARE OR ITS USE. Nothing in this license limits liability or rights that
45
+ cannot lawfully be limited.
package/README.md CHANGED
@@ -3,29 +3,30 @@
3
3
  Framework-neutral JavaScript and TypeScript client for the existing Vuoma v1
4
4
  public organization and member account APIs. Works with a host-provided Fetch
5
5
  implementation; it imports no React, Svelte, Expo or Supabase runtime. The package
6
- does not store credentials, read environment variables, create accounts, or
7
- grant access. Install the public package and pin the reviewed version:
6
+ does not store credentials, read environment variables, or grant access. Account
7
+ setup and booking use the same reviewed organization-member API methods as the
8
+ Vuoma native apps. Server authorization and studio policies remain authoritative.
9
+ Install the public package in your own application; access to Vuoma's private
10
+ repository is not required:
8
11
 
9
12
  ```sh
10
- npm install @vuoma/sdk@0.1.1
13
+ npm install @vuoma/sdk@0.2.0
11
14
  ```
12
15
 
13
16
  Use SvelteKit and SvelteKit UI with `@vuoma/web` for website account components
14
- and same-origin server sessions. Use React Native for mobile UI and this SDK as
17
+ and same-origin server sessions. Use `@vuoma/native-ui` for React Native account and booking UI and this SDK as
15
18
  its framework-neutral API transport. The complete `@vuoma/react-native` studio
16
19
  app core remains a private workspace; independent apps do not depend on it.
17
20
  The public setup guide is at `/developers` and `/o/<organization_slug>/developers`
18
21
  on the same combined Vuoma application and landing-page deployment. The current
19
- origin is `https://vuoma.vercel.app`; use `https://vuoma.com` only after domain
20
- cutover and exact-origin configuration have been verified.
22
+ verified API origin is `https://vuoma.com`.
21
23
 
22
24
  ```js
23
25
  import { createVuomaClient } from '@vuoma/sdk'
24
26
 
25
27
  const vuoma = createVuomaClient({
26
- apiOrigin: 'https://vuoma.vercel.app', // Use your verified Vuoma origin.
28
+ apiOrigin: 'https://vuoma.com', // Use your verified Vuoma origin.
27
29
  organizationSlug: 'your-studio',
28
- appKey: publishableAppKey, // The organization's registered vuoma_pk_ app key.
29
30
  fetch: globalThis.fetch,
30
31
  })
31
32
 
@@ -37,12 +38,11 @@ const schedule = await vuoma.public.getSchedule({
37
38
  })
38
39
  ```
39
40
 
40
- The optional `appKey` identifies a registered organization app. Obtain its
41
- publishable `vuoma_pk_` key from the organization's app registration and use the
42
- same key for public reads, sign-in, refresh, sign-out and member requests. Public
43
- reads also work without an app key where the API allows anonymous access. The
44
- organization slug is a route hint; the server validates the app's organization
45
- binding. Schedule timestamps are normalized to
41
+ The organization slug is public routing configuration, built into each studio app.
42
+ Sign-in authenticates a person for that studio. Private calls require their bearer
43
+ token; the server verifies the live session, studio membership and permissions.
44
+ Public schedule and catalog reads need no registration or app key.
45
+ Schedule timestamps are normalized to
46
46
  UTC, and ranges must be positive and at most 63 days. The response preserves
47
47
  `schedule.has_more`; when it is true, split the requested range and fetch each
48
48
  part rather than displaying the first response as a complete schedule.
@@ -57,9 +57,8 @@ server or native host; it does not establish a browser cookie session.
57
57
  ```js
58
58
  let session = null // Example in-memory host state; no SDK persistence.
59
59
  const vuoma = createVuomaClient({
60
- apiOrigin: 'https://vuoma.vercel.app',
60
+ apiOrigin: 'https://vuoma.com',
61
61
  organizationSlug: 'your-studio',
62
- appKey: publishableAppKey,
63
62
  tokenAdapter: { getSession: () => session },
64
63
  })
65
64
 
@@ -82,11 +81,8 @@ Native, keep refresh tokens in Keychain/Keystore through a secure-storage adapte
82
81
  keep access tokens in memory, and refresh through the documented member route.
83
82
  Refresh scheduling, retries and persistence remain host responsibilities.
84
83
 
85
- An app-bound member session requires the same active app registration on each
86
- request. Revoking that registration invalidates its bound sessions. Re-enabling
87
- or replacing an app key does not restore those sessions: remove the rejected
88
- session from your host adapter and require a fresh member sign-in. The SDK does
89
- not retry authorization failures or silently change registrations.
84
+ Existing signed-in sessions remain valid while the native app-key system is removed.
85
+ Authorization failures do not trigger SDK retries or change the host session.
90
86
 
91
87
  The SDK uses `credentials: 'omit'`, prevents redirects, and sends member access
92
88
  tokens only to its exact configured origin and organization. It rejects missing,
@@ -103,19 +99,108 @@ business/financial validation, and do not replace the server's authorization or
103
99
  canonical schemas. The configured response byte limit is checked after reading
104
100
  the response text and against a supplied Content-Length header.
105
101
 
102
+ ## Signup, activation and password reset
103
+
104
+ Email confirmation and password creation stay within the host UI; the person
105
+ reads the six-digit code in their email app. No access token, cookie or API key
106
+ is sent by these methods. For new or automatically resolved accounts, collect a
107
+ display name. Choose `auto` to let verified email ownership resolve a new profile,
108
+ an imported profile or an existing studio account; `activate` performs the same
109
+ verified resolution without collecting a new name. `new` and `migrate` retain
110
+ the dedicated signup confirmation path. Recovery uses its own code path.
111
+
112
+ ```js
113
+ await vuoma.auth.requestAccountCode({
114
+ mode: 'signup', signupIntent: 'auto', email, displayName,
115
+ })
116
+ const capability = await vuoma.auth.confirmAccountCode({
117
+ mode: 'signup', signupIntent: 'auto', email, code,
118
+ })
119
+ // The resolution contains only the verified display name and masked contacts.
120
+ const resolution = capability.account_resolution
121
+ await vuoma.auth.completePassword({ capability, password })
122
+ session = await vuoma.auth.signIn({ email, password })
123
+
124
+ // Password reset uses the same scoped completion step.
125
+ await vuoma.auth.requestAccountCode({ mode: 'recovery', email })
126
+ const reset = await vuoma.auth.confirmAccountCode({ mode: 'recovery', email, code })
127
+ await vuoma.auth.completePassword({ capability: reset, password: newPassword })
128
+ ```
129
+
130
+ Keep password capabilities in memory and scoped to their original studio. They
131
+ are not login tokens. A `VuomaApiError` with status `400` and code
132
+ `weak_password_retry` lets the person choose a stronger password using the same
133
+ capability. Other failures retain their HTTP status and safe machine code; raw
134
+ backend messages and credentials are never reflected in SDK error messages.
135
+
136
+ ## Class and appointment booking
137
+
138
+ Every mutation executes **once**. Create a `source_request_id` before submission
139
+ and retain it with the exact payload until the outcome is confirmed. A timeout,
140
+ abort or invalid response may follow a committed mutation. Retrying the same
141
+ class payload with the same ID uses the server's existing idempotency boundary;
142
+ do not generate a new ID to recover an uncertain result. Availability, credits,
143
+ prices, cancellation policies and current authorization are rechecked by Vuoma.
144
+
145
+ ```js
146
+ const bookingRequest = {
147
+ service_session_id: selectedSession.id,
148
+ source_request_id: crypto.randomUUID(),
149
+ // entitlement_id is optional; the server can select an eligible benefit.
150
+ }
151
+ const receipt = await vuoma.member.bookClass(bookingRequest)
152
+ const terms = await vuoma.member.getCancellationQuote(receipt.booking.id)
153
+ // Present terms and collect confirmation before this call.
154
+ await vuoma.member.cancelBooking(receipt.booking.id, {
155
+ expectedFeeAmount: terms.quote.fee_amount,
156
+ })
157
+ ```
158
+
159
+ When required by the studio, class requests also supply the complete existing
160
+ policy acceptance: `policy_version`, `policy_consent: true`,
161
+ `payment_authorized: true` and `payment_authorization_id`. Never invent consent
162
+ or a card authorization; payment/card setup is outside these methods. A
163
+ `booking_cancellation_quote_changed` response requires fetching and presenting
164
+ fresh terms before another cancellation attempt. `cancelBooking` also cancels an
165
+ individual appointment booking through the same reviewed API.
166
+
167
+ ```js
168
+ const bookingCatalog = await vuoma.public.getBookingCatalog()
169
+ const availability = await vuoma.public.getAppointmentAvailability({ from, to })
170
+ // If has_more is true, split the range and load both halves before presenting it
171
+ // as complete. Availability accepts half-open ranges spanning at most 14 days.
172
+ const visitRequest = {
173
+ source_request_id: crypto.randomUUID(),
174
+ appointments: selectedSlots.map(({ service_offering_id, starts_at, ends_at, credit_cost }) =>
175
+ ({ service_offering_id, starts_at, ends_at, credit_cost })),
176
+ }
177
+ const visit = await vuoma.member.bookAppointmentVisit(visitRequest)
178
+ // After an uncertain result, read the original receipt before resubmitting.
179
+ const recovered = await vuoma.member.getAppointmentVisit(visitRequest)
180
+ ```
181
+
182
+ Visits contain 1–10 ordered, non-overlapping appointments. Submission and
183
+ reconciliation validate the request ID, each selected offering/time/credit cost,
184
+ receipt totals and unique booking/session IDs. A status `404` alone does not
185
+ prove an earlier in-flight submission cannot finish; retain the saved payload
186
+ and recover/resubmit only with that same ID. The SDK does not persist pending
187
+ requests, retry mutations or decide when to clear recovery state.
188
+
189
+ Native hosts that keep an access token separately from customer profile data may
190
+ supply `bookingTokenAdapter: { getCredential: () => ({ organization_slug,
191
+ access_token }) }` instead of `tokenAdapter`. Return the **current** credential or
192
+ `null`, so completion after sign-out/account change is rejected. This adapter
193
+ supports only class/visit/quote/cancellation methods, rejects platform-account
194
+ and integration credentials, and does not authorize profile or commerce reads.
195
+ The complete member session adapter is preferred when available. Existing
196
+ Vuoma platform-account flows remain a separate private authority path.
197
+
106
198
  ## Keys and client IDs
107
199
 
108
- - **Publishable Vuoma app keys** have the exact format `vuoma_pk_` followed by
109
- 32 lowercase hexadecimal characters. They may be included in an app bundle
110
- and identify its organization registration. The SDK sends them only in
111
- `X-Vuoma-App-Key` to its configured origin, never in `Authorization`, a URL
112
- or a request body. They do not sign a customer in or grant private access;
113
- member access/refresh tokens and the server's live app/session checks remain
114
- required. Confidential organization or Supabase service keys are rejected
115
- as `appKey` values.
116
200
  - **Organization integration/delegated API keys** are confidential server
117
- credentials. Never put them in a website bundle, native binary, URL or SDK
118
- token adapter. They do not sign a customer in.
201
+ credentials. Keep them on an integration server and outside website bundles,
202
+ native binaries, URLs and the member client's session adapter. Use the separate
203
+ organization client below for supported scoped API reads.
119
204
  - **Registered OAuth client IDs** are public app identifiers. Existing shared
120
205
  sign-in grants only `openid`, `profile` and `email`; those identity tokens do
121
206
  not authorize this SDK's member account methods.
@@ -125,26 +210,72 @@ the response text and against a supplied Content-Length header.
125
210
  Platform-account sessions are a separate boundary and are intentionally not
126
211
  accepted by this member client.
127
212
 
128
- This package adds no OAuth grant, CORS policy or backend permission. A third-party
129
- website still needs its backend adapter. App registration and app-scoped member
130
- session grants are enforced by the API; a publishable app key alone never
213
+ This package adds no OAuth grant or backend permission. Vuoma's member routes
214
+ allow cross-origin requests with explicit bearer credentials and no cookies.
215
+ Browser cookie sessions use the website's same-origin backend adapter. Live user
216
+ sessions and studio permissions are enforced by the API; a studio slug never
131
217
  authorizes private account data.
132
218
 
133
- Supported routes are public `/api/v1/o/{slug}/member/{bootstrap,schedule,catalog}`;
134
- member sign-in, profile GET/PATCH and commerce GET; plus the existing global
135
- member refresh/sign-out routes. Signup, payments, booking mutations, uploads,
136
- staff administration and platform-account sessions are outside this initial SDK.
219
+ Supported routes include public `/api/v1/o/{slug}/member/{bootstrap,schedule,catalog,booking-catalog,availability}`;
220
+ organization Auth `/api/v1/o/{slug}/auth/{sign-in,refresh,sign-out,sign-up,recovery,password}`
221
+ and the reviewed `sign-up`, `account` and `recovery` `confirm-code` endpoints;
222
+ member profile GET (`/me/profile?view=basic`), PATCH `/me/profile`, GET `/me/commerce`,
223
+ POST `/me/commerce/bookings`, booking `cancellation-quote`/`cancel`, and
224
+ POST/GET `/me/commerce/visits` with receipt lookup by request ID.
225
+ Payments, uploads, staff administration and platform-account authentication
226
+ remain outside this member client.
227
+
228
+ ## Contract-generated organization operations
229
+
230
+ The server-side `@vuoma/sdk/organization` entry point accepts a confidential
231
+ scoped API token. It shares generated request and response schemas with the
232
+ operation registry, OpenAPI document and organization MCP server. The seven
233
+ reviewed reads cover organization details, locations, services, schedule,
234
+ the user directory, task lists and inventory. Route inventory is not a reviewed
235
+ payload contract and does not automatically become an SDK method.
236
+
237
+ ```js
238
+ import { createOrganizationApiClient } from '@vuoma/sdk/organization'
239
+
240
+ const api = createOrganizationApiClient({
241
+ apiOrigin: 'https://vuoma.com',
242
+ organizationSlug: 'your-studio',
243
+ getApiToken: () => integrationServerToken,
244
+ })
245
+ const result = await api.operations['users.directory.read']({ limit: 25 })
246
+ ```
247
+
248
+ The host supplies the current token for every request; the client does not store
249
+ it. Queries and responses are validated against the generated contracts, and
250
+ redirects and oversized responses are rejected. The server rechecks the exact
251
+ scope, current organization permissions and tenant access on every call. There
252
+ are no automatic retries. Three explicitly reviewed mutations are available in
253
+ `api.writeOperations`: `messages.send`, `messages.read_state.update` and
254
+ `messages.mute.update`. Supply `{ body: { channel_id, body } }` to send, or
255
+ `{ path: { channel_id }, body: { message_id } }` / `{ path: { channel_id }, body: { is_muted } }`
256
+ to advance read state / mute a channel. The client validates path, payload and response
257
+ contracts and enforces the request byte limit. Each call executes once; reconcile
258
+ an uncertain send by reading the channel before repeating it. Generated MCP tools
259
+ remain reviewed reads only. Run
260
+ `npm run api:generate` from the repository root after changing a reviewed
261
+ contract; `npm run api:check` verifies that generated files are current.
262
+
263
+ ## Maintainer verification and package artifact
137
264
 
138
- ## Verification and package artifact
265
+ From the Vuoma source checkout:
139
266
 
140
267
  ```sh
141
- npm test
142
- npm run verify
268
+ npm run verify --workspace @vuoma/sdk
143
269
  ```
144
270
 
145
271
  Verification tests the actual wire route/header/session boundaries and creates
146
- an installable npm archive under `artifacts/`. It checks the archive's public
147
- file list and loads the packed module in an isolated temporary directory.
148
- The verification command does not publish or attempt registry authentication. The inherited
149
- license remains `UNLICENSED`; `private: false` prepares packaging without granting
150
- open-source licensing rights.
272
+ `packages/sdk/artifacts/vuoma-sdk-0.2.0.tgz`. For pre-publication testing, a
273
+ maintainer can provide that reviewed archive to a consumer, which can install it
274
+ with `npm install ./vendor/vuoma-sdk-reviewed.tgz` and retain it with its lockfile. It checks the archive's public
275
+ file list, installs its declared dependencies in an isolated temporary directory,
276
+ and loads both packed entry points.
277
+ The verification command does not publish or attempt registry authentication. The [Vuoma Connected Application License](./LICENSE) permits use, modification
278
+ and redistribution for authorized Vuoma-connected apps, including commercial
279
+ studio apps and mocked development. It does not license an unrelated replacement
280
+ backend or grant API access. It is a restricted-use license, not an open-source
281
+ license. Include the license in your application's third-party notices.
package/package.json CHANGED
@@ -1,13 +1,24 @@
1
1
  {
2
2
  "name": "@vuoma/sdk",
3
- "version": "0.1.1",
3
+ "version": "0.2.0",
4
4
  "description": "Framework-neutral client for Vuoma public organization and member account APIs",
5
5
  "type": "module",
6
6
  "private": false,
7
- "license": "UNLICENSED",
8
- "files": ["src", "README.md"],
7
+ "license": "SEE LICENSE IN LICENSE",
8
+ "files": [
9
+ "src",
10
+ "README.md",
11
+ "LICENSE"
12
+ ],
9
13
  "exports": {
10
- ".": { "types": "./src/index.d.ts", "import": "./src/index.js" }
14
+ ".": {
15
+ "types": "./src/index.d.ts",
16
+ "import": "./src/index.js"
17
+ },
18
+ "./organization": {
19
+ "types": "./src/organization.d.ts",
20
+ "import": "./src/organization.js"
21
+ }
11
22
  },
12
23
  "types": "./src/index.d.ts",
13
24
  "sideEffects": false,
@@ -16,6 +27,14 @@
16
27
  "verify": "npm test && node scripts/verify-package.mjs",
17
28
  "prepack": "npm test"
18
29
  },
19
- "publishConfig": { "access": "public" },
20
- "engines": { "node": ">=20" }
30
+ "publishConfig": {
31
+ "access": "public"
32
+ },
33
+ "engines": {
34
+ "node": ">=20"
35
+ },
36
+ "dependencies": {
37
+ "ajv": "^8.20.0",
38
+ "ajv-formats": "^3.0.1"
39
+ }
21
40
  }
package/src/index.d.ts CHANGED
@@ -16,16 +16,22 @@ export interface MemberTokenAdapter {
16
16
  getSession(): MemberAuthSession | null | Promise<MemberAuthSession | null>
17
17
  }
18
18
 
19
+ /** For native booking wrappers that hold an access token separately from profile data.
20
+ * Accepted only by booking/visit methods; never accepts platform-account credentials. */
21
+ export interface MemberBookingTokenAdapter {
22
+ getCredential(): { organization_slug: string; access_token: string } | null | Promise<{ organization_slug: string; access_token: string } | null>
23
+ }
24
+
19
25
  export interface VuomaClientOptions {
20
26
  /** Origin only. HTTPS required except HTTP loopback during local development. */
21
27
  apiOrigin: string
22
28
  organizationSlug: string
23
29
  /** Optional stronger client-side tenant check against the member JWT claim. */
24
30
  organizationId?: string
25
- /** Publishable vuoma_pk_ app identity; private calls still require member auth. */
26
- appKey?: string
27
- fetch?: typeof globalThis.fetch
31
+ fetch?: (url: string, init: RequestInit) => Promise<Response>
28
32
  tokenAdapter?: MemberTokenAdapter
33
+ /** Mutually exclusive with tokenAdapter. Profile/commerce reads still require a full session. */
34
+ bookingTokenAdapter?: MemberBookingTokenAdapter
29
35
  /** Default 2 MiB; accepted range 1 KiB–16 MiB. */
30
36
  maximumResponseBytes?: number
31
37
  }
@@ -106,14 +112,61 @@ export interface MemberCommerceSnapshot {
106
112
  orders: ApiRecord[]
107
113
  }
108
114
 
115
+ export type MemberSignupIntent = 'new' | 'migrate' | 'auto' | 'activate'
116
+ export type MemberAccountCodeRequest = RequestOptions & { email: string } & (
117
+ { mode: 'recovery'; signupIntent?: never; displayName?: never } |
118
+ { mode: 'signup'; signupIntent: 'new' | 'auto'; displayName: string } |
119
+ { mode: 'signup'; signupIntent: 'migrate' | 'activate'; displayName?: never }
120
+ )
121
+ export type MemberAccountCodeConfirmation = RequestOptions & { email: string; code: string } & (
122
+ { mode: 'recovery'; signupIntent?: never } | { mode: 'signup'; signupIntent: MemberSignupIntent }
123
+ )
124
+ export interface MemberAccountResolution { kind: 'existing_account' | 'existing_profile' | 'new_profile'; display_name: string; masked_email: string; masked_phone: string | null }
125
+ export interface MemberPasswordCapability { organization_slug: string; value: string; account_resolution?: MemberAccountResolution }
126
+ export interface MemberBookingPolicyAcceptance { policy_version: string; policy_consent: true; payment_authorized: true; payment_authorization_id: string }
127
+ export type MemberClassBookingInput = { service_session_id: string; entitlement_id?: string | null; source_request_id: string } & (MemberBookingPolicyAcceptance | { policy_version?: never; policy_consent?: never; payment_authorized?: never; payment_authorization_id?: never })
128
+ export interface MemberClassBookingResult { api_version: 1; booking: { id: string; status: 'confirmed' | 'waitlisted' | 'cancelled' | 'attended' | 'no_show' }; redemption: { id: string; status: 'waitlisted' | 'reserved' | 'consumed' | 'released' | 'reversed' } }
129
+ export interface MemberCancellationQuote {
130
+ policy_version: string | null; can_cancel: boolean; late: boolean; late_cancellation_deadline: string | null;
131
+ grace_until: string | null; grace_applies: boolean; demand_observed: boolean; fee_amount: number; currency: string;
132
+ reason: 'no_policy' | 'fees_disabled' | 'legacy_booking' | 'on_time' | 'waitlisted' | 'booking_grace' | 'quiet_class' | 'late_fee_if_unfilled' | 'seat_refilled' | 'teacher_assignment' | 'started';
133
+ waive_if_filled: boolean; credit_return: boolean; quoted_at: string; policy_context?: ApiRecord | null
134
+ }
135
+ export interface MemberCancellationQuoteResult { api_version: 1; booking_id: string; quote: MemberCancellationQuote }
136
+ export interface MemberBookingCancellation { api_version: 1; booking_id: string; status: 'cancelled'; canceled: true; cancellation_quote: MemberCancellationQuote | null }
137
+ export interface MemberAppointmentSelection { service_offering_id: string; starts_at: string; ends_at: string; credit_cost: number }
138
+ export interface MemberAppointmentVisitRequest { source_request_id: string; appointments: MemberAppointmentSelection[] }
139
+ export interface MemberAppointmentVisitResult {
140
+ api_version: 1; visit_id: string; source_request_id: string; credit_cost: number;
141
+ bookings: Array<MemberAppointmentSelection & { booking_id: string; service_session_id: string; status: 'confirmed' | 'cancelled' | 'attended' | 'no_show' }>
142
+ }
143
+ export interface MemberLocation { public_id: string; name: string; timezone: string; city: string | null; region: string | null }
144
+ export interface MemberAppointmentCandidate extends MemberAppointmentSelection {
145
+ candidate_id: string; service_id: string; duration_minutes: number; location: MemberLocation;
146
+ service: { id: string; name: string; description: string | null }
147
+ }
148
+ export interface MemberPublicAvailability { api_version: 1; generated_at: string; range: { from: string; to: string }; slots: MemberAppointmentCandidate[]; has_more: boolean }
149
+ export interface MemberBookingCatalog {
150
+ api_version: 1; generated_at: string; cancellation_notice_minutes?: number;
151
+ experience: { classes: boolean; appointments: boolean; default_view: 'classes' | 'appointments' };
152
+ services: Array<{ id: string; name: string; description: string | null; offerings: Array<{ id: string; duration_minutes: number; location_public_id: string; credit_cost: number | null }> }>;
153
+ locations: MemberLocation[]
154
+ }
155
+
109
156
  export interface VuomaClient {
110
157
  readonly public: {
111
158
  getBootstrap(options?: RequestOptions & { includeEvents?: boolean }): Promise<MemberBootstrap>
112
159
  /** Returns one server range. If has_more is true, split the range and request again. */
113
160
  getSchedule(options: RequestOptions & { from: string; to: string; includeEvents?: boolean; fresh?: boolean }): Promise<MemberPublicSchedule>
161
+ getBookingCatalog(options?: RequestOptions): Promise<MemberBookingCatalog>
162
+ /** One half-open range, up to 14 days. Split when has_more is true. */
163
+ getAppointmentAvailability(options: RequestOptions & { from: string; to: string }): Promise<MemberPublicAvailability>
114
164
  getCatalog(options?: RequestOptions & { includeCreditPurchases?: boolean }): Promise<MemberPublicCatalog>
115
165
  }
116
166
  readonly auth: {
167
+ requestAccountCode(options: MemberAccountCodeRequest): Promise<{ ok: true; mode: 'signup' | 'recovery'; requires_email_code: true }>
168
+ confirmAccountCode(options: MemberAccountCodeConfirmation): Promise<MemberPasswordCapability>
169
+ completePassword(options: RequestOptions & { capability: MemberPasswordCapability; password: string }): Promise<void>
117
170
  /** Returns credentials to the host; the SDK neither stores nor installs them. */
118
171
  signIn(options: RequestOptions & { email: string; password: string }): Promise<MemberAuthSession>
119
172
  refresh(options: RequestOptions & { refreshToken: string }): Promise<MemberAuthSession>
@@ -121,6 +174,14 @@ export interface VuomaClient {
121
174
  signOut(options: RequestOptions & { refreshToken: string }): Promise<void>
122
175
  }
123
176
  readonly member: {
177
+ /** Executes once. Retain source_request_id and the exact payload for uncertain-result recovery. */
178
+ bookClass(input: MemberClassBookingInput, options?: RequestOptions): Promise<MemberClassBookingResult>
179
+ getCancellationQuote(bookingId: string, options?: RequestOptions): Promise<MemberCancellationQuoteResult>
180
+ /** Applies to individual class and appointment bookings. Recheck a changed quote before retrying. */
181
+ cancelBooking(bookingId: string, options?: RequestOptions & { expectedFeeAmount?: number }): Promise<MemberBookingCancellation>
182
+ bookAppointmentVisit(input: MemberAppointmentVisitRequest, options?: RequestOptions): Promise<MemberAppointmentVisitResult>
183
+ /** Read the original receipt using its saved request; validates every returned selection. */
184
+ getAppointmentVisit(input: MemberAppointmentVisitRequest, options?: RequestOptions): Promise<MemberAppointmentVisitResult>
124
185
  getProfile(options?: RequestOptions): Promise<MemberProfile>
125
186
  updateProfile(changes: MemberProfileChanges, options?: RequestOptions): Promise<MemberProfile>
126
187
  getCommerce(options?: RequestOptions): Promise<MemberCommerceSnapshot>