@productcraft/auth 1.0.0 → 1.1.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/api.md CHANGED
@@ -15,8 +15,11 @@ Create a new app owned by the caller.
15
15
 
16
16
  The new app starts with emails to its users turned on: they go out from the workspace default sender chosen in Mail, and the default verification, welcome, password-reset, invite and sign-in code templates are created in the workspace's Mail templates (an existing template with the same name is reused). Change or turn this off with `PATCH /v1/apps/{appId}/notification-settings`.
17
17
 
18
+ **Sign-up starts off.** A new app has `signup_enabled: false`, so `POST /{app_slug}/v1/auth/signup` answers 403 `FORBIDDEN` ("Signup is disabled for this app") until you turn it on with `PATCH /v1/apps/{app_id}/auth-config` and `{ "signup_enabled": true }`. Sign-in, invites and admin-created users work either way.
19
+
18
20
  - Params: `Auth.AppCreateParams`
19
21
  - Idempotent: an `Idempotency-Key` is sent automatically and reused on retries
22
+ - Credential: [`workspace_key`](#workspace_key)
20
23
 
21
24
  | Status | Error class | Codes | When |
22
25
  |---|---|---|---|
@@ -42,6 +45,8 @@ const app = await auth.apps.create({
42
45
 
43
46
  Delete an app permanently.
44
47
 
48
+ - Credential: [`workspace_key`](#workspace_key)
49
+
45
50
  | Status | Error class | Codes | When |
46
51
  |---|---|---|---|
47
52
  | 401 | `AuthenticationError` | `UNAUTHORIZED` | Missing, invalid or expired bearer token. |
@@ -50,7 +55,7 @@ Delete an app permanently.
50
55
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
51
56
 
52
57
  ```ts
53
- await auth.apps.delete("app");
58
+ await auth.apps.delete("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f");
54
59
  ```
55
60
 
56
61
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#delete-v1-apps-app-id)
@@ -63,6 +68,7 @@ List apps in workspaces the caller is a member of. Pass `workspace_id` to scope
63
68
 
64
69
  - Params: `Auth.AppListParams`
65
70
  - Paginated: `for await` iterates every item; `await` gives one page
71
+ - Credential: [`workspace_key`](#workspace_key)
66
72
 
67
73
  | Status | Error class | Codes | When |
68
74
  |---|---|---|---|
@@ -85,6 +91,8 @@ const page = await auth.apps.list({}, { timeout: 5_000 });
85
91
 
86
92
  Get app details.
87
93
 
94
+ - Credential: [`workspace_key`](#workspace_key)
95
+
88
96
  | Status | Error class | Codes | When |
89
97
  |---|---|---|---|
90
98
  | 401 | `AuthenticationError` | `UNAUTHORIZED` | Missing or invalid bearer token. |
@@ -93,7 +101,7 @@ Get app details.
93
101
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
94
102
 
95
103
  ```ts
96
- const app = await auth.apps.retrieve("app");
104
+ const app = await auth.apps.retrieve("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f");
97
105
  ```
98
106
 
99
107
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#get-v1-apps-app-id)
@@ -105,6 +113,7 @@ const app = await auth.apps.retrieve("app");
105
113
  Update app display name, slug, metadata, and retention settings.
106
114
 
107
115
  - Params: `Auth.AppUpdateParams`
116
+ - Credential: [`workspace_key`](#workspace_key)
108
117
 
109
118
  | Status | Error class | Codes | When |
110
119
  |---|---|---|---|
@@ -113,7 +122,7 @@ Update app display name, slug, metadata, and retention settings.
113
122
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
114
123
 
115
124
  ```ts
116
- const app = await auth.apps.update("app", { audit_retention_days: 365 });
125
+ const app = await auth.apps.update("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f", { audit_retention_days: 365 });
117
126
  ```
118
127
 
119
128
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#patch-v1-apps-app-id)
@@ -129,6 +138,7 @@ Aggregate counts for the signed-in PlatformUser.
129
138
  Returns total Auth apps owned by workspaces the caller is a member of, end-users across those apps, and active API keys across those apps. Pass `workspace_id` to scope the counts to a single workspace — required from the dashboard so cross-tenant data does not bleed into the totals.
130
139
 
131
140
  - Params: `Auth.StatsMeParams`
141
+ - Credential: [`workspace_key`](#workspace_key)
132
142
 
133
143
  | Status | Error class | Codes | When |
134
144
  |---|---|---|---|
@@ -154,6 +164,7 @@ Active end users over 1, 7 and 30 days, total and new end users, production app
154
164
  Days are UTC. Counts only: no end-user identifiers. Cached for up to 60 seconds (`generated_at`).
155
165
 
156
166
  - Params: `Auth.StatsRetrieveParams`
167
+ - Credential: [`workspace_key`](#workspace_key)
157
168
 
158
169
  | Status | Error class | Codes | When |
159
170
  |---|---|---|---|
@@ -175,6 +186,10 @@ const authWorkspaceStats = await auth.stats.retrieve();
175
186
 
176
187
  Verifies the signature and returns `Auth.UnwrapWebhookEvent`. Events: `app.slug_changed`, `tenant.created`, `tenant.deleted`, `tenant.invite.accepted`, `tenant.invite.created`, `tenant.invite.revoked`, `tenant.member.added`, `tenant.member.removed`, `tenant.member.role_changed`, `tenant.updated`, `user.contact_verified`, `user.deactivated`, `user.deleted`, `user.email_verified`, `user.mfa.enrollment_required`, `user.mfa.factor_disabled`, `user.mfa.factor_enrolled`, `user.mfa.recovery_code_used`, `user.mfa.reenrollment_required`, `user.mfa.step_up_locked`, `user.mfa.step_up_succeeded`, `user.password_reset.completed`, `user.password_reset.requested`, `user.reinstated`, `user.role_changed`, `user.signin`, `user.signup`, `user.suspended`, plus `UnknownWebhookEvent` for types added later.
177
188
 
189
+ ### `auth.webhooks.sign({ payload, secret?, id?, timestamp? })`
190
+
191
+ Signs a test event exactly as ProductCraft signs deliveries (Standard Webhooks) and returns `{ payload, headers }`, ready for `unwrap` or a request to your handler. For tests; real deliveries come signed.
192
+
178
193
  ## admin.app(…)
179
194
 
180
195
  ### `auth.admin.app(…).clone(params, options?)`
@@ -185,6 +200,7 @@ Create a second app in the same workspace whose configuration matches this one
185
200
 
186
201
  - Params: `Auth.CloneParams`
187
202
  - Idempotent: an `Idempotency-Key` is sent automatically and reused on retries
203
+ - Credential: [`workspace_key`](#workspace_key)
188
204
 
189
205
  | Status | Error class | Codes | When |
190
206
  |---|---|---|---|
@@ -196,7 +212,7 @@ Create a second app in the same workspace whose configuration matches this one
196
212
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
197
213
 
198
214
  ```ts
199
- const cloneApp = await auth.admin.app("app").clone({ environment: "staging", slug: "acme-app-staging" });
215
+ const cloneApp = await auth.admin.app("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f").clone({ environment: "staging", slug: "acme-app-staging" });
200
216
  ```
201
217
 
202
218
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#post-v1-apps-app-id-clone)
@@ -208,6 +224,7 @@ const cloneApp = await auth.admin.app("app").clone({ environment: "staging", slu
208
224
  Update app status (active, suspended, archived).
209
225
 
210
226
  - Params: `Auth.UpdateStatusParams`
227
+ - Credential: [`workspace_key`](#workspace_key)
211
228
 
212
229
  | Status | Error class | Codes | When |
213
230
  |---|---|---|---|
@@ -218,7 +235,7 @@ Update app status (active, suspended, archived).
218
235
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
219
236
 
220
237
  ```ts
221
- const app = await auth.admin.app("app").updateStatus({ status: "suspended" });
238
+ const app = await auth.admin.app("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f").updateStatus({ status: "suspended" });
222
239
  ```
223
240
 
224
241
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#patch-v1-apps-app-id-status)
@@ -233,6 +250,7 @@ Mint a new hdk_live_* API key with an explicit permission scope. The raw key is
233
250
 
234
251
  - Params: `Auth.ApiKeyCreateParams`
235
252
  - Idempotent: an `Idempotency-Key` is sent automatically and reused on retries
253
+ - Credential: [`workspace_key`](#workspace_key)
236
254
 
237
255
  | Status | Error class | Codes | When |
238
256
  |---|---|---|---|
@@ -244,7 +262,7 @@ Mint a new hdk_live_* API key with an explicit permission scope. The raw key is
244
262
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
245
263
 
246
264
  ```ts
247
- const createApiKey = await auth.admin.app("app").apiKeys.create({ name: "CI deploy bot", permissions: ["user.read", "user.list"] });
265
+ const createApiKey = await auth.admin.app("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f").apiKeys.create({ name: "CI deploy bot", permissions: ["user.read", "user.list"] });
248
266
  ```
249
267
 
250
268
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#post-v1-apps-app-id-api-keys)
@@ -255,6 +273,8 @@ const createApiKey = await auth.admin.app("app").apiKeys.create({ name: "CI depl
255
273
 
256
274
  Revoke an API key. The key stops authenticating immediately; the row is kept (soft delete) so the list and the audit trail can still answer who revoked it and when.
257
275
 
276
+ - Credential: [`workspace_key`](#workspace_key)
277
+
258
278
  | Status | Error class | Codes | When |
259
279
  |---|---|---|---|
260
280
  | 401 | `AuthenticationError` | `UNAUTHORIZED` | Missing, invalid or expired bearer token. |
@@ -262,7 +282,7 @@ Revoke an API key. The key stops authenticating immediately; the row is kept (so
262
282
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
263
283
 
264
284
  ```ts
265
- await auth.admin.app("app").apiKeys.delete("key");
285
+ await auth.admin.app("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f").apiKeys.delete("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f");
266
286
  ```
267
287
 
268
288
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#delete-v1-apps-app-id-api-keys-key-id)
@@ -275,6 +295,7 @@ List API keys for an app (no raw secret), newest first. Cursor-paginated: 20/pag
275
295
 
276
296
  - Params: `Auth.ApiKeyListParams`
277
297
  - Paginated: `for await` iterates every item; `await` gives one page
298
+ - Credential: [`workspace_key`](#workspace_key)
278
299
 
279
300
  | Status | Error class | Codes | When |
280
301
  |---|---|---|---|
@@ -283,10 +304,10 @@ List API keys for an app (no raw secret), newest first. Cursor-paginated: 20/pag
283
304
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
284
305
 
285
306
  ```ts
286
- for await (const apiKey of auth.admin.app("app").apiKeys.list()) {
307
+ for await (const apiKey of auth.admin.app("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f").apiKeys.list()) {
287
308
  console.log(apiKey);
288
309
  }
289
- const page = await auth.admin.app("app").apiKeys.list({}, { timeout: 5_000 });
310
+ const page = await auth.admin.app("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f").apiKeys.list({}, { timeout: 5_000 });
290
311
  ```
291
312
 
292
313
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#get-v1-apps-app-id-api-keys)
@@ -299,6 +320,7 @@ Mint a replacement for an existing key, carrying its name and permissions over.
299
320
 
300
321
  - Params: `Auth.ApiKeyRotateParams`
301
322
  - Idempotent: an `Idempotency-Key` is sent automatically and reused on retries
323
+ - Credential: [`workspace_key`](#workspace_key)
302
324
 
303
325
  | Status | Error class | Codes | When |
304
326
  |---|---|---|---|
@@ -308,7 +330,7 @@ Mint a replacement for an existing key, carrying its name and permissions over.
308
330
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
309
331
 
310
332
  ```ts
311
- const rotateApiKey = await auth.admin.app("app").apiKeys.rotate("key", { grace_hours: 24 });
333
+ const rotateApiKey = await auth.admin.app("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f").apiKeys.rotate("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f", { grace_hours: 24 });
312
334
  ```
313
335
 
314
336
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#post-v1-apps-app-id-api-keys-key-id-rotate)
@@ -324,6 +346,7 @@ Stream this app's audit log as NDJSON or CSV, applying the same `?action=` / `?a
324
346
  The response is streamed: rows are walked in keyset batches and written as they are read, so neither the service nor the client has to hold the whole result set. Works on both lanes — a platform bearer token and a `pcft_live_*` Platform API Key with `auth.audit.read` hit the same route. Hard cap of 1,000,000 rows per call; a wider request is refused with 413 before any body is written, so a truncated file can never masquerade as a complete one.
325
347
 
326
348
  - Params: `Auth.AuditLogExportParams`
349
+ - Credential: [`workspace_key`](#workspace_key)
327
350
 
328
351
  | Status | Error class | Codes | When |
329
352
  |---|---|---|---|
@@ -335,7 +358,7 @@ The response is streamed: rows are walked in keyset batches and written as they
335
358
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
336
359
 
337
360
  ```ts
338
- const response = await auth.admin.app("app").auditLogs.export();
361
+ const response = await auth.admin.app("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f").auditLogs.export();
339
362
  const text = await response.text();
340
363
  ```
341
364
 
@@ -349,6 +372,7 @@ Append-only audit log for this app, newest first. Filter by `?action=`, `?actor_
349
372
 
350
373
  - Params: `Auth.AuditLogListParams`
351
374
  - Paginated: `for await` iterates every item; `await` gives one page
375
+ - Credential: [`workspace_key`](#workspace_key)
352
376
 
353
377
  | Status | Error class | Codes | When |
354
378
  |---|---|---|---|
@@ -359,10 +383,10 @@ Append-only audit log for this app, newest first. Filter by `?action=`, `?actor_
359
383
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
360
384
 
361
385
  ```ts
362
- for await (const auditLog of auth.admin.app("app").auditLogs.list()) {
386
+ for await (const auditLog of auth.admin.app("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f").auditLogs.list()) {
363
387
  console.log(auditLog);
364
388
  }
365
- const page = await auth.admin.app("app").auditLogs.list({}, { timeout: 5_000 });
389
+ const page = await auth.admin.app("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f").auditLogs.list({}, { timeout: 5_000 });
366
390
  ```
367
391
 
368
392
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#get-v1-apps-app-id-audit-logs)
@@ -375,6 +399,8 @@ const page = await auth.admin.app("app").auditLogs.list({}, { timeout: 5_000 });
375
399
 
376
400
  Remove the audit sink. Shipping stops immediately; the audit log itself is untouched.
377
401
 
402
+ - Credential: [`workspace_key`](#workspace_key)
403
+
378
404
  | Status | Error class | Codes | When |
379
405
  |---|---|---|---|
380
406
  | 401 | `AuthenticationError` | `UNAUTHORIZED` | Missing, invalid or expired bearer token. |
@@ -383,7 +409,7 @@ Remove the audit sink. Shipping stops immediately; the audit log itself is untou
383
409
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
384
410
 
385
411
  ```ts
386
- await auth.admin.app("app").auditSink.disconnect();
412
+ await auth.admin.app("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f").auditSink.disconnect();
387
413
  ```
388
414
 
389
415
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#delete-v1-apps-app-id-audit-sink)
@@ -397,6 +423,7 @@ Create or replace the audit sink. Returns a freshly generated signing secret exa
397
423
  Audit rows are POSTed in batches of up to 100, signed with the same `X-Auth-Signature: t=<unix>,v1=<hex>` HMAC header as the outbound webhooks. Delivery is at-least-once: the cursor advances only after a 2xx, so a batch that times out after the receiver processed it is re-sent. Every record carries its immutable `audit_log` id, so dedupe on that. A new sink starts from the moment it is created — configuring one does not replay the existing history into your SIEM. Replacing an existing sink clears the auto-disabled state (the same "I fixed my receiver" signal a webhook PUT carries) but does NOT rewind the cursor.
398
424
 
399
425
  - Params: `Auth.AuditSinkReplaceParams`
426
+ - Credential: [`workspace_key`](#workspace_key)
400
427
 
401
428
  | Status | Error class | Codes | When |
402
429
  |---|---|---|---|
@@ -407,7 +434,7 @@ Audit rows are POSTed in batches of up to 100, signed with the same `X-Auth-Sign
407
434
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
408
435
 
409
436
  ```ts
410
- const auditSinkWithSecret = await auth.admin.app("app").auditSink.replace({ url: "https://siem.customer.example.com/ingest/productcraft-auth" });
437
+ const auditSinkWithSecret = await auth.admin.app("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f").auditSink.replace({ url: "https://siem.customer.example.com/ingest/productcraft-auth" });
411
438
  ```
412
439
 
413
440
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#put-v1-apps-app-id-audit-sink)
@@ -418,6 +445,8 @@ const auditSinkWithSecret = await auth.admin.app("app").auditSink.replace({ url:
418
445
 
419
446
  Current audit sink for this app, or 404 when none is configured. Never returns the signing secret — only its last-4 hint.
420
447
 
448
+ - Credential: [`workspace_key`](#workspace_key)
449
+
421
450
  | Status | Error class | Codes | When |
422
451
  |---|---|---|---|
423
452
  | 401 | `AuthenticationError` | `UNAUTHORIZED` | Missing, invalid or expired bearer token. |
@@ -426,7 +455,7 @@ Current audit sink for this app, or 404 when none is configured. Never returns t
426
455
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
427
456
 
428
457
  ```ts
429
- const auditSink = await auth.admin.app("app").auditSink.retrieve();
458
+ const auditSink = await auth.admin.app("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f").auditSink.retrieve();
430
459
  ```
431
460
 
432
461
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#get-v1-apps-app-id-audit-sink)
@@ -439,6 +468,8 @@ Issue a new signing secret, returned exactly once.
439
468
 
440
469
  The new secret signs every batch from now on; the old one stops at once. Update your receiver right away: batches it rejects in between are retried every minute (the position only moves after a 2xx), so nothing is lost, but export stops after 20 failures in a row. Clears the receiver-test proof.
441
470
 
471
+ - Credential: [`workspace_key`](#workspace_key)
472
+
442
473
  | Status | Error class | Codes | When |
443
474
  |---|---|---|---|
444
475
  | 401 | `AuthenticationError` | `UNAUTHORIZED` | Missing, invalid or expired bearer token. |
@@ -447,7 +478,7 @@ The new secret signs every batch from now on; the old one stops at once. Update
447
478
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
448
479
 
449
480
  ```ts
450
- const auditSinkWithSecret = await auth.admin.app("app").auditSink.rotateSecret();
481
+ const auditSinkWithSecret = await auth.admin.app("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f").auditSink.rotateSecret();
451
482
  ```
452
483
 
453
484
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#post-v1-apps-app-id-audit-sink-rotate-secret)
@@ -460,6 +491,8 @@ Get the audit export checklist: destination, receiver test and delivery.
460
491
 
461
492
  Rows, in order: `destination` (not_set until a sink exists), `test_batch` (pass once the receiver accepted a test batch with the current address and secret; blocked when the last test was refused; carries `test.action: audit_sink.test`, run with `POST /v1/apps/{appId}/audit-sink/test`), and `delivery` (blocked when export stopped after repeated failures, attention while paused or while recent batches fail). Computed from the stored sink on every request; answers 200 whether or not a sink exists.
462
493
 
494
+ - Credential: [`workspace_key`](#workspace_key)
495
+
463
496
  | Status | Error class | Codes | When |
464
497
  |---|---|---|---|
465
498
  | 401 | `AuthenticationError` | `UNAUTHORIZED` | Missing, invalid or expired bearer token. |
@@ -468,7 +501,7 @@ Rows, in order: `destination` (not_set until a sink exists), `test_batch` (pass
468
501
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
469
502
 
470
503
  ```ts
471
- const setupStatus = await auth.admin.app("app").auditSink.setupStatus();
504
+ const setupStatus = await auth.admin.app("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f").auditSink.setupStatus();
472
505
  ```
473
506
 
474
507
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#get-v1-apps-app-id-audit-sink-setup-status)
@@ -482,6 +515,7 @@ Send a test batch to the receiver now and report what it answered.
482
515
  POSTs one signed batch, shaped exactly like a real one (`type: audit.batch`, same headers and signature), with `test: true` and a single synthetic record whose `id` starts with `test_`. Works while export is paused or stopped, so a fix can be proven before export is turned back on. The outcome is recorded on the sink (`last_attempt_*`, `last_test_succeeded_at`) but never moves the position in the audit log or counts toward the automatic stop. A refused test is still a 200 with `delivered: false`. At most 20 tests per app per hour.
483
516
 
484
517
  - Idempotent: an `Idempotency-Key` is sent automatically and reused on retries
518
+ - Credential: [`workspace_key`](#workspace_key)
485
519
 
486
520
  | Status | Error class | Codes | When |
487
521
  |---|---|---|---|
@@ -492,7 +526,7 @@ POSTs one signed batch, shaped exactly like a real one (`type: audit.batch`, sam
492
526
  | 429 | `RateLimitError` | `RATE_LIMITED` | More than 20 tests for this app in the last hour (`RATE_LIMITED`). |
493
527
 
494
528
  ```ts
495
- const auditSinkTestResult = await auth.admin.app("app").auditSink.test();
529
+ const auditSinkTestResult = await auth.admin.app("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f").auditSink.test();
496
530
  ```
497
531
 
498
532
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#post-v1-apps-app-id-audit-sink-test)
@@ -506,6 +540,7 @@ Change the address or switch export on or off, keeping the signing secret.
506
540
  Send `url`, `enabled` or both. `enabled: false` pauses export; `enabled: true` resumes it and clears a stop after repeated failures and the failure count. The position in the audit log is never moved: nothing is skipped and nothing is replayed twice on purpose. A new `url` is checked by the same address guard as `PUT`.
507
541
 
508
542
  - Params: `Auth.AuditSinkUpdateParams`
543
+ - Credential: [`workspace_key`](#workspace_key)
509
544
 
510
545
  | Status | Error class | Codes | When |
511
546
  |---|---|---|---|
@@ -516,7 +551,7 @@ Send `url`, `enabled` or both. `enabled: false` pauses export; `enabled: true` r
516
551
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
517
552
 
518
553
  ```ts
519
- const auditSink = await auth.admin.app("app").auditSink.update({ enabled: true });
554
+ const auditSink = await auth.admin.app("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f").auditSink.update({ enabled: true });
520
555
  ```
521
556
 
522
557
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#patch-v1-apps-app-id-audit-sink)
@@ -529,6 +564,8 @@ const auditSink = await auth.admin.app("app").auditSink.update({ enabled: true }
529
564
 
530
565
  Get the per-app auth config (signup, signin, password policy, sessions).
531
566
 
567
+ - Credential: [`workspace_key`](#workspace_key)
568
+
532
569
  | Status | Error class | Codes | When |
533
570
  |---|---|---|---|
534
571
  | 401 | `AuthenticationError` | `UNAUTHORIZED` | Missing or invalid bearer token. |
@@ -537,7 +574,7 @@ Get the per-app auth config (signup, signin, password policy, sessions).
537
574
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
538
575
 
539
576
  ```ts
540
- const authConfig = await auth.admin.app("app").authConfig.retrieve();
577
+ const authConfig = await auth.admin.app("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f").authConfig.retrieve();
541
578
  ```
542
579
 
543
580
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#get-v1-apps-app-id-auth-config)
@@ -549,6 +586,7 @@ const authConfig = await auth.admin.app("app").authConfig.retrieve();
549
586
  Update the per-app auth config. Only fields you pass are changed.
550
587
 
551
588
  - Params: `Auth.AuthConfigUpdateParams`
589
+ - Credential: [`workspace_key`](#workspace_key)
552
590
 
553
591
  | Status | Error class | Codes | When |
554
592
  |---|---|---|---|
@@ -560,7 +598,7 @@ Update the per-app auth config. Only fields you pass are changed.
560
598
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
561
599
 
562
600
  ```ts
563
- const authConfig = await auth.admin.app("app").authConfig.update({ access_token_minutes: 60 });
601
+ const authConfig = await auth.admin.app("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f").authConfig.update({ access_token_minutes: 60 });
564
602
  ```
565
603
 
566
604
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#patch-v1-apps-app-id-auth-config)
@@ -575,6 +613,8 @@ Check the saved provider credentials with the provider.
575
613
 
576
614
  Calls the provider's token endpoint with a dummy sign-in code. If the provider refuses the client (`invalid_client`, GitHub `incorrect_client_credentials` or `unknown_client`), the result is `credentials_rejected` and the provider shows as `blocked` with reason `credentials_rejected` until the credentials are fixed. If it only refuses the code (`invalid_grant`, `bad_verification_code`), the credentials are valid. Nobody is signed in. The result is saved on the provider (`last_check_*`). The same check runs automatically whenever the credentials are saved. Works for a provider that is saved but not switched on yet.
577
615
 
616
+ - Credential: [`workspace_key`](#workspace_key)
617
+
578
618
  | Status | Error class | Codes | When |
579
619
  |---|---|---|---|
580
620
  | 400 | `BadRequestError` | `BAD_REQUEST`, `VALIDATION_ERROR` | Unknown provider id, or the saved config can no longer be read. |
@@ -584,7 +624,7 @@ Calls the provider's token endpoint with a dummy sign-in code. If the provider r
584
624
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
585
625
 
586
626
  ```ts
587
- const providerCheck = await auth.admin.app("app").authConfig.providers.check("provider");
627
+ const providerCheck = await auth.admin.app("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f").authConfig.providers.check("google");
588
628
  ```
589
629
 
590
630
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#post-v1-apps-app-id-auth-config-providers-provider-check)
@@ -595,6 +635,8 @@ const providerCheck = await auth.admin.app("app").authConfig.providers.check("pr
595
635
 
596
636
  Remove the config for an (app, provider). Existing identity rows (people who already signed in with this provider) stay; they just lose the ability to sign in again until config is re-uploaded.
597
637
 
638
+ - Credential: [`workspace_key`](#workspace_key)
639
+
598
640
  | Status | Error class | Codes | When |
599
641
  |---|---|---|---|
600
642
  | 400 | `BadRequestError` | `BAD_REQUEST`, `VALIDATION_ERROR` | Unknown provider id. |
@@ -602,7 +644,7 @@ Remove the config for an (app, provider). Existing identity rows (people who alr
602
644
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
603
645
 
604
646
  ```ts
605
- await auth.admin.app("app").authConfig.providers.delete("provider");
647
+ await auth.admin.app("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f").authConfig.providers.delete("google");
606
648
  ```
607
649
 
608
650
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#delete-v1-apps-app-id-auth-config-providers-provider)
@@ -615,6 +657,8 @@ List federated sign-in providers Auth supports, with their setup status for this
615
657
 
616
658
  Each entry carries `status` (`ready` | `blocked` | `disabled` | `not_configured`) and `blocked_reasons`, computed on the server with the same rule the web sign-in flow enforces, so a provider shown as `ready` is one people can actually sign in with.
617
659
 
660
+ - Credential: [`workspace_key`](#workspace_key)
661
+
618
662
  | Status | Error class | Codes | When |
619
663
  |---|---|---|---|
620
664
  | 401 | `AuthenticationError` | `UNAUTHORIZED` | Missing or invalid bearer token. |
@@ -623,7 +667,7 @@ Each entry carries `status` (`ready` | `blocked` | `disabled` | `not_configured`
623
667
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
624
668
 
625
669
  ```ts
626
- const result = await auth.admin.app("app").authConfig.providers.list();
670
+ const result = await auth.admin.app("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f").authConfig.providers.list();
627
671
  ```
628
672
 
629
673
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#get-v1-apps-app-id-auth-config-providers)
@@ -635,6 +679,7 @@ const result = await auth.admin.app("app").authConfig.providers.list();
635
679
  Write or replace the config for an (app, provider). Validates the body via the provider impl's `parseConfig` before encrypting; malformed config 400s. Default `enabled=false`.
636
680
 
637
681
  - Params: `Auth.AuthConfigProviderReplaceParams`
682
+ - Credential: [`workspace_key`](#workspace_key)
638
683
 
639
684
  | Status | Error class | Codes | When |
640
685
  |---|---|---|---|
@@ -643,7 +688,7 @@ Write or replace the config for an (app, provider). Validates the body via the p
643
688
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
644
689
 
645
690
  ```ts
646
- const providerDetail = await auth.admin.app("app").authConfig.providers.replace("provider", { config: {} });
691
+ const providerDetail = await auth.admin.app("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f").authConfig.providers.replace("google", { config: {} });
647
692
  ```
648
693
 
649
694
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#put-v1-apps-app-id-auth-config-providers-provider)
@@ -654,6 +699,8 @@ const providerDetail = await auth.admin.app("app").authConfig.providers.replace(
654
699
 
655
700
  Get the config for one (app, provider). Secrets are redacted — `private_key_present: true` is the only acknowledgement that a private key is on file.
656
701
 
702
+ - Credential: [`workspace_key`](#workspace_key)
703
+
657
704
  | Status | Error class | Codes | When |
658
705
  |---|---|---|---|
659
706
  | 400 | `BadRequestError` | `BAD_REQUEST`, `VALIDATION_ERROR` | Unknown provider id. |
@@ -662,7 +709,7 @@ Get the config for one (app, provider). Secrets are redacted — `private_key_pr
662
709
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
663
710
 
664
711
  ```ts
665
- const providerDetail = await auth.admin.app("app").authConfig.providers.retrieve("provider");
712
+ const providerDetail = await auth.admin.app("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f").authConfig.providers.retrieve("google");
666
713
  ```
667
714
 
668
715
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#get-v1-apps-app-id-auth-config-providers-provider)
@@ -674,6 +721,7 @@ const providerDetail = await auth.admin.app("app").authConfig.providers.retrieve
674
721
  Partially update the config for an (app, provider). Only top-level keys present in `config` are changed; omitted keys — secrets included — keep their stored values. The merged config is validated by the provider impl's `parseConfig`; 404 if the provider is not configured yet (create it with PUT).
675
722
 
676
723
  - Params: `Auth.AuthConfigProviderUpdateParams`
724
+ - Credential: [`workspace_key`](#workspace_key)
677
725
 
678
726
  | Status | Error class | Codes | When |
679
727
  |---|---|---|---|
@@ -683,7 +731,7 @@ Partially update the config for an (app, provider). Only top-level keys present
683
731
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
684
732
 
685
733
  ```ts
686
- const providerDetail = await auth.admin.app("app").authConfig.providers.update("provider", { config: {} });
734
+ const providerDetail = await auth.admin.app("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f").authConfig.providers.update("google", { config: {} });
687
735
  ```
688
736
 
689
737
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#patch-v1-apps-app-id-auth-config-providers-provider)
@@ -698,6 +746,7 @@ Create a new M2M client.
698
746
 
699
747
  - Params: `Auth.CredentialCreateParams`
700
748
  - Idempotent: an `Idempotency-Key` is sent automatically and reused on retries
749
+ - Credential: [`workspace_key`](#workspace_key)
701
750
 
702
751
  | Status | Error class | Codes | When |
703
752
  |---|---|---|---|
@@ -709,7 +758,7 @@ Create a new M2M client.
709
758
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
710
759
 
711
760
  ```ts
712
- const m2mClientCreate = await auth.admin.app("app").credentials.create({ name: "CI Pipeline" });
761
+ const m2mClientCreate = await auth.admin.app("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f").credentials.create({ name: "CI Pipeline" });
713
762
  ```
714
763
 
715
764
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#post-v1-apps-app-id-credentials)
@@ -720,6 +769,8 @@ const m2mClientCreate = await auth.admin.app("app").credentials.create({ name: "
720
769
 
721
770
  Delete an M2M client.
722
771
 
772
+ - Credential: [`workspace_key`](#workspace_key)
773
+
723
774
  | Status | Error class | Codes | When |
724
775
  |---|---|---|---|
725
776
  | 401 | `AuthenticationError` | `UNAUTHORIZED` | Missing, invalid or expired bearer token. |
@@ -728,7 +779,7 @@ Delete an M2M client.
728
779
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
729
780
 
730
781
  ```ts
731
- await auth.admin.app("app").credentials.delete("client");
782
+ await auth.admin.app("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f").credentials.delete("m2m_a1b2c3d4e5f6");
732
783
  ```
733
784
 
734
785
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#delete-v1-apps-app-id-credentials-client-id)
@@ -741,6 +792,7 @@ List M2M clients.
741
792
 
742
793
  - Params: `Auth.CredentialListParams`
743
794
  - Paginated: `for await` iterates every item; `await` gives one page
795
+ - Credential: [`workspace_key`](#workspace_key)
744
796
 
745
797
  | Status | Error class | Codes | When |
746
798
  |---|---|---|---|
@@ -750,10 +802,10 @@ List M2M clients.
750
802
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
751
803
 
752
804
  ```ts
753
- for await (const credential of auth.admin.app("app").credentials.list()) {
805
+ for await (const credential of auth.admin.app("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f").credentials.list()) {
754
806
  console.log(credential);
755
807
  }
756
- const page = await auth.admin.app("app").credentials.list({}, { timeout: 5_000 });
808
+ const page = await auth.admin.app("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f").credentials.list({}, { timeout: 5_000 });
757
809
  ```
758
810
 
759
811
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#get-v1-apps-app-id-credentials)
@@ -764,6 +816,8 @@ const page = await auth.admin.app("app").credentials.list({}, { timeout: 5_000 }
764
816
 
765
817
  Get M2M client details.
766
818
 
819
+ - Credential: [`workspace_key`](#workspace_key)
820
+
767
821
  | Status | Error class | Codes | When |
768
822
  |---|---|---|---|
769
823
  | 401 | `AuthenticationError` | `UNAUTHORIZED` | Missing or invalid bearer token. |
@@ -772,7 +826,7 @@ Get M2M client details.
772
826
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
773
827
 
774
828
  ```ts
775
- const m2mClientDetail = await auth.admin.app("app").credentials.retrieve("client");
829
+ const m2mClientDetail = await auth.admin.app("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f").credentials.retrieve("m2m_a1b2c3d4e5f6");
776
830
  ```
777
831
 
778
832
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#get-v1-apps-app-id-credentials-client-id)
@@ -783,6 +837,8 @@ const m2mClientDetail = await auth.admin.app("app").credentials.retrieve("client
783
837
 
784
838
  Rotate M2M client secret.
785
839
 
840
+ - Credential: [`workspace_key`](#workspace_key)
841
+
786
842
  | Status | Error class | Codes | When |
787
843
  |---|---|---|---|
788
844
  | 401 | `AuthenticationError` | `UNAUTHORIZED` | Missing, invalid or expired bearer token. |
@@ -791,7 +847,7 @@ Rotate M2M client secret.
791
847
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
792
848
 
793
849
  ```ts
794
- const m2mRotateSecret = await auth.admin.app("app").credentials.rotate("client");
850
+ const m2mRotateSecret = await auth.admin.app("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f").credentials.rotate("m2m_a1b2c3d4e5f6");
795
851
  ```
796
852
 
797
853
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#post-v1-apps-app-id-credentials-client-id-rotate)
@@ -803,6 +859,7 @@ const m2mRotateSecret = await auth.admin.app("app").credentials.rotate("client")
803
859
  Update M2M client status.
804
860
 
805
861
  - Params: `Auth.CredentialUpdateParams`
862
+ - Credential: [`workspace_key`](#workspace_key)
806
863
 
807
864
  | Status | Error class | Codes | When |
808
865
  |---|---|---|---|
@@ -813,7 +870,7 @@ Update M2M client status.
813
870
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
814
871
 
815
872
  ```ts
816
- const m2mClientDetail = await auth.admin.app("app").credentials.update("client", { is_active: true });
873
+ const m2mClientDetail = await auth.admin.app("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f").credentials.update("m2m_a1b2c3d4e5f6", { is_active: true });
817
874
  ```
818
875
 
819
876
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#patch-v1-apps-app-id-credentials-client-id)
@@ -825,6 +882,7 @@ const m2mClientDetail = await auth.admin.app("app").credentials.update("client",
825
882
  Set scopes for an M2M client.
826
883
 
827
884
  - Params: `Auth.CredentialUpdateScopesParams`
885
+ - Credential: [`workspace_key`](#workspace_key)
828
886
 
829
887
  | Status | Error class | Codes | When |
830
888
  |---|---|---|---|
@@ -835,7 +893,7 @@ Set scopes for an M2M client.
835
893
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
836
894
 
837
895
  ```ts
838
- const result = await auth.admin.app("app").credentials.updateScopes("client", { permissions: ["user.read", "app.read"] });
896
+ const result = await auth.admin.app("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f").credentials.updateScopes("m2m_a1b2c3d4e5f6", { permissions: ["user.read", "app.read"] });
839
897
  ```
840
898
 
841
899
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#put-v1-apps-app-id-credentials-client-id-scopes)
@@ -852,6 +910,7 @@ Reads the zone through the provider's API and plans both records: `create`, or `
852
910
 
853
911
  - Params: `Auth.CustomDomainApplyParams`
854
912
  - Idempotent: an `Idempotency-Key` is sent automatically and reused on retries
913
+ - Credential: [`workspace_key`](#workspace_key)
855
914
 
856
915
  | Status | Error class | Codes | When |
857
916
  |---|---|---|---|
@@ -864,7 +923,7 @@ Reads the zone through the provider's API and plans both records: `create`, or `
864
923
  | 502 | `InternalServerError` | `BAD_GATEWAY`, `PROVIDER_UNAVAILABLE`, `PROVIDER_ERROR` | The provider failed or did not answer (`PROVIDER_UNAVAILABLE`, `PROVIDER_ERROR`). Nothing was written unless the plan items say so. |
865
924
 
866
925
  ```ts
867
- const customDomainApplyResult = await auth.admin.app("app").customDomains.apply("3f0c1a2b-4c5d-4e6f-8a9b-0c1d2e3f4a5b", { dry_run: true, provider: "cloudflare", token: "cf-token-from-your-dashboard" });
926
+ const customDomainApplyResult = await auth.admin.app("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f").customDomains.apply("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f", { dry_run: true, provider: "cloudflare", token: "cf-token-from-your-dashboard" });
868
927
  ```
869
928
 
870
929
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#post-v1-apps-app-id-custom-domains-domain-id-apply)
@@ -877,6 +936,8 @@ Check the custom domain's DNS records now.
877
936
 
878
937
  Asks the zone's own nameservers and a public resolver for both records, stores each record's state and conflicts, and moves the status: `active` once both are verified; an active domain turns `failing` only when a record is definitely gone or blocked (a DNS outage never takes a working domain down). Safe to repeat.
879
938
 
939
+ - Credential: [`workspace_key`](#workspace_key)
940
+
880
941
  | Status | Error class | Codes | When |
881
942
  |---|---|---|---|
882
943
  | 400 | `BadRequestError` | `BAD_REQUEST`, `VALIDATION_ERROR` | `domainId` is not a UUID. |
@@ -886,7 +947,7 @@ Asks the zone's own nameservers and a public resolver for both records, stores e
886
947
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
887
948
 
888
949
  ```ts
889
- const customDomain = await auth.admin.app("app").customDomains.check("3f0c1a2b-4c5d-4e6f-8a9b-0c1d2e3f4a5b");
950
+ const customDomain = await auth.admin.app("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f").customDomains.check("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f");
890
951
  ```
891
952
 
892
953
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#post-v1-apps-app-id-custom-domains-domain-id-check)
@@ -901,6 +962,7 @@ Returns the two records to publish: a CNAME from the host to `edge_target`, and
901
962
 
902
963
  - Params: `Auth.CustomDomainCreateParams`
903
964
  - Idempotent: an `Idempotency-Key` is sent automatically and reused on retries
965
+ - Credential: [`workspace_key`](#workspace_key)
904
966
 
905
967
  | Status | Error class | Codes | When |
906
968
  |---|---|---|---|
@@ -912,7 +974,7 @@ Returns the two records to publish: a CNAME from the host to `edge_target`, and
912
974
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
913
975
 
914
976
  ```ts
915
- const customDomain = await auth.admin.app("app").customDomains.create({ hostname: "auth.acme.com" });
977
+ const customDomain = await auth.admin.app("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f").customDomains.create({ hostname: "auth.acme.com" });
916
978
  ```
917
979
 
918
980
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#post-v1-apps-app-id-custom-domains)
@@ -925,6 +987,8 @@ Remove the custom domain.
925
987
 
926
988
  The host stops serving this app at once and its certificate is no longer renewed. Your DNS records are not touched: delete the CNAME and the `_productcraft-auth` TXT record at your DNS provider. Sign-ins on the canonical host are unaffected.
927
989
 
990
+ - Credential: [`workspace_key`](#workspace_key)
991
+
928
992
  | Status | Error class | Codes | When |
929
993
  |---|---|---|---|
930
994
  | 400 | `BadRequestError` | `BAD_REQUEST`, `VALIDATION_ERROR` | `domainId` is not a UUID. |
@@ -934,7 +998,7 @@ The host stops serving this app at once and its certificate is no longer renewed
934
998
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
935
999
 
936
1000
  ```ts
937
- await auth.admin.app("app").customDomains.delete("3f0c1a2b-4c5d-4e6f-8a9b-0c1d2e3f4a5b");
1001
+ await auth.admin.app("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f").customDomains.delete("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f");
938
1002
  ```
939
1003
 
940
1004
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#delete-v1-apps-app-id-custom-domains-domain-id)
@@ -948,6 +1012,7 @@ Download the custom domain's DNS records as a BIND zone fragment or a CSV.
948
1012
  `format=bind`: a zone-file fragment (`$ORIGIN <zone>.`, `$TTL 3600`, names relative to the zone, a comment per record with its purpose and any conflict) that Cloudflare and Route 53 import directly. `format=csv`: `type,name,value,priority,ttl,purpose,required`, fully qualified names. By default only records not verified yet; `all=true` for both. Served as an attachment.
949
1013
 
950
1014
  - Params: `Auth.CustomDomainExportParams`
1015
+ - Credential: [`workspace_key`](#workspace_key)
951
1016
 
952
1017
  | Status | Error class | Codes | When |
953
1018
  |---|---|---|---|
@@ -958,7 +1023,7 @@ Download the custom domain's DNS records as a BIND zone fragment or a CSV.
958
1023
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
959
1024
 
960
1025
  ```ts
961
- const response = await auth.admin.app("app").customDomains.export("3f0c1a2b-4c5d-4e6f-8a9b-0c1d2e3f4a5b", { format: "bind" });
1026
+ const response = await auth.admin.app("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f").customDomains.export("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f", { format: "bind" });
962
1027
  const text = await response.text();
963
1028
  ```
964
1029
 
@@ -973,6 +1038,7 @@ List the app's custom domains (at most one).
973
1038
  Each with its status and the last check of its records, plus `available`: whether custom domains are switched on for the workspace. Always a single page.
974
1039
 
975
1040
  - Paginated: `for await` iterates every item; `await` gives one page
1041
+ - Credential: [`workspace_key`](#workspace_key)
976
1042
 
977
1043
  | Status | Error class | Codes | When |
978
1044
  |---|---|---|---|
@@ -982,7 +1048,7 @@ Each with its status and the last check of its records, plus `available`: whethe
982
1048
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
983
1049
 
984
1050
  ```ts
985
- for await (const customDomain of auth.admin.app("app").customDomains.list()) {
1051
+ for await (const customDomain of auth.admin.app("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f").customDomains.list()) {
986
1052
  console.log(customDomain);
987
1053
  }
988
1054
  ```
@@ -997,6 +1063,8 @@ Get a custom domain and the last check of its records.
997
1063
 
998
1064
  Reads stored state only; `POST …/check` runs a new DNS check.
999
1065
 
1066
+ - Credential: [`workspace_key`](#workspace_key)
1067
+
1000
1068
  | Status | Error class | Codes | When |
1001
1069
  |---|---|---|---|
1002
1070
  | 400 | `BadRequestError` | `BAD_REQUEST`, `VALIDATION_ERROR` | `domainId` is not a UUID. |
@@ -1006,7 +1074,7 @@ Reads stored state only; `POST …/check` runs a new DNS check.
1006
1074
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
1007
1075
 
1008
1076
  ```ts
1009
- const customDomain = await auth.admin.app("app").customDomains.retrieve("3f0c1a2b-4c5d-4e6f-8a9b-0c1d2e3f4a5b");
1077
+ const customDomain = await auth.admin.app("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f").customDomains.retrieve("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f");
1010
1078
  ```
1011
1079
 
1012
1080
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#get-v1-apps-app-id-custom-domains-domain-id)
@@ -1019,6 +1087,8 @@ const customDomain = await auth.admin.app("app").customDomains.retrieve("3f0c1a2
1019
1087
 
1020
1088
  Auth health for this app over the last 30 days: verification completion, sign-in failure rate, MFA adoption, password-reset follow-through, and session counts. Rates are null when there is nothing to divide by.
1021
1089
 
1090
+ - Credential: [`workspace_key`](#workspace_key)
1091
+
1022
1092
  | Status | Error class | Codes | When |
1023
1093
  |---|---|---|---|
1024
1094
  | 400 | `BadRequestError` | `BAD_REQUEST`, `VALIDATION_ERROR` | `appId` is not a UUID. |
@@ -1027,7 +1097,7 @@ Auth health for this app over the last 30 days: verification completion, sign-in
1027
1097
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
1028
1098
 
1029
1099
  ```ts
1030
- const endUserInsights = await auth.admin.app("3f0c1a2b-4c5d-4e6f-8a9b-0c1d2e3f4a5b").endUserInsights.retrieve();
1100
+ const endUserInsights = await auth.admin.app("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f").endUserInsights.retrieve();
1031
1101
  ```
1032
1102
 
1033
1103
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#get-v1-apps-app-id-end-user-insights)
@@ -1040,6 +1110,8 @@ const endUserInsights = await auth.admin.app("3f0c1a2b-4c5d-4e6f-8a9b-0c1d2e3f4a
1040
1110
 
1041
1111
  One end-user's recorded activity: their audit trail, their sessions with device/IP, and their enrolled factors. `noActivityRecorded` distinguishes "nothing happened" from "no data".
1042
1112
 
1113
+ - Credential: [`workspace_key`](#workspace_key)
1114
+
1043
1115
  | Status | Error class | Codes | When |
1044
1116
  |---|---|---|---|
1045
1117
  | 400 | `BadRequestError` | `BAD_REQUEST`, `VALIDATION_ERROR` | `appId` or `userId` is not a UUID. |
@@ -1049,7 +1121,7 @@ One end-user's recorded activity: their audit trail, their sessions with device/
1049
1121
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
1050
1122
 
1051
1123
  ```ts
1052
- const endUserDetailInsights = await auth.admin.app("3f0c1a2b-4c5d-4e6f-8a9b-0c1d2e3f4a5b").endUserInsights.users.retrieve("3f0c1a2b-4c5d-4e6f-8a9b-0c1d2e3f4a5b");
1124
+ const endUserDetailInsights = await auth.admin.app("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f").endUserInsights.users.retrieve("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f");
1053
1125
  ```
1054
1126
 
1055
1127
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#get-v1-apps-app-id-end-user-insights-users-user-id)
@@ -1065,6 +1137,7 @@ Bulk-delete accounts across a selection.
1065
1137
  Deletes every matched account in one transaction. Each deleted account gets an `auth.account.deleted` audit row (`via: platform_bulk`) whose metadata carries the `selection` (`{ mode: "ids" }`, or `{ mode: "filter", filter }`), so a filter sweep reads as one. The workspace audit log gets one `auth.end_users.deleted` row with the count. `user.deleted` (`via: "platform_bulk"`) fires per account only when 100 or fewer were deleted.
1066
1138
 
1067
1139
  - Params: `Auth.EndUserBulkDeleteParams`
1140
+ - Credential: [`workspace_key`](#workspace_key)
1068
1141
 
1069
1142
  | Status | Error class | Codes | When |
1070
1143
  |---|---|---|---|
@@ -1075,7 +1148,7 @@ Deletes every matched account in one transaction. Each deleted account gets an `
1075
1148
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
1076
1149
 
1077
1150
  ```ts
1078
- const bulkAffectedUsers = await auth.admin.app("app").endUsers.bulkDelete({ selection: { mode: "ids" } });
1151
+ const bulkAffectedUsers = await auth.admin.app("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f").endUsers.bulkDelete({ selection: { mode: "ids" } });
1079
1152
  ```
1080
1153
 
1081
1154
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#post-v1-apps-app-id-end-users-bulk-delete)
@@ -1087,6 +1160,7 @@ const bulkAffectedUsers = await auth.admin.app("app").endUsers.bulkDelete({ sele
1087
1160
  Bulk-revoke sessions across a selection.
1088
1161
 
1089
1162
  - Params: `Auth.EndUserBulkRevokeSessionsParams`
1163
+ - Credential: [`workspace_key`](#workspace_key)
1090
1164
 
1091
1165
  | Status | Error class | Codes | When |
1092
1166
  |---|---|---|---|
@@ -1097,7 +1171,7 @@ Bulk-revoke sessions across a selection.
1097
1171
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
1098
1172
 
1099
1173
  ```ts
1100
- const bulkAffectedUsers = await auth.admin.app("app").endUsers.bulkRevokeSessions({ selection: { mode: "ids" } });
1174
+ const bulkAffectedUsers = await auth.admin.app("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f").endUsers.bulkRevokeSessions({ selection: { mode: "ids" } });
1101
1175
  ```
1102
1176
 
1103
1177
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#post-v1-apps-app-id-end-users-bulk-revoke-sessions)
@@ -1109,6 +1183,7 @@ const bulkAffectedUsers = await auth.admin.app("app").endUsers.bulkRevokeSession
1109
1183
  Bulk-assign a role across a selection.
1110
1184
 
1111
1185
  - Params: `Auth.EndUserBulkUpdateRoleParams`
1186
+ - Credential: [`workspace_key`](#workspace_key)
1112
1187
 
1113
1188
  | Status | Error class | Codes | When |
1114
1189
  |---|---|---|---|
@@ -1119,7 +1194,7 @@ Bulk-assign a role across a selection.
1119
1194
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
1120
1195
 
1121
1196
  ```ts
1122
- const bulkAffectedUsers = await auth.admin.app("app").endUsers.bulkUpdateRole({ role_name: "string", selection: { mode: "ids" } });
1197
+ const bulkAffectedUsers = await auth.admin.app("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f").endUsers.bulkUpdateRole({ role_name: "member", selection: { mode: "ids" } });
1123
1198
  ```
1124
1199
 
1125
1200
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#post-v1-apps-app-id-end-users-bulk-update-role)
@@ -1131,6 +1206,7 @@ const bulkAffectedUsers = await auth.admin.app("app").endUsers.bulkUpdateRole({
1131
1206
  Bulk-update status across a selection.
1132
1207
 
1133
1208
  - Params: `Auth.EndUserBulkUpdateStatusParams`
1209
+ - Credential: [`workspace_key`](#workspace_key)
1134
1210
 
1135
1211
  | Status | Error class | Codes | When |
1136
1212
  |---|---|---|---|
@@ -1141,7 +1217,7 @@ Bulk-update status across a selection.
1141
1217
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
1142
1218
 
1143
1219
  ```ts
1144
- const bulkAffectedUsers = await auth.admin.app("app").endUsers.bulkUpdateStatus({ selection: { mode: "ids" }, status: "suspended" });
1220
+ const bulkAffectedUsers = await auth.admin.app("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f").endUsers.bulkUpdateStatus({ selection: { mode: "ids" }, status: "suspended" });
1145
1221
  ```
1146
1222
 
1147
1223
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#post-v1-apps-app-id-end-users-bulk-update-status)
@@ -1154,6 +1230,8 @@ Delete an end-user permanently.
1154
1230
 
1155
1231
  Removes the account with its contacts, sessions and tenant memberships. Writes one `auth.account.deleted` audit row (`via: platform`) in the same transaction, mirrors the deletion into the workspace audit log as `auth.end_users.deleted`, and fires `user.deleted` with `via: "platform"`.
1156
1232
 
1233
+ - Credential: [`workspace_key`](#workspace_key)
1234
+
1157
1235
  | Status | Error class | Codes | When |
1158
1236
  |---|---|---|---|
1159
1237
  | 401 | `AuthenticationError` | `UNAUTHORIZED` | Missing or invalid bearer token. |
@@ -1162,7 +1240,7 @@ Removes the account with its contacts, sessions and tenant memberships. Writes o
1162
1240
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
1163
1241
 
1164
1242
  ```ts
1165
- await auth.admin.app("app").endUsers.delete("user");
1243
+ await auth.admin.app("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f").endUsers.delete("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f");
1166
1244
  ```
1167
1245
 
1168
1246
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#delete-v1-apps-app-id-end-users-user-id)
@@ -1176,6 +1254,7 @@ Stream this app's end-user directory as NDJSON or CSV, applying the list's `?sta
1176
1254
  Streamed in keyset batches — neither the service nor the client holds the whole directory. Ordered oldest-first by `(created_at, id)` so a re-run is stable. Gated on `auth.user.export`, which is escalation class: this hands over every address in the directory in one call, where `auth.list` pages it. Hard cap of 1,000,000 rows; a wider request is refused with 413 before any body is written.
1177
1255
 
1178
1256
  - Params: `Auth.EndUserExportParams`
1257
+ - Credential: [`workspace_key`](#workspace_key)
1179
1258
 
1180
1259
  | Status | Error class | Codes | When |
1181
1260
  |---|---|---|---|
@@ -1187,7 +1266,7 @@ Streamed in keyset batches — neither the service nor the client holds the whol
1187
1266
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
1188
1267
 
1189
1268
  ```ts
1190
- const response = await auth.admin.app("app").endUsers.export();
1269
+ const response = await auth.admin.app("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f").endUsers.export();
1191
1270
  const text = await response.text();
1192
1271
  ```
1193
1272
 
@@ -1201,6 +1280,7 @@ List end-users in this app (cursor-paginated).
1201
1280
 
1202
1281
  - Params: `Auth.EndUserListParams`
1203
1282
  - Paginated: `for await` iterates every item; `await` gives one page
1283
+ - Credential: [`workspace_key`](#workspace_key)
1204
1284
 
1205
1285
  | Status | Error class | Codes | When |
1206
1286
  |---|---|---|---|
@@ -1209,10 +1289,10 @@ List end-users in this app (cursor-paginated).
1209
1289
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
1210
1290
 
1211
1291
  ```ts
1212
- for await (const endUser of auth.admin.app("app").endUsers.list()) {
1292
+ for await (const endUser of auth.admin.app("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f").endUsers.list()) {
1213
1293
  console.log(endUser);
1214
1294
  }
1215
- const page = await auth.admin.app("app").endUsers.list({}, { timeout: 5_000 });
1295
+ const page = await auth.admin.app("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f").endUsers.list({}, { timeout: 5_000 });
1216
1296
  ```
1217
1297
 
1218
1298
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#get-v1-apps-app-id-end-users)
@@ -1223,6 +1303,8 @@ const page = await auth.admin.app("app").endUsers.list({}, { timeout: 5_000 });
1223
1303
 
1224
1304
  Resend verification to the primary email contact.
1225
1305
 
1306
+ - Credential: [`workspace_key`](#workspace_key)
1307
+
1226
1308
  | Status | Error class | Codes | When |
1227
1309
  |---|---|---|---|
1228
1310
  | 401 | `AuthenticationError` | `UNAUTHORIZED` | Missing or invalid bearer token. |
@@ -1232,7 +1314,7 @@ Resend verification to the primary email contact.
1232
1314
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
1233
1315
 
1234
1316
  ```ts
1235
- const issuedVerificationCode = await auth.admin.app("app").endUsers.resendVerification("user");
1317
+ const issuedVerificationCode = await auth.admin.app("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f").endUsers.resendVerification("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f");
1236
1318
  ```
1237
1319
 
1238
1320
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#post-v1-apps-app-id-end-users-user-id-resend-verification)
@@ -1243,6 +1325,8 @@ const issuedVerificationCode = await auth.admin.app("app").endUsers.resendVerifi
1243
1325
 
1244
1326
  Get one end-user.
1245
1327
 
1328
+ - Credential: [`workspace_key`](#workspace_key)
1329
+
1246
1330
  | Status | Error class | Codes | When |
1247
1331
  |---|---|---|---|
1248
1332
  | 401 | `AuthenticationError` | `UNAUTHORIZED` | Missing or invalid bearer token. |
@@ -1251,7 +1335,7 @@ Get one end-user.
1251
1335
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
1252
1336
 
1253
1337
  ```ts
1254
- const endUserSummary = await auth.admin.app("app").endUsers.retrieve("user");
1338
+ const endUserSummary = await auth.admin.app("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f").endUsers.retrieve("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f");
1255
1339
  ```
1256
1340
 
1257
1341
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#get-v1-apps-app-id-end-users-user-id)
@@ -1263,6 +1347,7 @@ const endUserSummary = await auth.admin.app("app").endUsers.retrieve("user");
1263
1347
  Update end-user display name, primary email, or metadata keys (merged).
1264
1348
 
1265
1349
  - Params: `Auth.EndUserUpdateParams`
1350
+ - Credential: [`workspace_key`](#workspace_key)
1266
1351
 
1267
1352
  | Status | Error class | Codes | When |
1268
1353
  |---|---|---|---|
@@ -1273,7 +1358,7 @@ Update end-user display name, primary email, or metadata keys (merged).
1273
1358
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
1274
1359
 
1275
1360
  ```ts
1276
- const endUserSummary = await auth.admin.app("app").endUsers.update("user", { display_name: "string" });
1361
+ const endUserSummary = await auth.admin.app("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f").endUsers.update("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f", { display_name: "Ada Lovelace" });
1277
1362
  ```
1278
1363
 
1279
1364
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#patch-v1-apps-app-id-end-users-user-id)
@@ -1285,6 +1370,7 @@ const endUserSummary = await auth.admin.app("app").endUsers.update("user", { dis
1285
1370
  Assign an end-user a role.
1286
1371
 
1287
1372
  - Params: `Auth.EndUserUpdateRoleParams`
1373
+ - Credential: [`workspace_key`](#workspace_key)
1288
1374
 
1289
1375
  | Status | Error class | Codes | When |
1290
1376
  |---|---|---|---|
@@ -1295,7 +1381,7 @@ Assign an end-user a role.
1295
1381
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
1296
1382
 
1297
1383
  ```ts
1298
- const endUserRoleAssignment = await auth.admin.app("app").endUsers.updateRole("user", { role_name: "admin" });
1384
+ const endUserRoleAssignment = await auth.admin.app("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f").endUsers.updateRole("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f", { role_name: "admin" });
1299
1385
  ```
1300
1386
 
1301
1387
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#patch-v1-apps-app-id-end-users-user-id-role)
@@ -1307,6 +1393,7 @@ const endUserRoleAssignment = await auth.admin.app("app").endUsers.updateRole("u
1307
1393
  Change end-user status (active / suspended / deactivated).
1308
1394
 
1309
1395
  - Params: `Auth.EndUserUpdateStatusParams`
1396
+ - Credential: [`workspace_key`](#workspace_key)
1310
1397
 
1311
1398
  | Status | Error class | Codes | When |
1312
1399
  |---|---|---|---|
@@ -1317,7 +1404,7 @@ Change end-user status (active / suspended / deactivated).
1317
1404
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
1318
1405
 
1319
1406
  ```ts
1320
- const endUserSummary = await auth.admin.app("app").endUsers.updateStatus("user", { status: "suspended" });
1407
+ const endUserSummary = await auth.admin.app("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f").endUsers.updateStatus("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f", { status: "suspended" });
1321
1408
  ```
1322
1409
 
1323
1410
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#patch-v1-apps-app-id-end-users-user-id-status)
@@ -1332,6 +1419,7 @@ Add a contact for an end-user (unverified).
1332
1419
 
1333
1420
  - Params: `Auth.EndUserContactCreateParams`
1334
1421
  - Idempotent: an `Idempotency-Key` is sent automatically and reused on retries
1422
+ - Credential: [`workspace_key`](#workspace_key)
1335
1423
 
1336
1424
  | Status | Error class | Codes | When |
1337
1425
  |---|---|---|---|
@@ -1343,7 +1431,7 @@ Add a contact for an end-user (unverified).
1343
1431
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
1344
1432
 
1345
1433
  ```ts
1346
- const accountContact = await auth.admin.app("app").endUsers.contacts.create("user", { type: "email", value: "string" });
1434
+ const accountContact = await auth.admin.app("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f").endUsers.contacts.create("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f", { type: "email", value: "ada@example.com" });
1347
1435
  ```
1348
1436
 
1349
1437
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#post-v1-apps-app-id-end-users-user-id-contacts)
@@ -1354,6 +1442,8 @@ const accountContact = await auth.admin.app("app").endUsers.contacts.create("use
1354
1442
 
1355
1443
  Delete a contact from an end-user.
1356
1444
 
1445
+ - Credential: [`workspace_key`](#workspace_key)
1446
+
1357
1447
  | Status | Error class | Codes | When |
1358
1448
  |---|---|---|---|
1359
1449
  | 401 | `AuthenticationError` | `UNAUTHORIZED` | Missing or invalid bearer token. |
@@ -1363,7 +1453,7 @@ Delete a contact from an end-user.
1363
1453
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
1364
1454
 
1365
1455
  ```ts
1366
- await auth.admin.app("app").endUsers.contacts.delete("user", "contact");
1456
+ await auth.admin.app("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f").endUsers.contacts.delete("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f", "3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f");
1367
1457
  ```
1368
1458
 
1369
1459
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#delete-v1-apps-app-id-end-users-user-id-contacts-contact-id)
@@ -1374,6 +1464,8 @@ await auth.admin.app("app").endUsers.contacts.delete("user", "contact");
1374
1464
 
1375
1465
  List an end-user's contacts.
1376
1466
 
1467
+ - Credential: [`workspace_key`](#workspace_key)
1468
+
1377
1469
  | Status | Error class | Codes | When |
1378
1470
  |---|---|---|---|
1379
1471
  | 401 | `AuthenticationError` | `UNAUTHORIZED` | Missing or invalid bearer token. |
@@ -1382,7 +1474,7 @@ List an end-user's contacts.
1382
1474
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
1383
1475
 
1384
1476
  ```ts
1385
- const accountContactList = await auth.admin.app("app").endUsers.contacts.list("user");
1477
+ const accountContactList = await auth.admin.app("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f").endUsers.contacts.list("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f");
1386
1478
  ```
1387
1479
 
1388
1480
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#get-v1-apps-app-id-end-users-user-id-contacts)
@@ -1393,6 +1485,8 @@ const accountContactList = await auth.admin.app("app").endUsers.contacts.list("u
1393
1485
 
1394
1486
  Promote a verified contact to primary.
1395
1487
 
1488
+ - Credential: [`workspace_key`](#workspace_key)
1489
+
1396
1490
  | Status | Error class | Codes | When |
1397
1491
  |---|---|---|---|
1398
1492
  | 401 | `AuthenticationError` | `UNAUTHORIZED` | Missing or invalid bearer token. |
@@ -1402,7 +1496,7 @@ Promote a verified contact to primary.
1402
1496
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
1403
1497
 
1404
1498
  ```ts
1405
- await auth.admin.app("app").endUsers.contacts.promote("user", "contact");
1499
+ await auth.admin.app("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f").endUsers.contacts.promote("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f", "3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f");
1406
1500
  ```
1407
1501
 
1408
1502
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#post-v1-apps-app-id-end-users-user-id-contacts-contact-id-promote)
@@ -1413,6 +1507,8 @@ await auth.admin.app("app").endUsers.contacts.promote("user", "contact");
1413
1507
 
1414
1508
  Reissue a verification code for a contact.
1415
1509
 
1510
+ - Credential: [`workspace_key`](#workspace_key)
1511
+
1416
1512
  | Status | Error class | Codes | When |
1417
1513
  |---|---|---|---|
1418
1514
  | 401 | `AuthenticationError` | `UNAUTHORIZED` | Missing or invalid bearer token. |
@@ -1422,7 +1518,7 @@ Reissue a verification code for a contact.
1422
1518
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
1423
1519
 
1424
1520
  ```ts
1425
- const issuedVerificationCode = await auth.admin.app("app").endUsers.contacts.resendVerification("user", "contact");
1521
+ const issuedVerificationCode = await auth.admin.app("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f").endUsers.contacts.resendVerification("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f", "3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f");
1426
1522
  ```
1427
1523
 
1428
1524
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#post-v1-apps-app-id-end-users-user-id-contacts-contact-id-resend-verification)
@@ -1438,6 +1534,7 @@ Queue a bulk end-user import. Body is NDJSON — one JSON object per line. Retur
1438
1534
  Row fields: `email` (required), `username`, `display_name`, `role`, `tenant_slugs` (array), `external_id`, `metadata`, `email_verified`, and `password_hash` + `password_hash_algorithm`. `password_hash_algorithm` is `argon2id` or `bcrypt`. **argon2id** hashes are accepted at any parameters. **bcrypt** hashes (`$2a$`, `$2b$`, `$2y$` — Auth0, PHP `password_hash`, Spring, node `bcrypt`, Devise / Rails, Laravel, Django) are accepted at any valid cost (4-31), verified on the user's first signin and replaced by an argon2id hash of the same password in that request, so the user keeps their password and never notices. Signin pads a bcrypt verify up to the argon2id floor, so at cost 10 and below an imported account cannot be told apart by timing; at cost 11 and above the verify is slower than the floor and answers when it finishes — a small timing difference on migrated accounts only, gone after each one's first signin. A re-run is idempotent: rows match on `external_id` first, then the primary email, and update rather than duplicate. An existing password, an already-verified address and an existing member's role are never overwritten — see the module docs for why each one.
1439
1535
 
1440
1536
  - Idempotent: an `Idempotency-Key` is sent automatically and reused on retries
1537
+ - Credential: [`workspace_key`](#workspace_key)
1441
1538
 
1442
1539
  | Status | Error class | Codes | When |
1443
1540
  |---|---|---|---|
@@ -1449,7 +1546,7 @@ Row fields: `email` (required), `username`, `display_name`, `role`, `tenant_slug
1449
1546
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
1450
1547
 
1451
1548
  ```ts
1452
- const importJob = await auth.admin.app("app").endUsers.imports.create();
1549
+ const importJob = await auth.admin.app("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f").endUsers.imports.create();
1453
1550
  ```
1454
1551
 
1455
1552
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#post-v1-apps-app-id-end-users-import)
@@ -1462,6 +1559,8 @@ Import job status and per-row error report. Never returns the uploaded file.
1462
1559
 
1463
1560
  `status: succeeded` means the job RAN TO COMPLETION, not that every row landed — read `failed_count` and `errors` for that. `errors` is capped at the first 500 entries; the counts stay exact.
1464
1561
 
1562
+ - Credential: [`workspace_key`](#workspace_key)
1563
+
1465
1564
  | Status | Error class | Codes | When |
1466
1565
  |---|---|---|---|
1467
1566
  | 401 | `AuthenticationError` | `UNAUTHORIZED` | Missing, invalid or expired bearer token. |
@@ -1470,7 +1569,7 @@ Import job status and per-row error report. Never returns the uploaded file.
1470
1569
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
1471
1570
 
1472
1571
  ```ts
1473
- const importJob = await auth.admin.app("app").endUsers.imports.retrieve("job");
1572
+ const importJob = await auth.admin.app("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f").endUsers.imports.retrieve("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f");
1474
1573
  ```
1475
1574
 
1476
1575
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#get-v1-apps-app-id-end-users-import-job-id)
@@ -1485,6 +1584,7 @@ Pre-allocate an EndUser invite. Dispatches an invitation email via the workspace
1485
1584
 
1486
1585
  - Params: `Auth.EndUserInviteCreateParams`
1487
1586
  - Idempotent: an `Idempotency-Key` is sent automatically and reused on retries
1587
+ - Credential: [`workspace_key`](#workspace_key)
1488
1588
 
1489
1589
  | Status | Error class | Codes | When |
1490
1590
  |---|---|---|---|
@@ -1494,7 +1594,7 @@ Pre-allocate an EndUser invite. Dispatches an invitation email via the workspace
1494
1594
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
1495
1595
 
1496
1596
  ```ts
1497
- const endUserInvite = await auth.admin.app("app").endUsers.invites.create({ email: "alice@example.com" });
1597
+ const endUserInvite = await auth.admin.app("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f").endUsers.invites.create({ email: "alice@example.com" });
1498
1598
  ```
1499
1599
 
1500
1600
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#post-v1-apps-app-id-end-users-invites)
@@ -1505,6 +1605,8 @@ const endUserInvite = await auth.admin.app("app").endUsers.invites.create({ emai
1505
1605
 
1506
1606
  Revoke an EndUser invite (soft-delete).
1507
1607
 
1608
+ - Credential: [`workspace_key`](#workspace_key)
1609
+
1508
1610
  | Status | Error class | Codes | When |
1509
1611
  |---|---|---|---|
1510
1612
  | 401 | `AuthenticationError` | `UNAUTHORIZED` | Missing or invalid bearer token. |
@@ -1513,7 +1615,7 @@ Revoke an EndUser invite (soft-delete).
1513
1615
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
1514
1616
 
1515
1617
  ```ts
1516
- await auth.admin.app("app").endUsers.invites.delete("invite");
1618
+ await auth.admin.app("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f").endUsers.invites.delete("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f");
1517
1619
  ```
1518
1620
 
1519
1621
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#delete-v1-apps-app-id-end-users-invites-invite-id)
@@ -1526,6 +1628,7 @@ List EndUser invites for this app, newest first. Filter by `?status=`.
1526
1628
 
1527
1629
  - Params: `Auth.EndUserInviteListParams`
1528
1630
  - Paginated: `for await` iterates every item; `await` gives one page
1631
+ - Credential: [`workspace_key`](#workspace_key)
1529
1632
 
1530
1633
  | Status | Error class | Codes | When |
1531
1634
  |---|---|---|---|
@@ -1534,10 +1637,10 @@ List EndUser invites for this app, newest first. Filter by `?status=`.
1534
1637
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
1535
1638
 
1536
1639
  ```ts
1537
- for await (const invite of auth.admin.app("app").endUsers.invites.list()) {
1640
+ for await (const invite of auth.admin.app("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f").endUsers.invites.list()) {
1538
1641
  console.log(invite);
1539
1642
  }
1540
- const page = await auth.admin.app("app").endUsers.invites.list({}, { timeout: 5_000 });
1643
+ const page = await auth.admin.app("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f").endUsers.invites.list({}, { timeout: 5_000 });
1541
1644
  ```
1542
1645
 
1543
1646
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#get-v1-apps-app-id-end-users-invites)
@@ -1552,6 +1655,8 @@ Force an end-user back through MFA enrolment.
1552
1655
 
1553
1656
  Disables every enabled factor on the account, destroys its recovery codes, and revokes every live session. The next sign-in on an app running `auth_config.mfa: "required"` returns `mfa_enrollment_required` and the user must enrol a new factor before a session is minted. On an app running `mfa: "optional"` the user simply ends up with no MFA, which is the pre-existing meaning of deleting their factors. All three steps matter: leaving the recovery codes alive would let whoever holds the old printout complete `/auth/mfa/recover` and skip enrolment entirely. Audited, and emits `user.mfa.factor_disabled` per factor plus one `user.mfa.reenrollment_required`. Idempotent.
1554
1657
 
1658
+ - Credential: [`workspace_key`](#workspace_key)
1659
+
1555
1660
  | Status | Error class | Codes | When |
1556
1661
  |---|---|---|---|
1557
1662
  | 401 | `AuthenticationError` | `UNAUTHORIZED` | Missing, invalid or expired bearer token. |
@@ -1560,7 +1665,7 @@ Disables every enabled factor on the account, destroys its recovery codes, and r
1560
1665
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
1561
1666
 
1562
1667
  ```ts
1563
- const requireMfaReenrollment = await auth.admin.app("app").endUsers.mfa.requireReenrollment("user");
1668
+ const requireMfaReenrollment = await auth.admin.app("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f").endUsers.mfa.requireReenrollment("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f");
1564
1669
  ```
1565
1670
 
1566
1671
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#post-v1-apps-app-id-end-users-user-id-mfa-require-reenrollment)
@@ -1573,6 +1678,8 @@ const requireMfaReenrollment = await auth.admin.app("app").endUsers.mfa.requireR
1573
1678
 
1574
1679
  Revoke every active session for an end-user.
1575
1680
 
1681
+ - Credential: [`workspace_key`](#workspace_key)
1682
+
1576
1683
  | Status | Error class | Codes | When |
1577
1684
  |---|---|---|---|
1578
1685
  | 401 | `AuthenticationError` | `UNAUTHORIZED` | Missing or invalid bearer token. |
@@ -1581,7 +1688,7 @@ Revoke every active session for an end-user.
1581
1688
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
1582
1689
 
1583
1690
  ```ts
1584
- await auth.admin.app("app").endUsers.sessions.revokeAll("user");
1691
+ await auth.admin.app("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f").endUsers.sessions.revokeAll("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f");
1585
1692
  ```
1586
1693
 
1587
1694
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#post-v1-apps-app-id-end-users-user-id-sessions-revoke-all)
@@ -1594,6 +1701,8 @@ await auth.admin.app("app").endUsers.sessions.revokeAll("user");
1594
1701
 
1595
1702
  Remove a member from the app.
1596
1703
 
1704
+ - Credential: [`workspace_key`](#workspace_key)
1705
+
1597
1706
  | Status | Error class | Codes | When |
1598
1707
  |---|---|---|---|
1599
1708
  | 401 | `AuthenticationError` | `UNAUTHORIZED` | Missing or invalid bearer token. |
@@ -1602,7 +1711,7 @@ Remove a member from the app.
1602
1711
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
1603
1712
 
1604
1713
  ```ts
1605
- await auth.admin.app("app").members.delete("account");
1714
+ await auth.admin.app("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f").members.delete("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f");
1606
1715
  ```
1607
1716
 
1608
1717
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#delete-v1-apps-app-id-members-account-id)
@@ -1615,6 +1724,7 @@ List app members (PlatformUser view of EndUser memberships).
1615
1724
 
1616
1725
  - Params: `Auth.MemberListParams`
1617
1726
  - Paginated: `for await` iterates every item; `await` gives one page
1727
+ - Credential: [`workspace_key`](#workspace_key)
1618
1728
 
1619
1729
  | Status | Error class | Codes | When |
1620
1730
  |---|---|---|---|
@@ -1625,10 +1735,10 @@ List app members (PlatformUser view of EndUser memberships).
1625
1735
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
1626
1736
 
1627
1737
  ```ts
1628
- for await (const member of auth.admin.app("app").members.list()) {
1738
+ for await (const member of auth.admin.app("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f").members.list()) {
1629
1739
  console.log(member);
1630
1740
  }
1631
- const page = await auth.admin.app("app").members.list({}, { timeout: 5_000 });
1741
+ const page = await auth.admin.app("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f").members.list({}, { timeout: 5_000 });
1632
1742
  ```
1633
1743
 
1634
1744
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#get-v1-apps-app-id-members)
@@ -1643,6 +1753,8 @@ Read the per-app notification settings. Absent row reads as defaults (notificati
1643
1753
 
1644
1754
  `effective_sender` is the sender emails actually go out from, checked live in Mail with your own access (the workspace default sender, or the app's custom address).
1645
1755
 
1756
+ - Credential: [`workspace_key`](#workspace_key)
1757
+
1646
1758
  | Status | Error class | Codes | When |
1647
1759
  |---|---|---|---|
1648
1760
  | 401 | `AuthenticationError` | `UNAUTHORIZED` | Missing or invalid bearer token. |
@@ -1651,7 +1763,7 @@ Read the per-app notification settings. Absent row reads as defaults (notificati
1651
1763
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
1652
1764
 
1653
1765
  ```ts
1654
- const appNotificationSettings = await auth.admin.app("app").notificationSettings.retrieve();
1766
+ const appNotificationSettings = await auth.admin.app("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f").notificationSettings.retrieve();
1655
1767
  ```
1656
1768
 
1657
1769
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#get-v1-apps-app-id-notification-settings)
@@ -1665,6 +1777,7 @@ Update the per-app notification settings. Each field is independently optional;
1665
1777
  Turning `notifications_via_envoi` on (from off) creates the default template of every empty slot in the workspace's Mail templates (`auth-verification`, `auth-welcome`, `auth-password-reset`, `auth-invite`, `auth-mfa-code`), reusing a template that already has that name, and fills the slot. Templates are created with your own Mail access; a slot that could not be created stays empty. A slot you clear later is not refilled until emails are switched off and on again.
1666
1778
 
1667
1779
  - Params: `Auth.NotificationSettingUpdateParams`
1780
+ - Credential: [`workspace_key`](#workspace_key)
1668
1781
 
1669
1782
  | Status | Error class | Codes | When |
1670
1783
  |---|---|---|---|
@@ -1676,7 +1789,7 @@ Turning `notifications_via_envoi` on (from off) creates the default template of
1676
1789
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
1677
1790
 
1678
1791
  ```ts
1679
- const appNotificationSettings = await auth.admin.app("app").notificationSettings.update({ default_sender_address: "ada@example.com" });
1792
+ const appNotificationSettings = await auth.admin.app("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f").notificationSettings.update({ default_sender_address: "ada@example.com" });
1680
1793
  ```
1681
1794
 
1682
1795
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#patch-v1-apps-app-id-notification-settings)
@@ -1692,6 +1805,7 @@ Send me a test of one email.
1692
1805
  Sends the slot's real Mail template, with this app's real sender (the workspace default or the custom address) and example values for every variable, to your own verified account address. It cannot be sent anywhere else. It uses your own Mail access, goes through the same send path as real emails, and shows up in Mail > Emails. At most 10 tests per person per hour. Not available to API keys (a key has no inbox).
1693
1806
 
1694
1807
  - Idempotent: an `Idempotency-Key` is sent automatically and reused on retries
1808
+ - Credential: [`workspace_key`](#workspace_key)
1695
1809
 
1696
1810
  | Status | Error class | Codes | When |
1697
1811
  |---|---|---|---|
@@ -1706,7 +1820,7 @@ Sends the slot's real Mail template, with this app's real sender (the workspace
1706
1820
  | 503 | `InternalServerError` | `SERVICE_UNAVAILABLE`, `MAIL_UNREACHABLE` | `MAIL_UNREACHABLE`: Mail did not answer. |
1707
1821
 
1708
1822
  ```ts
1709
- const notificationTest = await auth.admin.app("app").notifications.test("slot");
1823
+ const notificationTest = await auth.admin.app("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f").notifications.test("verification");
1710
1824
  ```
1711
1825
 
1712
1826
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#post-v1-apps-app-id-notifications-slot-test)
@@ -1721,6 +1835,7 @@ Create a custom permission.
1721
1835
 
1722
1836
  - Params: `Auth.AppScopedPermissionCreateParams`
1723
1837
  - Idempotent: an `Idempotency-Key` is sent automatically and reused on retries
1838
+ - Credential: [`workspace_key`](#workspace_key)
1724
1839
 
1725
1840
  | Status | Error class | Codes | When |
1726
1841
  |---|---|---|---|
@@ -1732,7 +1847,7 @@ Create a custom permission.
1732
1847
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
1733
1848
 
1734
1849
  ```ts
1735
- const permission = await auth.admin.app("app").permissions.create({ action: "read", resource: "project" });
1850
+ const permission = await auth.admin.app("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f").permissions.create({ action: "read", resource: "project" });
1736
1851
  ```
1737
1852
 
1738
1853
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#post-v1-apps-app-id-permissions)
@@ -1743,6 +1858,8 @@ const permission = await auth.admin.app("app").permissions.create({ action: "rea
1743
1858
 
1744
1859
  Delete a custom permission.
1745
1860
 
1861
+ - Credential: [`workspace_key`](#workspace_key)
1862
+
1746
1863
  | Status | Error class | Codes | When |
1747
1864
  |---|---|---|---|
1748
1865
  | 400 | `BadRequestError` | `BAD_REQUEST`, `VALIDATION_ERROR` | `permissionKey` is not in `resource.action` format. |
@@ -1752,7 +1869,7 @@ Delete a custom permission.
1752
1869
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
1753
1870
 
1754
1871
  ```ts
1755
- await auth.admin.app("app").permissions.delete("permission_key");
1872
+ await auth.admin.app("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f").permissions.delete("invoice.read");
1756
1873
  ```
1757
1874
 
1758
1875
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#delete-v1-apps-app-id-permissions-permission-key)
@@ -1763,6 +1880,8 @@ await auth.admin.app("app").permissions.delete("permission_key");
1763
1880
 
1764
1881
  List all permissions (system + custom).
1765
1882
 
1883
+ - Credential: [`workspace_key`](#workspace_key)
1884
+
1766
1885
  | Status | Error class | Codes | When |
1767
1886
  |---|---|---|---|
1768
1887
  | 401 | `AuthenticationError` | `UNAUTHORIZED` | Missing or invalid bearer token. |
@@ -1771,7 +1890,7 @@ List all permissions (system + custom).
1771
1890
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
1772
1891
 
1773
1892
  ```ts
1774
- const result = await auth.admin.app("app").permissions.list();
1893
+ const result = await auth.admin.app("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f").permissions.list();
1775
1894
  ```
1776
1895
 
1777
1896
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#get-v1-apps-app-id-permissions)
@@ -1786,6 +1905,8 @@ List permissions that at least one Consumer-API route enforces.
1786
1905
 
1787
1906
  Returns the deduplicated set of permission strings that any handler in this users-api build gates via `@RequireAppPermission`. Drives the console "Enforced" badge — a customer who binds `user.delete` to a role can see whether any route in this deploy actually checks for it. App-agnostic in shape; the path parameter is included for URN symmetry with the rest of the per-app surface, not to narrow the response.
1788
1907
 
1908
+ - Credential: [`workspace_key`](#workspace_key)
1909
+
1789
1910
  | Status | Error class | Codes | When |
1790
1911
  |---|---|---|---|
1791
1912
  | 401 | `AuthenticationError` | `UNAUTHORIZED` | Missing or invalid bearer token. |
@@ -1794,7 +1915,7 @@ Returns the deduplicated set of permission strings that any handler in this user
1794
1915
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
1795
1916
 
1796
1917
  ```ts
1797
- const enforcedPermissions = await auth.admin.app("app").permissions.enforcement.list();
1918
+ const enforcedPermissions = await auth.admin.app("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f").permissions.enforcement.list();
1798
1919
  ```
1799
1920
 
1800
1921
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#get-v1-apps-app-id-permissions-enforcement)
@@ -1808,6 +1929,7 @@ const enforcedPermissions = await auth.admin.app("app").permissions.enforcement.
1808
1929
  Assign a role to a user.
1809
1930
 
1810
1931
  - Params: `Auth.RoleAssignParams`
1932
+ - Credential: [`workspace_key`](#workspace_key)
1811
1933
 
1812
1934
  | Status | Error class | Codes | When |
1813
1935
  |---|---|---|---|
@@ -1818,7 +1940,7 @@ Assign a role to a user.
1818
1940
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
1819
1941
 
1820
1942
  ```ts
1821
- const appMembership = await auth.admin.app("app").roles.assign({ account_id: "string", role: "admin" });
1943
+ const appMembership = await auth.admin.app("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f").roles.assign({ account_id: "3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f", role: "admin" });
1822
1944
  ```
1823
1945
 
1824
1946
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#post-v1-apps-app-id-roles-assign)
@@ -1831,6 +1953,7 @@ Create a new role (no permissions yet — set them via PUT /:roleName/permission
1831
1953
 
1832
1954
  - Params: `Auth.AppScopedRoleCreateParams`
1833
1955
  - Idempotent: an `Idempotency-Key` is sent automatically and reused on retries
1956
+ - Credential: [`workspace_key`](#workspace_key)
1834
1957
 
1835
1958
  | Status | Error class | Codes | When |
1836
1959
  |---|---|---|---|
@@ -1842,7 +1965,7 @@ Create a new role (no permissions yet — set them via PUT /:roleName/permission
1842
1965
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
1843
1966
 
1844
1967
  ```ts
1845
- const role = await auth.admin.app("app").roles.create({ name: "editor" });
1968
+ const role = await auth.admin.app("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f").roles.create({ name: "editor" });
1846
1969
  ```
1847
1970
 
1848
1971
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#post-v1-apps-app-id-roles)
@@ -1853,6 +1976,8 @@ const role = await auth.admin.app("app").roles.create({ name: "editor" });
1853
1976
 
1854
1977
  Delete a custom role.
1855
1978
 
1979
+ - Credential: [`workspace_key`](#workspace_key)
1980
+
1856
1981
  | Status | Error class | Codes | When |
1857
1982
  |---|---|---|---|
1858
1983
  | 401 | `AuthenticationError` | `UNAUTHORIZED` | Missing or invalid bearer token. |
@@ -1862,7 +1987,7 @@ Delete a custom role.
1862
1987
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
1863
1988
 
1864
1989
  ```ts
1865
- await auth.admin.app("app").roles.delete("role_name");
1990
+ await auth.admin.app("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f").roles.delete("admin");
1866
1991
  ```
1867
1992
 
1868
1993
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#delete-v1-apps-app-id-roles-role-name)
@@ -1875,6 +2000,7 @@ List all roles in an app. Filter by `?search=` on the role name.
1875
2000
 
1876
2001
  - Params: `Auth.AppScopedRoleListParams`
1877
2002
  - Paginated: `for await` iterates every item; `await` gives one page
2003
+ - Credential: [`workspace_key`](#workspace_key)
1878
2004
 
1879
2005
  | Status | Error class | Codes | When |
1880
2006
  |---|---|---|---|
@@ -1885,10 +2011,10 @@ List all roles in an app. Filter by `?search=` on the role name.
1885
2011
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
1886
2012
 
1887
2013
  ```ts
1888
- for await (const role of auth.admin.app("app").roles.list()) {
2014
+ for await (const role of auth.admin.app("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f").roles.list()) {
1889
2015
  console.log(role);
1890
2016
  }
1891
- const page = await auth.admin.app("app").roles.list({}, { timeout: 5_000 });
2017
+ const page = await auth.admin.app("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f").roles.list({}, { timeout: 5_000 });
1892
2018
  ```
1893
2019
 
1894
2020
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#get-v1-apps-app-id-roles)
@@ -1899,6 +2025,8 @@ const page = await auth.admin.app("app").roles.list({}, { timeout: 5_000 });
1899
2025
 
1900
2026
  Get role details with permissions.
1901
2027
 
2028
+ - Credential: [`workspace_key`](#workspace_key)
2029
+
1902
2030
  | Status | Error class | Codes | When |
1903
2031
  |---|---|---|---|
1904
2032
  | 401 | `AuthenticationError` | `UNAUTHORIZED` | Missing or invalid bearer token. |
@@ -1907,7 +2035,7 @@ Get role details with permissions.
1907
2035
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
1908
2036
 
1909
2037
  ```ts
1910
- const roleWithPermissions = await auth.admin.app("app").roles.retrieve("role_name");
2038
+ const roleWithPermissions = await auth.admin.app("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f").roles.retrieve("admin");
1911
2039
  ```
1912
2040
 
1913
2041
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#get-v1-apps-app-id-roles-role-name)
@@ -1919,6 +2047,7 @@ const roleWithPermissions = await auth.admin.app("app").roles.retrieve("role_nam
1919
2047
  Update role name or description.
1920
2048
 
1921
2049
  - Params: `Auth.AppScopedRoleUpdateParams`
2050
+ - Credential: [`workspace_key`](#workspace_key)
1922
2051
 
1923
2052
  | Status | Error class | Codes | When |
1924
2053
  |---|---|---|---|
@@ -1929,7 +2058,7 @@ Update role name or description.
1929
2058
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
1930
2059
 
1931
2060
  ```ts
1932
- const role = await auth.admin.app("app").roles.update("role_name", { description: "Updated description" });
2061
+ const role = await auth.admin.app("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f").roles.update("admin", { description: "Updated description" });
1933
2062
  ```
1934
2063
 
1935
2064
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#patch-v1-apps-app-id-roles-role-name)
@@ -1941,6 +2070,7 @@ const role = await auth.admin.app("app").roles.update("role_name", { description
1941
2070
  Set permissions for a role (caller-narrowed: caller must hold every requested permission).
1942
2071
 
1943
2072
  - Params: `Auth.AppScopedRoleUpdatePermissionsParams`
2073
+ - Credential: [`workspace_key`](#workspace_key)
1944
2074
 
1945
2075
  | Status | Error class | Codes | When |
1946
2076
  |---|---|---|---|
@@ -1951,7 +2081,7 @@ Set permissions for a role (caller-narrowed: caller must hold every requested pe
1951
2081
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
1952
2082
 
1953
2083
  ```ts
1954
- const result = await auth.admin.app("app").roles.updatePermissions("role_name", { permissions: ["user.read", "role.create"] });
2084
+ const result = await auth.admin.app("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f").roles.updatePermissions("admin", { permissions: ["user.read", "role.create"] });
1955
2085
  ```
1956
2086
 
1957
2087
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#put-v1-apps-app-id-roles-role-name-permissions)
@@ -1964,6 +2094,8 @@ const result = await auth.admin.app("app").roles.updatePermissions("role_name",
1964
2094
 
1965
2095
  List all available permissions.
1966
2096
 
2097
+ - Credential: [`workspace_key`](#workspace_key)
2098
+
1967
2099
  | Status | Error class | Codes | When |
1968
2100
  |---|---|---|---|
1969
2101
  | 401 | `AuthenticationError` | `UNAUTHORIZED` | Missing or invalid bearer token. |
@@ -1972,7 +2104,7 @@ List all available permissions.
1972
2104
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
1973
2105
 
1974
2106
  ```ts
1975
- const result = await auth.admin.app("app").roles.permissions.list();
2107
+ const result = await auth.admin.app("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f").roles.permissions.list();
1976
2108
  ```
1977
2109
 
1978
2110
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#get-v1-apps-app-id-roles-permissions)
@@ -1987,6 +2119,8 @@ Get the setup checklist for an app: what works, what is missing, and where to fi
1987
2119
 
1988
2120
  Rows, in order: `signups` (attention while sign-ups are off), `redirect_origins` (blocked until at least one site may start sign-in), one `provider_<id>` row per switched-on sign-in provider (blocked while its status is blocked), `email` (sender chosen and a template for every email; attention while emails are off), `api_key` (attention until an active app key exists), `webhook` (attention until an endpoint has accepted a delivery), and `passkeys` (only while passkeys are on; blocked when no allowed redirect origin is on the passkey site). `complete` is true once no row is `not_set` or `blocked`. Each `fix.href` is a console path to the exact setting. Computed on every request from stored settings, plus the workspace default sender read from Mail with your own access when emails use it: nothing here calls DNS or a sign-in provider (a provider whose last credential check was rejected shows as blocked). A ready provider row carries `test.action: sign_in.try`: run it with `POST /v1/apps/{appId}/sign-in-tests` `{ method: "oauth", provider }`.
1989
2121
 
2122
+ - Credential: [`workspace_key`](#workspace_key)
2123
+
1990
2124
  | Status | Error class | Codes | When |
1991
2125
  |---|---|---|---|
1992
2126
  | 401 | `AuthenticationError` | `UNAUTHORIZED` | Missing or invalid bearer token. |
@@ -1995,7 +2129,7 @@ Rows, in order: `signups` (attention while sign-ups are off), `redirect_origins`
1995
2129
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
1996
2130
 
1997
2131
  ```ts
1998
- const setupStatus = await auth.admin.app("app").setupStatus.retrieve();
2132
+ const setupStatus = await auth.admin.app("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f").setupStatus.retrieve();
1999
2133
  ```
2000
2134
 
2001
2135
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#get-v1-apps-app-id-setup-status)
@@ -2012,6 +2146,7 @@ For `oauth`, the server first checks what it can on its own (an allowed redirect
2012
2146
 
2013
2147
  - Params: `Auth.SignInTestCreateParams`
2014
2148
  - Idempotent: an `Idempotency-Key` is sent automatically and reused on retries
2149
+ - Credential: [`workspace_key`](#workspace_key)
2015
2150
 
2016
2151
  | Status | Error class | Codes | When |
2017
2152
  |---|---|---|---|
@@ -2023,7 +2158,7 @@ For `oauth`, the server first checks what it can on its own (an allowed redirect
2023
2158
  | 429 | `RateLimitError` | `RATE_LIMITED` | More than 20 tests for this app in the last hour (`RATE_LIMITED`). |
2024
2159
 
2025
2160
  ```ts
2026
- const signInTest = await auth.admin.app("app").signInTests.create({ method: "oauth" });
2161
+ const signInTest = await auth.admin.app("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f").signInTests.create({ method: "oauth" });
2027
2162
  ```
2028
2163
 
2029
2164
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#post-v1-apps-app-id-sign-in-tests)
@@ -2036,6 +2171,8 @@ Get a sign-in test and its result.
2036
2171
 
2037
2172
  Poll this after opening `start_url`. `state` turns `succeeded` (with `outcome` and `identity`) or `failed` (with `error.fix`). A test that is never finished turns `failed` with code `expired` 15 minutes after it was created.
2038
2173
 
2174
+ - Credential: [`workspace_key`](#workspace_key)
2175
+
2039
2176
  | Status | Error class | Codes | When |
2040
2177
  |---|---|---|---|
2041
2178
  | 400 | `BadRequestError` | `BAD_REQUEST`, `VALIDATION_ERROR` | `testId` is not a UUID. |
@@ -2045,7 +2182,7 @@ Poll this after opening `start_url`. `state` turns `succeeded` (with `outcome` a
2045
2182
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
2046
2183
 
2047
2184
  ```ts
2048
- const signInTest = await auth.admin.app("app").signInTests.retrieve("3f0c1a2b-4c5d-4e6f-8a9b-0c1d2e3f4a5b");
2185
+ const signInTest = await auth.admin.app("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f").signInTests.retrieve("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f");
2049
2186
  ```
2050
2187
 
2051
2188
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#get-v1-apps-app-id-sign-in-tests-test-id)
@@ -2060,6 +2197,8 @@ Revoke a tenant's API key.
2060
2197
 
2061
2198
  Immediate and final; recorded as revoked by you (`revoked_by_type: platform_user`) in the key and in the audit log. Idempotent: revoking a revoked key is a 204 that changes nothing. Requires `auth.revoke` on the app.
2062
2199
 
2200
+ - Credential: [`workspace_key`](#workspace_key)
2201
+
2063
2202
  | Status | Error class | Codes | When |
2064
2203
  |---|---|---|---|
2065
2204
  | 401 | `AuthenticationError` | `UNAUTHORIZED` | Missing or invalid bearer token. |
@@ -2068,7 +2207,7 @@ Immediate and final; recorded as revoked by you (`revoked_by_type: platform_user
2068
2207
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
2069
2208
 
2070
2209
  ```ts
2071
- await auth.admin.app("app").tenantApiKeys.delete("key");
2210
+ await auth.admin.app("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f").tenantApiKeys.delete("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f");
2072
2211
  ```
2073
2212
 
2074
2213
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#delete-v1-apps-app-id-tenant-api-keys-key-id)
@@ -2083,6 +2222,7 @@ Every tenant of the app, newest first, cursor-paginated (20 per page, max 100).
2083
2222
 
2084
2223
  - Params: `Auth.AppScopedTenantApiKeyListParams`
2085
2224
  - Paginated: `for await` iterates every item; `await` gives one page
2225
+ - Credential: [`workspace_key`](#workspace_key)
2086
2226
 
2087
2227
  | Status | Error class | Codes | When |
2088
2228
  |---|---|---|---|
@@ -2093,10 +2233,10 @@ Every tenant of the app, newest first, cursor-paginated (20 per page, max 100).
2093
2233
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
2094
2234
 
2095
2235
  ```ts
2096
- for await (const tenantApiKey of auth.admin.app("app").tenantApiKeys.list()) {
2236
+ for await (const tenantApiKey of auth.admin.app("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f").tenantApiKeys.list()) {
2097
2237
  console.log(tenantApiKey);
2098
2238
  }
2099
- const page = await auth.admin.app("app").tenantApiKeys.list({}, { timeout: 5_000 });
2239
+ const page = await auth.admin.app("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f").tenantApiKeys.list({}, { timeout: 5_000 });
2100
2240
  ```
2101
2241
 
2102
2242
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#get-v1-apps-app-id-tenant-api-keys)
@@ -2111,6 +2251,8 @@ Read the app's tenant API key settings.
2111
2251
 
2112
2252
  Includes the platform kill switch `suspended`. Requires `auth.read` on the app.
2113
2253
 
2254
+ - Credential: [`workspace_key`](#workspace_key)
2255
+
2114
2256
  | Status | Error class | Codes | When |
2115
2257
  |---|---|---|---|
2116
2258
  | 401 | `AuthenticationError` | `UNAUTHORIZED` | Missing or invalid bearer token. |
@@ -2119,7 +2261,7 @@ Includes the platform kill switch `suspended`. Requires `auth.read` on the app.
2119
2261
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
2120
2262
 
2121
2263
  ```ts
2122
- const tenantApiKeysConfig = await auth.admin.app("app").tenantApiKeys.config.retrieve();
2264
+ const tenantApiKeysConfig = await auth.admin.app("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f").tenantApiKeys.config.retrieve();
2123
2265
  ```
2124
2266
 
2125
2267
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#get-v1-apps-app-id-tenant-api-keys-config)
@@ -2134,6 +2276,7 @@ Fields you leave out keep their stored value. `suspended: true` is the kill swit
2134
2276
 
2135
2277
  - Params: `Auth.AppScopedTenantApiKeyConfigUpdateParams`
2136
2278
  - Idempotent: an `Idempotency-Key` is sent automatically and reused on retries
2279
+ - Credential: [`workspace_key`](#workspace_key)
2137
2280
 
2138
2281
  | Status | Error class | Codes | When |
2139
2282
  |---|---|---|---|
@@ -2145,7 +2288,7 @@ Fields you leave out keep their stored value. `suspended: true` is the kill swit
2145
2288
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
2146
2289
 
2147
2290
  ```ts
2148
- const tenantApiKeysConfig = await auth.admin.app("app").tenantApiKeys.config.update({ default_expiry_days: 7 });
2291
+ const tenantApiKeysConfig = await auth.admin.app("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f").tenantApiKeys.config.update({ default_expiry_days: 7 });
2149
2292
  ```
2150
2293
 
2151
2294
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#put-v1-apps-app-id-tenant-api-keys-config)
@@ -2160,6 +2303,7 @@ Create a tenant in this app. Auth-admin lane; requires auth.tenant.create on the
2160
2303
 
2161
2304
  - Params: `Auth.AppScopedTenantCreateParams`
2162
2305
  - Idempotent: an `Idempotency-Key` is sent automatically and reused on retries
2306
+ - Credential: [`workspace_key`](#workspace_key)
2163
2307
 
2164
2308
  | Status | Error class | Codes | When |
2165
2309
  |---|---|---|---|
@@ -2171,7 +2315,7 @@ Create a tenant in this app. Auth-admin lane; requires auth.tenant.create on the
2171
2315
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
2172
2316
 
2173
2317
  ```ts
2174
- const tenant = await auth.admin.app("app").tenants.create({ display_name: "Acme Co" });
2318
+ const tenant = await auth.admin.app("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f").tenants.create({ display_name: "Acme Co" });
2175
2319
  ```
2176
2320
 
2177
2321
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#post-v1-apps-app-id-tenants)
@@ -2182,6 +2326,8 @@ const tenant = await auth.admin.app("app").tenants.create({ display_name: "Acme
2182
2326
 
2183
2327
  Delete a tenant.
2184
2328
 
2329
+ - Credential: [`workspace_key`](#workspace_key)
2330
+
2185
2331
  | Status | Error class | Codes | When |
2186
2332
  |---|---|---|---|
2187
2333
  | 401 | `AuthenticationError` | `UNAUTHORIZED` | Missing or invalid bearer token. |
@@ -2190,7 +2336,7 @@ Delete a tenant.
2190
2336
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
2191
2337
 
2192
2338
  ```ts
2193
- await auth.admin.app("app").tenants.delete("tenant");
2339
+ await auth.admin.app("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f").tenants.delete("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f");
2194
2340
  ```
2195
2341
 
2196
2342
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#delete-v1-apps-app-id-tenants-tenant-id)
@@ -2203,6 +2349,7 @@ List tenants in this app, paginated by created_at desc.
2203
2349
 
2204
2350
  - Params: `Auth.AppScopedTenantListParams`
2205
2351
  - Paginated: `for await` iterates every item; `await` gives one page
2352
+ - Credential: [`workspace_key`](#workspace_key)
2206
2353
 
2207
2354
  | Status | Error class | Codes | When |
2208
2355
  |---|---|---|---|
@@ -2213,10 +2360,10 @@ List tenants in this app, paginated by created_at desc.
2213
2360
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
2214
2361
 
2215
2362
  ```ts
2216
- for await (const tenant of auth.admin.app("app").tenants.list()) {
2363
+ for await (const tenant of auth.admin.app("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f").tenants.list()) {
2217
2364
  console.log(tenant);
2218
2365
  }
2219
- const page = await auth.admin.app("app").tenants.list({}, { timeout: 5_000 });
2366
+ const page = await auth.admin.app("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f").tenants.list({}, { timeout: 5_000 });
2220
2367
  ```
2221
2368
 
2222
2369
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#get-v1-apps-app-id-tenants)
@@ -2227,6 +2374,8 @@ const page = await auth.admin.app("app").tenants.list({}, { timeout: 5_000 });
2227
2374
 
2228
2375
  Get a single tenant by id or display id.
2229
2376
 
2377
+ - Credential: [`workspace_key`](#workspace_key)
2378
+
2230
2379
  | Status | Error class | Codes | When |
2231
2380
  |---|---|---|---|
2232
2381
  | 401 | `AuthenticationError` | `UNAUTHORIZED` | Missing or invalid bearer token. |
@@ -2235,7 +2384,7 @@ Get a single tenant by id or display id.
2235
2384
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
2236
2385
 
2237
2386
  ```ts
2238
- const tenant = await auth.admin.app("app").tenants.retrieve("tenant");
2387
+ const tenant = await auth.admin.app("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f").tenants.retrieve("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f");
2239
2388
  ```
2240
2389
 
2241
2390
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#get-v1-apps-app-id-tenants-tenant-id)
@@ -2247,6 +2396,7 @@ const tenant = await auth.admin.app("app").tenants.retrieve("tenant");
2247
2396
  Update a tenant (display_name, slug, status, metadata).
2248
2397
 
2249
2398
  - Params: `Auth.AppScopedTenantUpdateParams`
2399
+ - Credential: [`workspace_key`](#workspace_key)
2250
2400
 
2251
2401
  | Status | Error class | Codes | When |
2252
2402
  |---|---|---|---|
@@ -2258,7 +2408,7 @@ Update a tenant (display_name, slug, status, metadata).
2258
2408
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
2259
2409
 
2260
2410
  ```ts
2261
- const tenant = await auth.admin.app("app").tenants.update("tenant", { display_name: "string" });
2411
+ const tenant = await auth.admin.app("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f").tenants.update("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f", { display_name: "Acme Inc" });
2262
2412
  ```
2263
2413
 
2264
2414
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#patch-v1-apps-app-id-tenants-tenant-id)
@@ -2273,6 +2423,7 @@ Invite an email address into a tenant. Returns the plaintext code once; only its
2273
2423
 
2274
2424
  - Params: `Auth.AppScopedTenantInviteCreateParams`
2275
2425
  - Idempotent: an `Idempotency-Key` is sent automatically and reused on retries
2426
+ - Credential: [`workspace_key`](#workspace_key)
2276
2427
 
2277
2428
  | Status | Error class | Codes | When |
2278
2429
  |---|---|---|---|
@@ -2283,7 +2434,7 @@ Invite an email address into a tenant. Returns the plaintext code once; only its
2283
2434
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
2284
2435
 
2285
2436
  ```ts
2286
- const createTenantInvite = await auth.admin.app("app").tenants.invites.create("tenant", { email: "alice@example.com", role_name: "member" });
2437
+ const createTenantInvite = await auth.admin.app("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f").tenants.invites.create("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f", { email: "alice@example.com", role_name: "member" });
2287
2438
  ```
2288
2439
 
2289
2440
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#post-v1-apps-app-id-tenants-tenant-id-invites)
@@ -2294,6 +2445,8 @@ const createTenantInvite = await auth.admin.app("app").tenants.invites.create("t
2294
2445
 
2295
2446
  Revoke a pending invite. Idempotent — a second revoke is a 204 no-op and fires no second webhook.
2296
2447
 
2448
+ - Credential: [`workspace_key`](#workspace_key)
2449
+
2297
2450
  | Status | Error class | Codes | When |
2298
2451
  |---|---|---|---|
2299
2452
  | 401 | `AuthenticationError` | `UNAUTHORIZED` | Missing, invalid or expired bearer token. |
@@ -2301,7 +2454,7 @@ Revoke a pending invite. Idempotent — a second revoke is a 204 no-op and fires
2301
2454
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
2302
2455
 
2303
2456
  ```ts
2304
- await auth.admin.app("app").tenants.invites.delete("tenant", "invite");
2457
+ await auth.admin.app("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f").tenants.invites.delete("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f", "3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f");
2305
2458
  ```
2306
2459
 
2307
2460
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#delete-v1-apps-app-id-tenants-tenant-id-invites-invite-id)
@@ -2314,6 +2467,7 @@ List a tenant's invites, newest first. Optional `?status=pending|revoked|expired
2314
2467
 
2315
2468
  - Params: `Auth.AppScopedTenantInviteListParams`
2316
2469
  - Paginated: `for await` iterates every item; `await` gives one page
2470
+ - Credential: [`workspace_key`](#workspace_key)
2317
2471
 
2318
2472
  | Status | Error class | Codes | When |
2319
2473
  |---|---|---|---|
@@ -2323,10 +2477,10 @@ List a tenant's invites, newest first. Optional `?status=pending|revoked|expired
2323
2477
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
2324
2478
 
2325
2479
  ```ts
2326
- for await (const invite of auth.admin.app("app").tenants.invites.list("tenant")) {
2480
+ for await (const invite of auth.admin.app("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f").tenants.invites.list("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f")) {
2327
2481
  console.log(invite);
2328
2482
  }
2329
- const page = await auth.admin.app("app").tenants.invites.list("tenant", {}, { timeout: 5_000 });
2483
+ const page = await auth.admin.app("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f").tenants.invites.list("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f", {}, { timeout: 5_000 });
2330
2484
  ```
2331
2485
 
2332
2486
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#get-v1-apps-app-id-tenants-tenant-id-invites)
@@ -2341,6 +2495,7 @@ Add a member to a tenant.
2341
2495
 
2342
2496
  - Params: `Auth.AppScopedTenantMemberCreateParams`
2343
2497
  - Idempotent: an `Idempotency-Key` is sent automatically and reused on retries
2498
+ - Credential: [`workspace_key`](#workspace_key)
2344
2499
 
2345
2500
  | Status | Error class | Codes | When |
2346
2501
  |---|---|---|---|
@@ -2352,7 +2507,7 @@ Add a member to a tenant.
2352
2507
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
2353
2508
 
2354
2509
  ```ts
2355
- const addMember = await auth.admin.app("app").tenants.members.create("tenant", { role_name: "member" });
2510
+ const addMember = await auth.admin.app("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f").tenants.members.create("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f", { role_name: "member" });
2356
2511
  ```
2357
2512
 
2358
2513
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#post-v1-apps-app-id-tenants-tenant-id-members)
@@ -2363,6 +2518,8 @@ const addMember = await auth.admin.app("app").tenants.members.create("tenant", {
2363
2518
 
2364
2519
  Remove a member from a tenant.
2365
2520
 
2521
+ - Credential: [`workspace_key`](#workspace_key)
2522
+
2366
2523
  | Status | Error class | Codes | When |
2367
2524
  |---|---|---|---|
2368
2525
  | 401 | `AuthenticationError` | `UNAUTHORIZED` | Missing or invalid bearer token. |
@@ -2371,7 +2528,7 @@ Remove a member from a tenant.
2371
2528
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
2372
2529
 
2373
2530
  ```ts
2374
- await auth.admin.app("app").tenants.members.delete("tenant", "account");
2531
+ await auth.admin.app("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f").tenants.members.delete("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f", "3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f");
2375
2532
  ```
2376
2533
 
2377
2534
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#delete-v1-apps-app-id-tenants-tenant-id-members-account-id)
@@ -2384,6 +2541,7 @@ List members of a tenant, newest membership first. Filter by `?search=` and/or `
2384
2541
 
2385
2542
  - Params: `Auth.AppScopedTenantMemberListParams`
2386
2543
  - Paginated: `for await` iterates every item; `await` gives one page
2544
+ - Credential: [`workspace_key`](#workspace_key)
2387
2545
 
2388
2546
  | Status | Error class | Codes | When |
2389
2547
  |---|---|---|---|
@@ -2394,10 +2552,10 @@ List members of a tenant, newest membership first. Filter by `?search=` and/or `
2394
2552
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
2395
2553
 
2396
2554
  ```ts
2397
- for await (const member of auth.admin.app("app").tenants.members.list("tenant")) {
2555
+ for await (const member of auth.admin.app("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f").tenants.members.list("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f")) {
2398
2556
  console.log(member);
2399
2557
  }
2400
- const page = await auth.admin.app("app").tenants.members.list("tenant", {}, { timeout: 5_000 });
2558
+ const page = await auth.admin.app("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f").tenants.members.list("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f", {}, { timeout: 5_000 });
2401
2559
  ```
2402
2560
 
2403
2561
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#get-v1-apps-app-id-tenants-tenant-id-members)
@@ -2409,6 +2567,7 @@ const page = await auth.admin.app("app").tenants.members.list("tenant", {}, { ti
2409
2567
  Change a member's role inside a tenant.
2410
2568
 
2411
2569
  - Params: `Auth.AppScopedTenantMemberUpdateParams`
2570
+ - Credential: [`workspace_key`](#workspace_key)
2412
2571
 
2413
2572
  | Status | Error class | Codes | When |
2414
2573
  |---|---|---|---|
@@ -2419,7 +2578,7 @@ Change a member's role inside a tenant.
2419
2578
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
2420
2579
 
2421
2580
  ```ts
2422
- const tenantMembership = await auth.admin.app("app").tenants.members.update("tenant", "account", { role_name: "admin" });
2581
+ const tenantMembership = await auth.admin.app("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f").tenants.members.update("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f", "3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f", { role_name: "admin" });
2423
2582
  ```
2424
2583
 
2425
2584
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#patch-v1-apps-app-id-tenants-tenant-id-members-account-id)
@@ -2432,6 +2591,8 @@ const tenantMembership = await auth.admin.app("app").tenants.members.update("ten
2432
2591
 
2433
2592
  Get the unverified-account expiry setting and how many accounts it deleted in the last 30 days.
2434
2593
 
2594
+ - Credential: [`workspace_key`](#workspace_key)
2595
+
2435
2596
  | Status | Error class | Codes | When |
2436
2597
  |---|---|---|---|
2437
2598
  | 401 | `AuthenticationError` | `UNAUTHORIZED` | Missing or invalid bearer token. |
@@ -2440,7 +2601,7 @@ Get the unverified-account expiry setting and how many accounts it deleted in th
2440
2601
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
2441
2602
 
2442
2603
  ```ts
2443
- const unverifiedAccountExpiryStatus = await auth.admin.app("app").unverifiedAccountExpiry.retrieve();
2604
+ const unverifiedAccountExpiryStatus = await auth.admin.app("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f").unverifiedAccountExpiry.retrieve();
2444
2605
  ```
2445
2606
 
2446
2607
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#get-v1-apps-app-id-unverified-account-expiry)
@@ -2455,6 +2616,7 @@ Permanently deletes every account that qualifies under the app's `unverified_acc
2455
2616
 
2456
2617
  - Params: `Auth.UnverifiedAccountExpiryRunParams`
2457
2618
  - Idempotent: an `Idempotency-Key` is sent automatically and reused on retries
2619
+ - Credential: [`workspace_key`](#workspace_key)
2458
2620
 
2459
2621
  | Status | Error class | Codes | When |
2460
2622
  |---|---|---|---|
@@ -2467,7 +2629,7 @@ Permanently deletes every account that qualifies under the app's `unverified_acc
2467
2629
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
2468
2630
 
2469
2631
  ```ts
2470
- const unverifiedAccountExpiryRun = await auth.admin.app("app").unverifiedAccountExpiry.run({ dry_run: false });
2632
+ const unverifiedAccountExpiryRun = await auth.admin.app("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f").unverifiedAccountExpiry.run({ dry_run: false });
2471
2633
  ```
2472
2634
 
2473
2635
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#post-v1-apps-app-id-unverified-account-expiry-run)
@@ -2482,6 +2644,8 @@ Get the per-app passkey (WebAuthn) relying-party settings.
2482
2644
 
2483
2645
  Returns defaults with `enabled: false` and a null `rp_id` when passkeys have never been configured for the app.
2484
2646
 
2647
+ - Credential: [`workspace_key`](#workspace_key)
2648
+
2485
2649
  | Status | Error class | Codes | When |
2486
2650
  |---|---|---|---|
2487
2651
  | 401 | `AuthenticationError` | `UNAUTHORIZED` | Missing or invalid bearer token. |
@@ -2490,7 +2654,7 @@ Returns defaults with `enabled: false` and a null `rp_id` when passkeys have nev
2490
2654
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
2491
2655
 
2492
2656
  ```ts
2493
- const webauthnConfig = await auth.admin.app("app").webauthnConfig.retrieve();
2657
+ const webauthnConfig = await auth.admin.app("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f").webauthnConfig.retrieve();
2494
2658
  ```
2495
2659
 
2496
2660
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#get-v1-apps-app-id-webauthn-config)
@@ -2502,6 +2666,7 @@ const webauthnConfig = await auth.admin.app("app").webauthnConfig.retrieve();
2502
2666
  Set the per-app passkey (WebAuthn) relying-party settings.
2503
2667
 
2504
2668
  - Params: `Auth.WebauthnConfigUpdateParams`
2669
+ - Credential: [`workspace_key`](#workspace_key)
2505
2670
 
2506
2671
  | Status | Error class | Codes | When |
2507
2672
  |---|---|---|---|
@@ -2512,7 +2677,7 @@ Set the per-app passkey (WebAuthn) relying-party settings.
2512
2677
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
2513
2678
 
2514
2679
  ```ts
2515
- const webauthnConfig = await auth.admin.app("app").webauthnConfig.update({ enabled: true, rp_id: "example.com" });
2680
+ const webauthnConfig = await auth.admin.app("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f").webauthnConfig.update({ enabled: true, rp_id: "example.com" });
2516
2681
  ```
2517
2682
 
2518
2683
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#put-v1-apps-app-id-webauthn-config)
@@ -2529,6 +2694,7 @@ Deliveries are signed with the Standard Webhooks headers `webhook-id`, `webhook-
2529
2694
 
2530
2695
  - Params: `Auth.WebhookEndpointCreateParams`
2531
2696
  - Idempotent: an `Idempotency-Key` is sent automatically and reused on retries
2697
+ - Credential: [`workspace_key`](#workspace_key)
2532
2698
 
2533
2699
  | Status | Error class | Codes | When |
2534
2700
  |---|---|---|---|
@@ -2541,7 +2707,7 @@ Deliveries are signed with the Standard Webhooks headers `webhook-id`, `webhook-
2541
2707
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
2542
2708
 
2543
2709
  ```ts
2544
- const webhookEndpointWithSecret = await auth.admin.app("3f0c1a2b-4c5d-4e6f-8a9b-0c1d2e3f4a5b").webhooks.endpoints.create({ url: "https://api.acme.com/webhooks" });
2710
+ const webhookEndpointWithSecret = await auth.admin.app("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f").webhooks.endpoints.create({ url: "https://api.acme.com/webhooks" });
2545
2711
  ```
2546
2712
 
2547
2713
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#post-v1-apps-app-id-webhooks-endpoints)
@@ -2554,6 +2720,8 @@ Delete a webhook endpoint.
2554
2720
 
2555
2721
  Queued retries are dropped. Its past attempts stay in the event log until the events expire.
2556
2722
 
2723
+ - Credential: [`workspace_key`](#workspace_key)
2724
+
2557
2725
  | Status | Error class | Codes | When |
2558
2726
  |---|---|---|---|
2559
2727
  | 400 | `BadRequestError` | `BAD_REQUEST`, `VALIDATION_ERROR` | `endpoint_id` is not a UUID. |
@@ -2563,7 +2731,7 @@ Queued retries are dropped. Its past attempts stay in the event log until the ev
2563
2731
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
2564
2732
 
2565
2733
  ```ts
2566
- await auth.admin.app("3f0c1a2b-4c5d-4e6f-8a9b-0c1d2e3f4a5b").webhooks.endpoints.delete("3f0c1a2b-4c5d-4e6f-8a9b-0c1d2e3f4a5b");
2734
+ await auth.admin.app("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f").webhooks.endpoints.delete("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f");
2567
2735
  ```
2568
2736
 
2569
2737
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#delete-v1-apps-app-id-webhooks-endpoints-endpoint-id)
@@ -2577,6 +2745,7 @@ List webhook endpoints, oldest first.
2577
2745
  At most 10, so this is always one page. Signing secrets are never returned here, only their last 4 characters.
2578
2746
 
2579
2747
  - Paginated: `for await` iterates every item; `await` gives one page
2748
+ - Credential: [`workspace_key`](#workspace_key)
2580
2749
 
2581
2750
  | Status | Error class | Codes | When |
2582
2751
  |---|---|---|---|
@@ -2587,7 +2756,7 @@ At most 10, so this is always one page. Signing secrets are never returned here,
2587
2756
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
2588
2757
 
2589
2758
  ```ts
2590
- for await (const endpoint of auth.admin.app("3f0c1a2b-4c5d-4e6f-8a9b-0c1d2e3f4a5b").webhooks.endpoints.list()) {
2759
+ for await (const endpoint of auth.admin.app("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f").webhooks.endpoints.list()) {
2591
2760
  console.log(endpoint);
2592
2761
  }
2593
2762
  ```
@@ -2602,6 +2771,8 @@ Get one webhook endpoint (without its secret).
2602
2771
 
2603
2772
  The endpoint's URL, subscription, on/off state, failure streak and the last 4 characters of its signing secret. The full secret is only returned when the endpoint is created or its secret is rotated.
2604
2773
 
2774
+ - Credential: [`workspace_key`](#workspace_key)
2775
+
2605
2776
  | Status | Error class | Codes | When |
2606
2777
  |---|---|---|---|
2607
2778
  | 400 | `BadRequestError` | `BAD_REQUEST`, `VALIDATION_ERROR` | `endpoint_id` is not a UUID. |
@@ -2611,7 +2782,7 @@ The endpoint's URL, subscription, on/off state, failure streak and the last 4 ch
2611
2782
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
2612
2783
 
2613
2784
  ```ts
2614
- const webhookEndpoint = await auth.admin.app("3f0c1a2b-4c5d-4e6f-8a9b-0c1d2e3f4a5b").webhooks.endpoints.retrieve("3f0c1a2b-4c5d-4e6f-8a9b-0c1d2e3f4a5b");
2785
+ const webhookEndpoint = await auth.admin.app("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f").webhooks.endpoints.retrieve("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f");
2615
2786
  ```
2616
2787
 
2617
2788
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#get-v1-apps-app-id-webhooks-endpoints-endpoint-id)
@@ -2626,6 +2797,7 @@ For `grace_hours` (default 24) the previous secret keeps signing too: `webhook-s
2626
2797
 
2627
2798
  - Params: `Auth.WebhookEndpointRotateSecretParams`
2628
2799
  - Idempotent: an `Idempotency-Key` is sent automatically and reused on retries
2800
+ - Credential: [`workspace_key`](#workspace_key)
2629
2801
 
2630
2802
  | Status | Error class | Codes | When |
2631
2803
  |---|---|---|---|
@@ -2637,7 +2809,7 @@ For `grace_hours` (default 24) the previous secret keeps signing too: `webhook-s
2637
2809
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
2638
2810
 
2639
2811
  ```ts
2640
- const webhookEndpointWithSecret = await auth.admin.app("3f0c1a2b-4c5d-4e6f-8a9b-0c1d2e3f4a5b").webhooks.endpoints.rotateSecret("3f0c1a2b-4c5d-4e6f-8a9b-0c1d2e3f4a5b", { grace_hours: 24 });
2812
+ const webhookEndpointWithSecret = await auth.admin.app("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f").webhooks.endpoints.rotateSecret("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f", { grace_hours: 24 });
2641
2813
  ```
2642
2814
 
2643
2815
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#post-v1-apps-app-id-webhooks-endpoints-endpoint-id-rotate-secret)
@@ -2652,6 +2824,7 @@ Delivers the catalogue example of `event_type`, signed like any delivery, with `
2652
2824
 
2653
2825
  - Params: `Auth.WebhookEndpointTestParams`
2654
2826
  - Idempotent: an `Idempotency-Key` is sent automatically and reused on retries
2827
+ - Credential: [`workspace_key`](#workspace_key)
2655
2828
 
2656
2829
  | Status | Error class | Codes | When |
2657
2830
  |---|---|---|---|
@@ -2663,7 +2836,7 @@ Delivers the catalogue example of `event_type`, signed like any delivery, with `
2663
2836
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
2664
2837
 
2665
2838
  ```ts
2666
- const webhookDeliveryAttempt = await auth.admin.app("3f0c1a2b-4c5d-4e6f-8a9b-0c1d2e3f4a5b").webhooks.endpoints.test("3f0c1a2b-4c5d-4e6f-8a9b-0c1d2e3f4a5b", { event_type: "user.signup" });
2839
+ const webhookDeliveryAttempt = await auth.admin.app("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f").webhooks.endpoints.test("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f", { event_type: "user.signup" });
2667
2840
  ```
2668
2841
 
2669
2842
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#post-v1-apps-app-id-webhooks-endpoints-endpoint-id-test)
@@ -2678,6 +2851,7 @@ Only the fields sent change. `enabled: true` (or a new URL) also clears an autom
2678
2851
 
2679
2852
  - Params: `Auth.WebhookEndpointUpdateParams`
2680
2853
  - Idempotent: an `Idempotency-Key` is sent automatically and reused on retries
2854
+ - Credential: [`workspace_key`](#workspace_key)
2681
2855
 
2682
2856
  | Status | Error class | Codes | When |
2683
2857
  |---|---|---|---|
@@ -2690,7 +2864,7 @@ Only the fields sent change. `enabled: true` (or a new URL) also clears an autom
2690
2864
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
2691
2865
 
2692
2866
  ```ts
2693
- const webhookEndpoint = await auth.admin.app("3f0c1a2b-4c5d-4e6f-8a9b-0c1d2e3f4a5b").webhooks.endpoints.update("3f0c1a2b-4c5d-4e6f-8a9b-0c1d2e3f4a5b", { description: "Production receiver" });
2867
+ const webhookEndpoint = await auth.admin.app("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f").webhooks.endpoints.update("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f", { description: "Production receiver" });
2694
2868
  ```
2695
2869
 
2696
2870
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#patch-v1-apps-app-id-webhooks-endpoints-endpoint-id)
@@ -2705,6 +2879,8 @@ List the events an endpoint can subscribe to.
2705
2879
 
2706
2880
  Each type with a plain description, its group and an example `data` object (what "Send test" delivers).
2707
2881
 
2882
+ - Credential: [`workspace_key`](#workspace_key)
2883
+
2708
2884
  | Status | Error class | Codes | When |
2709
2885
  |---|---|---|---|
2710
2886
  | 401 | `AuthenticationError` | `UNAUTHORIZED` | Missing or invalid bearer token. |
@@ -2713,7 +2889,7 @@ Each type with a plain description, its group and an example `data` object (what
2713
2889
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
2714
2890
 
2715
2891
  ```ts
2716
- const listWebhookEventTypes = await auth.admin.app("3f0c1a2b-4c5d-4e6f-8a9b-0c1d2e3f4a5b").webhooks.eventTypes.list();
2892
+ const listWebhookEventTypes = await auth.admin.app("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f").webhooks.eventTypes.list();
2717
2893
  ```
2718
2894
 
2719
2895
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#get-v1-apps-app-id-webhooks-event-types)
@@ -2730,6 +2906,7 @@ Every event logged while at least one endpoint existed, with the latest state pe
2730
2906
 
2731
2907
  - Params: `Auth.WebhookEventListParams`
2732
2908
  - Paginated: `for await` iterates every item; `await` gives one page
2909
+ - Credential: [`workspace_key`](#workspace_key)
2733
2910
 
2734
2911
  | Status | Error class | Codes | When |
2735
2912
  |---|---|---|---|
@@ -2740,10 +2917,10 @@ Every event logged while at least one endpoint existed, with the latest state pe
2740
2917
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
2741
2918
 
2742
2919
  ```ts
2743
- for await (const event of auth.admin.app("3f0c1a2b-4c5d-4e6f-8a9b-0c1d2e3f4a5b").webhooks.events.list()) {
2920
+ for await (const event of auth.admin.app("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f").webhooks.events.list()) {
2744
2921
  console.log(event);
2745
2922
  }
2746
- const page = await auth.admin.app("3f0c1a2b-4c5d-4e6f-8a9b-0c1d2e3f4a5b").webhooks.events.list({}, { timeout: 5_000 });
2923
+ const page = await auth.admin.app("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f").webhooks.events.list({}, { timeout: 5_000 });
2747
2924
  ```
2748
2925
 
2749
2926
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#get-v1-apps-app-id-webhooks-events)
@@ -2758,6 +2935,7 @@ Same body and `webhook-id`, freshly signed, to every switched-on endpoint that r
2758
2935
 
2759
2936
  - Params: `Auth.WebhookEventReplayParams`
2760
2937
  - Idempotent: an `Idempotency-Key` is sent automatically and reused on retries
2938
+ - Credential: [`workspace_key`](#workspace_key)
2761
2939
 
2762
2940
  | Status | Error class | Codes | When |
2763
2941
  |---|---|---|---|
@@ -2769,7 +2947,7 @@ Same body and `webhook-id`, freshly signed, to every switched-on endpoint that r
2769
2947
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
2770
2948
 
2771
2949
  ```ts
2772
- const replayWebhookEvent = await auth.admin.app("3f0c1a2b-4c5d-4e6f-8a9b-0c1d2e3f4a5b").webhooks.events.replay("event");
2950
+ const replayWebhookEvent = await auth.admin.app("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f").webhooks.events.replay("evt_3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f");
2773
2951
  ```
2774
2952
 
2775
2953
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#post-v1-apps-app-id-webhooks-events-event-id-replay)
@@ -2782,6 +2960,8 @@ Get one webhook event with every delivery attempt.
2782
2960
 
2783
2961
  The event as it was sent (`type`, `data`) and every delivery attempt to every endpoint, with its trigger, HTTP status, error and the start of the response body. Use it to debug a receiver, then `replay` to send it again.
2784
2962
 
2963
+ - Credential: [`workspace_key`](#workspace_key)
2964
+
2785
2965
  | Status | Error class | Codes | When |
2786
2966
  |---|---|---|---|
2787
2967
  | 400 | `BadRequestError` | `BAD_REQUEST`, `VALIDATION_ERROR` | `event_id` is not `evt_<uuid>`. |
@@ -2791,7 +2971,7 @@ The event as it was sent (`type`, `data`) and every delivery attempt to every en
2791
2971
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
2792
2972
 
2793
2973
  ```ts
2794
- const webhookEventDetail = await auth.admin.app("3f0c1a2b-4c5d-4e6f-8a9b-0c1d2e3f4a5b").webhooks.events.retrieve("event");
2974
+ const webhookEventDetail = await auth.admin.app("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f").webhooks.events.retrieve("evt_3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f");
2795
2975
  ```
2796
2976
 
2797
2977
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#get-v1-apps-app-id-webhooks-events-event-id)
@@ -2821,7 +3001,7 @@ Materialises a pre-allocated invite into a real EndUser: creates account + prima
2821
3001
  | 503 | `InternalServerError` | `SERVICE_UNAVAILABLE` | The service is momentarily busy (`SERVICE_UNAVAILABLE`): too many password hashes or verifications are already running or queued on the instance that took the request. Nothing was changed and no attempt was counted. Retry after the number of seconds in the `Retry-After` header (also in `details.retry_after_seconds`). Decided by instance load only, never by the identifier or the account, so it reveals nothing about whether an account exists. |
2822
3002
 
2823
3003
  ```ts
2824
- const result = await auth.consumer("app").acceptInvite({ code: "inv_x7H2k3jLqXr", password: "CorrectHorseBatteryStaple" });
3004
+ const result = await auth.consumer("my-app").acceptInvite({ code: "inv_x7H2k3jLqXr", password: "CorrectHorseBatteryStaple" });
2825
3005
  ```
2826
3006
 
2827
3007
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#post-app-slug-v1-auth-accept-invite)
@@ -2849,7 +3029,7 @@ Materialises a pending `tenant_invite` into a real EndUser: creates account + ve
2849
3029
  | 503 | `InternalServerError` | `SERVICE_UNAVAILABLE` | The service is momentarily busy (`SERVICE_UNAVAILABLE`): too many password hashes or verifications are already running or queued on the instance that took the request. Nothing was changed and no attempt was counted. Retry after the number of seconds in the `Retry-After` header (also in `details.retry_after_seconds`). Decided by instance load only, never by the identifier or the account, so it reveals nothing about whether an account exists. |
2850
3030
 
2851
3031
  ```ts
2852
- const consumerToken = await auth.consumer("app").acceptTenantInvite({ code: "tinv_9dK2mQ7pR4vXbN1sT6yZ0hLcJeW3fAgU", password: "CorrectHorseBatteryStaple" });
3032
+ const consumerToken = await auth.consumer("my-app").acceptTenantInvite({ code: "tinv_9dK2mQ7pR4vXbN1sT6yZ0hLcJeW3fAgU", password: "CorrectHorseBatteryStaple" });
2853
3033
  ```
2854
3034
 
2855
3035
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#post-app-slug-v1-auth-accept-tenant-invite)
@@ -2870,7 +3050,7 @@ Exchange a `heimdall_code` (received on the web-flow callback redirect) for Auth
2870
3050
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
2871
3051
 
2872
3052
  ```ts
2873
- const exchange = await auth.consumer("app").exchangeProviderCode({ code: "string" });
3053
+ const exchange = await auth.consumer("my-app").exchangeProviderCode({ code: "hc_8f14e45fceea167a5a36dedd4bea2543" });
2874
3054
  ```
2875
3055
 
2876
3056
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#post-app-slug-v1-auth-oauth-exchange)
@@ -2896,7 +3076,9 @@ Destroys the session that owns the refresh token. The matching access token cont
2896
3076
  | 429 | `RateLimitError` | `RATE_LIMITED` | Per-IP rate limit (`RATE_LIMITED`): this path is in the `default` bucket — 60 requests per minute per IP over a sliding 60-second window, shared across pods through Redis. Applied by `RateLimitMiddleware` before the handler runs, so it is reachable whatever the body says, and the response carries `Retry-After` in seconds. |
2897
3077
 
2898
3078
  ```ts
2899
- await auth.consumer("app").logout({ refresh_token: "string" });
3079
+ await auth.consumer("my-app").logout({
3080
+ refresh_token: "eyJhbGciOiJSUzI1NiIsImtpZCI6ImsxIn0.eyJ0eXAiOiJyZWZyZXNoIn0.c2lnbmF0dXJl",
3081
+ });
2900
3082
  ```
2901
3083
 
2902
3084
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#post-app-slug-v1-auth-logout)
@@ -2922,7 +3104,9 @@ Rotates the refresh token on every call — the previous refresh token is revoke
2922
3104
  | 429 | `RateLimitError` | `RATE_LIMITED` | Per-IP rate limit (`RATE_LIMITED`): this path is in the `auth` bucket — 10 requests per minute per IP over a sliding 60-second window, shared across pods through Redis. Applied by `RateLimitMiddleware` before the handler runs, so it is reachable whatever the body says, and the response carries `Retry-After` in seconds. |
2923
3105
 
2924
3106
  ```ts
2925
- const consumerToken = await auth.consumer("app").refresh({ refresh_token: "string" });
3107
+ const consumerToken = await auth.consumer("my-app").refresh({
3108
+ refresh_token: "eyJhbGciOiJSUzI1NiIsImtpZCI6ImsxIn0.eyJ0eXAiOiJyZWZyZXNoIn0.c2lnbmF0dXJl",
3109
+ });
2926
3110
  ```
2927
3111
 
2928
3112
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#post-app-slug-v1-auth-refresh)
@@ -2940,6 +3124,7 @@ PAK-required: `auth.user.password-reset.create`. Returns `{ code, expires_at }`
2940
3124
  Renamed from /auth/request-reset.
2941
3125
 
2942
3126
  - Params: `Auth.RequestPasswordResetParams`
3127
+ - Credential: [`workspace_key`](#workspace_key)
2943
3128
 
2944
3129
  | Status | Error class | Codes | When |
2945
3130
  |---|---|---|---|
@@ -2951,7 +3136,7 @@ Renamed from /auth/request-reset.
2951
3136
  | 429 | `RateLimitError` | `RATE_LIMITED` | Per-IP rate limit (`RATE_LIMITED`): this path is in the `auth` bucket — 10 requests per minute per IP over a sliding 60-second window, shared across pods through Redis. Applied by `RateLimitMiddleware` before the handler runs, so it is reachable whatever the body says, and the response carries `Retry-After` in seconds. |
2952
3137
 
2953
3138
  ```ts
2954
- const consumerCodeIssue = await auth.consumer("app").requestPasswordReset({ email: "jane@example.com" });
3139
+ const consumerCodeIssue = await auth.consumer("my-app").requestPasswordReset({ email: "jane@example.com" });
2955
3140
  ```
2956
3141
 
2957
3142
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#post-app-slug-v1-auth-request-password-reset)
@@ -2969,6 +3154,7 @@ Returns `{ code, expires_at }` for delivery via the customer's own SMTP / SMS pr
2969
3154
  Issuing a code invalidates any previously issued, unconsumed code for the same contact — including the one auto-emailed at signup. Only the most recently minted code is accepted.
2970
3155
 
2971
3156
  - Params: `Auth.RequestVerificationParams`
3157
+ - Credential: [`workspace_key`](#workspace_key)
2972
3158
 
2973
3159
  | Status | Error class | Codes | When |
2974
3160
  |---|---|---|---|
@@ -2980,7 +3166,7 @@ Issuing a code invalidates any previously issued, unconsumed code for the same c
2980
3166
  | 429 | `RateLimitError` | `RATE_LIMITED` | Per-IP rate limit (`RATE_LIMITED`): this path is in the `auth` bucket — 10 requests per minute per IP over a sliding 60-second window, shared across pods through Redis. Applied by `RateLimitMiddleware` before the handler runs, so it is reachable whatever the body says, and the response carries `Retry-After` in seconds. |
2981
3167
 
2982
3168
  ```ts
2983
- const consumerCodeIssue = await auth.consumer("app").requestVerification({ email: "jane@example.com" });
3169
+ const consumerCodeIssue = await auth.consumer("my-app").requestVerification({ email: "jane@example.com" });
2984
3170
  ```
2985
3171
 
2986
3172
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#post-app-slug-v1-auth-request-verification)
@@ -3007,7 +3193,7 @@ Consumes the 6-digit reset code (single-use). On success the password is updated
3007
3193
  | 503 | `InternalServerError` | `SERVICE_UNAVAILABLE` | The service is momentarily busy (`SERVICE_UNAVAILABLE`): too many password hashes or verifications are already running or queued on the instance that took the request. Nothing was changed and no attempt was counted. Retry after the number of seconds in the `Retry-After` header (also in `details.retry_after_seconds`). Decided by instance load only, never by the identifier or the account, so it reveals nothing about whether an account exists. |
3008
3194
 
3009
3195
  ```ts
3010
- await auth.consumer("app").resetPassword({ code: "123456", new_password: "string" });
3196
+ await auth.consumer("my-app").resetPassword({ code: "123456", new_password: "a new long passphrase 42" });
3011
3197
  ```
3012
3198
 
3013
3199
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#post-app-slug-v1-auth-reset-password)
@@ -3021,6 +3207,7 @@ Mint + dispatch a password-reset email via Mail (PAK-required).
3021
3207
  Same shape as `send-verification-email` but for the reset slot. PAK perm: `auth.user.password-reset.send-email`. Same loud precondition errors. Email-only.
3022
3208
 
3023
3209
  - Params: `Auth.SendPasswordResetEmailParams`
3210
+ - Credential: [`workspace_key`](#workspace_key)
3024
3211
 
3025
3212
  | Status | Error class | Codes | When |
3026
3213
  |---|---|---|---|
@@ -3034,7 +3221,7 @@ Same shape as `send-verification-email` but for the reset slot. PAK perm: `auth.
3034
3221
  | 503 | `InternalServerError` | `SERVICE_UNAVAILABLE` | Mail dispatch failed or client not wired. |
3035
3222
 
3036
3223
  ```ts
3037
- const consumerCodeDispatch = await auth.consumer("app").sendPasswordResetEmail({ email: "jane@example.com" });
3224
+ const consumerCodeDispatch = await auth.consumer("my-app").sendPasswordResetEmail({ email: "jane@example.com" });
3038
3225
  ```
3039
3226
 
3040
3227
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#post-app-slug-v1-auth-send-password-reset-email)
@@ -3061,6 +3248,7 @@ On success returns `{ expires_at }`. The plaintext code is NOT returned. Returns
3061
3248
  Issuing a code invalidates any previously issued, unconsumed code for the same contact — including the one auto-emailed at signup. Only the most recently minted code is accepted.
3062
3249
 
3063
3250
  - Params: `Auth.SendVerificationEmailParams`
3251
+ - Credential: [`workspace_key`](#workspace_key)
3064
3252
 
3065
3253
  | Status | Error class | Codes | When |
3066
3254
  |---|---|---|---|
@@ -3074,7 +3262,7 @@ Issuing a code invalidates any previously issued, unconsumed code for the same c
3074
3262
  | 503 | `InternalServerError` | `SERVICE_UNAVAILABLE` | Mail dispatch failed or client not wired. |
3075
3263
 
3076
3264
  ```ts
3077
- const consumerCodeDispatch = await auth.consumer("app").sendVerificationEmail({ email: "jane@example.com" });
3265
+ const consumerCodeDispatch = await auth.consumer("my-app").sendVerificationEmail({ email: "jane@example.com" });
3078
3266
  ```
3079
3267
 
3080
3268
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#post-app-slug-v1-auth-send-verification-email)
@@ -3101,7 +3289,7 @@ Authenticate with username or verified primary email + password. When the accoun
3101
3289
  | 503 | `InternalServerError` | `SERVICE_UNAVAILABLE` | The service is momentarily busy (`SERVICE_UNAVAILABLE`): too many password hashes or verifications are already running or queued on the instance that took the request. Nothing was changed and no attempt was counted. Retry after the number of seconds in the `Retry-After` header (also in `details.retry_after_seconds`). Decided by instance load only, never by the identifier or the account, so it reveals nothing about whether an account exists. |
3102
3290
 
3103
3291
  ```ts
3104
- const result = await auth.consumer("app").signin({ identifier: "jane_doe", password: "string" });
3292
+ const result = await auth.consumer("my-app").signin({ identifier: "jane_doe", password: "correct horse battery staple" });
3105
3293
  ```
3106
3294
 
3107
3295
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#post-app-slug-v1-auth-signin)
@@ -3124,7 +3312,10 @@ Submit the provider-issued ID token from a native client (iOS ASAuthorizationCon
3124
3312
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
3125
3313
 
3126
3314
  ```ts
3127
- const result = await auth.consumer("app").signinWithProvider("provider", { id_token: "string", nonce: "string" });
3315
+ const result = await auth.consumer("my-app").signinWithProvider("google", {
3316
+ id_token: "eyJhbGciOiJSUzI1NiIsImtpZCI6ImsxIn0.eyJzdWIiOiJ1c3JfMSJ9.c2lnbmF0dXJl",
3317
+ nonce: "n-0S6_WzA2Mj",
3318
+ });
3128
3319
  ```
3129
3320
 
3130
3321
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#post-app-slug-v1-auth-oauth-provider)
@@ -3142,12 +3333,13 @@ When the app runs `auth_config.mfa: "required"`, the account is still created bu
3142
3333
  When the app has bot protection on (`auth_config.bot_protection`, Cloudflare Turnstile or hCaptcha), send the widget response as `bot_protection_token`. Auth verifies it with the provider before it looks at the email, username or password, and refuses the sign-up when the token is missing (400 `BOT_PROTECTION_REQUIRED`), rejected (403 `BOT_PROTECTION_FAILED`) or cannot be checked (503 `BOT_PROTECTION_UNAVAILABLE`; no account is created). This applies to PAK-authenticated calls too: a backend forwards the token its browser received.
3143
3334
 
3144
3335
  - Params: `Auth.SignupParams`
3336
+ - Credential (optional): [`workspace_key`](#workspace_key)
3145
3337
 
3146
3338
  | Status | Error class | Codes | When |
3147
3339
  |---|---|---|---|
3148
3340
  | 400 | `BadRequestError` | `BAD_REQUEST`, `VALIDATION_ERROR`, `BOT_PROTECTION_REQUIRED` | `BOT_PROTECTION_REQUIRED`: the app has bot protection on and the request carried no `bot_protection_token`. Also: Request validation failed: a required body field is missing, has the wrong type, or fails its format constraint. Produced by the global `ValidationPipe` before the handler runs; the response body lists the offending fields. |
3149
3341
  | 401 | `AuthenticationError` | `UNAUTHORIZED` | The app sets `signup_requires_pak` and no Platform API Key was presented on `Authorization: Bearer`. |
3150
- | 403 | `PermissionDeniedError` | `FORBIDDEN`, `BOT_PROTECTION_FAILED`, `APP_SUSPENDED`, `APP_ARCHIVED` | `BOT_PROTECTION_FAILED`: the bot-protection provider rejected the `bot_protection_token` (bad, expired or already used), or the hostname it reported is not one of the app's `bot_protection.expected_hostnames`; render a fresh widget and retry. Otherwise: signup is disabled for this app, the email domain is not on the app's allow-list, the presented PAK belongs to a different workspace, or its policy denies `auth.signup.create` on this app. Also returned when the app itself is suspended (`APP_SUSPENDED`) or archived (`APP_ARCHIVED`). |
3342
+ | 403 | `PermissionDeniedError` | `FORBIDDEN`, `BOT_PROTECTION_FAILED`, `APP_SUSPENDED`, `APP_ARCHIVED` | `BOT_PROTECTION_FAILED`: the bot-protection provider rejected the `bot_protection_token` (bad, expired or already used), or the hostname it reported is not one of the app's `bot_protection.expected_hostnames`; render a fresh widget and retry. Otherwise: signup is disabled for this app (new apps start with `signup_enabled: false`; turn it on with `PATCH /v1/apps/{app_id}/auth-config`), the email domain is not on the app's allow-list, the presented PAK belongs to a different workspace, or its policy denies `auth.signup.create` on this app. Also returned when the app itself is suspended (`APP_SUSPENDED`) or archived (`APP_ARCHIVED`). |
3151
3343
  | 404 | `NotFoundError` | `NOT_FOUND` | The `:appSlug` in the path matches no app (or no live rename alias for one). This is about the **app**, not the account — it is decided before any identifier, code or credential in the body is looked at, so it is never a signal about whether an account or a contact exists. |
3152
3344
  | 409 | `ConflictError` | `CONFLICT` | Email or username already in use in this app, or the app has no workspace binding and therefore cannot accept a PAK signup. |
3153
3345
  | 415 | `APIError` | `UNSUPPORTED_MEDIA_TYPE` | The request body was sent as `application/x-www-form-urlencoded` (`UNSUPPORTED_MEDIA_TYPE`). This API is JSON-only: a form body is a CORS-simple request that skips preflight, so accepting one would widen the CSRF surface. Send `application/json`. Rejected in `main.ts` before routing, on every non-GET request. |
@@ -3155,7 +3347,7 @@ When the app has bot protection on (`auth_config.bot_protection`, Cloudflare Tur
3155
3347
  | 503 | `InternalServerError` | `SERVICE_UNAVAILABLE`, `BOT_PROTECTION_UNAVAILABLE` | `BOT_PROTECTION_UNAVAILABLE`: the app has bot protection on and the token could not be checked (the provider timed out or failed, or the app's bot-protection secret is missing or was refused by the provider). Sign-up fails closed: no account was created. Retry shortly. Otherwise: The service is momentarily busy (`SERVICE_UNAVAILABLE`): too many password hashes or verifications are already running or queued on the instance that took the request. Nothing was changed and no attempt was counted. Retry after the number of seconds in the `Retry-After` header (also in `details.retry_after_seconds`). Decided by instance load only, never by the identifier or the account, so it reveals nothing about whether an account exists. |
3156
3348
 
3157
3349
  ```ts
3158
- const result = await auth.consumer("app").signup({ email: "jane@example.com", password: "CorrectHorseBatteryStaple", username: "john_doe" });
3350
+ const result = await auth.consumer("my-app").signup({ email: "jane@example.com", password: "CorrectHorseBatteryStaple", username: "john_doe" });
3159
3351
  ```
3160
3352
 
3161
3353
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#post-app-slug-v1-auth-signup)
@@ -3169,6 +3361,7 @@ Switch the active tenant for this session.
3169
3361
  Re-issues the EndUser's access + refresh tokens with the new `org_id` + `org_role` claims, after verifying the caller holds a tenant_membership for the target tenant. The session row is updated to the new tenant so subsequent refreshes preserve the context. The new access token keeps the session's `amr` and `mfa_at` (as `/auth/refresh` does), so a sign-in that completed MFA still proves it after a switch. Refresh-token rotation runs as usual; the previous refresh token enters the standard 60-second grace window.
3170
3362
 
3171
3363
  - Params: `Auth.SwitchTenantParams`
3364
+ - Credential: [`end_user_token`](#end_user_token)
3172
3365
 
3173
3366
  | Status | Error class | Codes | When |
3174
3367
  |---|---|---|---|
@@ -3180,7 +3373,7 @@ Re-issues the EndUser's access + refresh tokens with the new `org_id` + `org_rol
3180
3373
  | 429 | `RateLimitError` | `RATE_LIMITED` | Per-IP rate limit (`RATE_LIMITED`): this path is in the `default` bucket — 60 requests per minute per IP over a sliding 60-second window, shared across pods through Redis. Applied by `RateLimitMiddleware` before the handler runs, so it is reachable whatever the body says, and the response carries `Retry-After` in seconds. |
3181
3374
 
3182
3375
  ```ts
3183
- const consumerToken = await auth.consumer("app").switchTenant({ tenant_id: "string" });
3376
+ const consumerToken = await auth.consumer("my-app").switchTenant({ tenant_id: "3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f" });
3184
3377
  ```
3185
3378
 
3186
3379
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#post-app-slug-v1-auth-switch-tenant)
@@ -3208,7 +3401,7 @@ Renamed from /verify-email — the same endpoint now serves both email and phone
3208
3401
  | 429 | `RateLimitError` | `RATE_LIMITED` | Per-IP rate limit (`RATE_LIMITED`): this path is in the `verify_code` bucket — 5 requests per minute per IP over a sliding 60-second window, shared across pods through Redis. Applied by `RateLimitMiddleware` before the handler runs, so it is reachable whatever the body says, and the response carries `Retry-After` in seconds. |
3209
3402
 
3210
3403
  ```ts
3211
- const consumerVerify = await auth.consumer("app").verifyEmail({ code: "123456" });
3404
+ const consumerVerify = await auth.consumer("my-app").verifyEmail({ code: "123456" });
3212
3405
  ```
3213
3406
 
3214
3407
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#post-app-slug-v1-auth-verify)
@@ -3224,6 +3417,7 @@ Change the password of the signed-in EndUser.
3224
3417
  Verifies `current_password`, sets the new hash, and revokes every OTHER active session for the account (the calling session is preserved). 401 if the current password is wrong or the account has no password credential — admin-provisioned accounts without an initial password should use the reset flow instead. The new password is held to exactly the same policy as signup: the app's length + character-class rules, the common-password deny-list, and the breach corpus.
3225
3418
 
3226
3419
  - Params: `Auth.MeChangePasswordParams`
3420
+ - Credential: [`end_user_token`](#end_user_token)
3227
3421
 
3228
3422
  | Status | Error class | Codes | When |
3229
3423
  |---|---|---|---|
@@ -3233,7 +3427,10 @@ Verifies `current_password`, sets the new hash, and revokes every OTHER active s
3233
3427
  | 503 | `InternalServerError` | `SERVICE_UNAVAILABLE` | The service is momentarily busy (`SERVICE_UNAVAILABLE`): too many password hashes or verifications are already running or queued on the instance that took the request. Nothing was changed and no attempt was counted. Retry after the number of seconds in the `Retry-After` header (also in `details.retry_after_seconds`). Decided by instance load only, never by the identifier or the account, so it reveals nothing about whether an account exists. |
3234
3428
 
3235
3429
  ```ts
3236
- await auth.consumer("app").me.changePassword({ current_password: "string", new_password: "string" });
3430
+ await auth.consumer("my-app").me.changePassword({
3431
+ current_password: "correct horse battery staple",
3432
+ new_password: "a new long passphrase 42",
3433
+ });
3237
3434
  ```
3238
3435
 
3239
3436
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#post-app-slug-v1-me-change-password)
@@ -3246,13 +3443,15 @@ Permanently delete the signed-in EndUser account.
3246
3443
 
3247
3444
  Removes the EndUser, all sessions, and all related records inside this app. Cannot be undone. The app audit log keeps one `auth.account.deleted` row (`via: self`; username and email domain only, never the address), and `user.deleted` fires with `via: "self"`.
3248
3445
 
3446
+ - Credential: [`end_user_token`](#end_user_token)
3447
+
3249
3448
  | Status | Error class | Codes | When |
3250
3449
  |---|---|---|---|
3251
3450
  | 401 | `AuthenticationError` | `UNAUTHORIZED` | Missing, invalid or expired bearer token, unknown or inactive app, or the account/session is no longer active. |
3252
3451
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
3253
3452
 
3254
3453
  ```ts
3255
- await auth.consumer("app").me.deleteAccount();
3454
+ await auth.consumer("my-app").me.deleteAccount();
3256
3455
  ```
3257
3456
 
3258
3457
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#delete-app-slug-v1-me)
@@ -3265,6 +3464,8 @@ Remove the EndUser avatar.
3265
3464
 
3266
3465
  Clears both the uploaded avatar and any provider-captured picture, and deletes the stored object. A later Google/GitHub sign-in re-populates the provider picture — that is "sync from provider" behavior, by design.
3267
3466
 
3467
+ - Credential: [`end_user_token`](#end_user_token)
3468
+
3268
3469
  | Status | Error class | Codes | When |
3269
3470
  |---|---|---|---|
3270
3471
  | 401 | `AuthenticationError` | `UNAUTHORIZED` | Missing, invalid or expired bearer token, unknown or inactive app, or the account/session is no longer active. |
@@ -3272,7 +3473,7 @@ Clears both the uploaded avatar and any provider-captured picture, and deletes t
3272
3473
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
3273
3474
 
3274
3475
  ```ts
3275
- await auth.consumer("app").me.deleteAvatar();
3476
+ await auth.consumer("my-app").me.deleteAvatar();
3276
3477
  ```
3277
3478
 
3278
3479
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#delete-app-slug-v1-me-avatar)
@@ -3285,13 +3486,15 @@ Resolve the signed-in EndUser's effective permissions.
3285
3486
 
3286
3487
  Returns the union of the app-level role permissions and the tenant-level role permissions (when the token carries an `org_id`). Resolved per-request through a 60-second LRU — role-permission edits propagate within ~1 minute. Use this instead of decoding a `permissions[]` claim from the JWT, which is no longer included. M2M tokens cannot call this — use `POST /:appSlug/v1/oauth/introspect` to inspect M2M scopes.
3287
3488
 
3489
+ - Credential: [`end_user_token`](#end_user_token)
3490
+
3288
3491
  | Status | Error class | Codes | When |
3289
3492
  |---|---|---|---|
3290
3493
  | 401 | `AuthenticationError` | `UNAUTHORIZED` | Missing, invalid or expired bearer token, unknown or inactive app, or the account/session is no longer active. |
3291
3494
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
3292
3495
 
3293
3496
  ```ts
3294
- const mePermissions = await auth.consumer("app").me.permissions();
3497
+ const mePermissions = await auth.consumer("my-app").me.permissions();
3295
3498
  ```
3296
3499
 
3297
3500
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#get-app-slug-v1-me-permissions)
@@ -3304,6 +3507,8 @@ Get the signed-in EndUser profile.
3304
3507
 
3305
3508
  Returns the EndUser identity, app membership, and role name. The flat `permissions[]` array is intentionally NOT included — permissions resolve from the role on demand. Call `GET /me/permissions` for the flat list when the client needs it for UI gating.
3306
3509
 
3510
+ - Credential: [`end_user_token`](#end_user_token)
3511
+
3307
3512
  | Status | Error class | Codes | When |
3308
3513
  |---|---|---|---|
3309
3514
  | 401 | `AuthenticationError` | `UNAUTHORIZED` | Missing, invalid or expired bearer token. |
@@ -3311,7 +3516,7 @@ Returns the EndUser identity, app membership, and role name. The flat `permissio
3311
3516
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
3312
3517
 
3313
3518
  ```ts
3314
- const meProfile = await auth.consumer("app").me.retrieve();
3519
+ const meProfile = await auth.consumer("my-app").me.retrieve();
3315
3520
  ```
3316
3521
 
3317
3522
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#get-app-slug-v1-me)
@@ -3325,6 +3530,7 @@ Update the signed-in EndUser profile.
3325
3530
  Editable fields: `display_name` and `username`. The username is unique per app (case-insensitive, same rule as signup); sending the current username is a no-op. Usernames cannot be cleared.
3326
3531
 
3327
3532
  - Params: `Auth.MeUpdateParams`
3533
+ - Credential: [`end_user_token`](#end_user_token)
3328
3534
 
3329
3535
  | Status | Error class | Codes | When |
3330
3536
  |---|---|---|---|
@@ -3333,7 +3539,7 @@ Editable fields: `display_name` and `username`. The username is unique per app (
3333
3539
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
3334
3540
 
3335
3541
  ```ts
3336
- const meUpdatedAccount = await auth.consumer("app").me.update({ display_name: "Ada Lovelace" });
3542
+ const meUpdatedAccount = await auth.consumer("my-app").me.update({ display_name: "Ada Lovelace" });
3337
3543
  ```
3338
3544
 
3339
3545
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#patch-app-slug-v1-me)
@@ -3347,6 +3553,7 @@ Upload (or replace) the EndUser avatar.
3347
3553
  PNG/JPEG/WEBP only — SVG is rejected (stored-XSS vector). ≤2 MB. Magic bytes are sniffed; a mislabeled file is rejected. The image is re-encoded server-side (orientation baked in, all metadata incl. EXIF/GPS stripped, bounded to 512×512, webp). An uploaded avatar takes precedence over a provider-captured picture.
3348
3554
 
3349
3555
  - Params: `Auth.MeUploadAvatarParams`
3556
+ - Credential: [`end_user_token`](#end_user_token)
3350
3557
 
3351
3558
  | Status | Error class | Codes | When |
3352
3559
  |---|---|---|---|
@@ -3356,7 +3563,7 @@ PNG/JPEG/WEBP only — SVG is rejected (stored-XSS vector). ≤2 MB. Magic bytes
3356
3563
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
3357
3564
 
3358
3565
  ```ts
3359
- const avatarUrl = await auth.consumer("app").me.uploadAvatar({ file: new File(["…"], "image.png", { type: "image/png" }) });
3566
+ const avatarUrl = await auth.consumer("my-app").me.uploadAvatar({ file: new File(["…"], "image.png", { type: "image/png" }) });
3360
3567
  ```
3361
3568
 
3362
3569
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#post-app-slug-v1-me-avatar)
@@ -3372,6 +3579,7 @@ Recent account activity.
3372
3579
  Login events, password changes, and other security-relevant records, scoped to this EndUser.
3373
3580
 
3374
3581
  - Paginated: `for await` iterates every item; `await` gives one page
3582
+ - Credential: [`end_user_token`](#end_user_token)
3375
3583
 
3376
3584
  | Status | Error class | Codes | When |
3377
3585
  |---|---|---|---|
@@ -3379,7 +3587,7 @@ Login events, password changes, and other security-relevant records, scoped to t
3379
3587
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
3380
3588
 
3381
3589
  ```ts
3382
- for await (const activity of auth.consumer("app").me.activity.list()) {
3590
+ for await (const activity of auth.consumer("my-app").me.activity.list()) {
3383
3591
  console.log(activity);
3384
3592
  }
3385
3593
  ```
@@ -3398,6 +3606,7 @@ Adds an email or phone contact to the EndUser. The contact starts unverified and
3398
3606
 
3399
3607
  - Params: `Auth.MeContactCreateParams`
3400
3608
  - Idempotent: an `Idempotency-Key` is sent automatically and reused on retries
3609
+ - Credential: [`end_user_token`](#end_user_token)
3401
3610
 
3402
3611
  | Status | Error class | Codes | When |
3403
3612
  |---|---|---|---|
@@ -3407,7 +3616,7 @@ Adds an email or phone contact to the EndUser. The contact starts unverified and
3407
3616
  | 429 | `RateLimitError` | `RATE_LIMITED` | Per-IP rate limit (`RATE_LIMITED`): `/me/contacts` is in the strict `auth` bucket (10 requests per minute per IP). |
3408
3617
 
3409
3618
  ```ts
3410
- const meContact = await auth.consumer("app").me.contacts.create({ type: "email", value: "string" });
3619
+ const meContact = await auth.consumer("my-app").me.contacts.create({ type: "email", value: "ada@example.com" });
3411
3620
  ```
3412
3621
 
3413
3622
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#post-app-slug-v1-me-contacts)
@@ -3418,6 +3627,8 @@ const meContact = await auth.consumer("app").me.contacts.create({ type: "email",
3418
3627
 
3419
3628
  Delete a contact.
3420
3629
 
3630
+ - Credential: [`end_user_token`](#end_user_token)
3631
+
3421
3632
  | Status | Error class | Codes | When |
3422
3633
  |---|---|---|---|
3423
3634
  | 401 | `AuthenticationError` | `UNAUTHORIZED` | Missing, invalid or expired bearer token, unknown or inactive app, or the account/session is no longer active. |
@@ -3426,7 +3637,7 @@ Delete a contact.
3426
3637
  | 429 | `RateLimitError` | `RATE_LIMITED` | Per-IP rate limit (`RATE_LIMITED`): `/me/contacts` is in the strict `auth` bucket (10 requests per minute per IP). |
3427
3638
 
3428
3639
  ```ts
3429
- await auth.consumer("app").me.contacts.delete("id");
3640
+ await auth.consumer("my-app").me.contacts.delete("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f");
3430
3641
  ```
3431
3642
 
3432
3643
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#delete-app-slug-v1-me-contacts-id)
@@ -3437,13 +3648,15 @@ await auth.consumer("app").me.contacts.delete("id");
3437
3648
 
3438
3649
  List the EndUser's contacts.
3439
3650
 
3651
+ - Credential: [`end_user_token`](#end_user_token)
3652
+
3440
3653
  | Status | Error class | Codes | When |
3441
3654
  |---|---|---|---|
3442
3655
  | 401 | `AuthenticationError` | `UNAUTHORIZED` | Missing, invalid or expired bearer token, unknown or inactive app, or the account/session is no longer active. |
3443
3656
  | 429 | `RateLimitError` | `RATE_LIMITED` | Per-IP rate limit (`RATE_LIMITED`): the general bucket, 60 requests per minute per IP. Adding, verifying or removing a contact is in the strict `auth` bucket (10 per minute). |
3444
3657
 
3445
3658
  ```ts
3446
- const meContacts = await auth.consumer("app").me.contacts.list();
3659
+ const meContacts = await auth.consumer("my-app").me.contacts.list();
3447
3660
  ```
3448
3661
 
3449
3662
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#get-app-slug-v1-me-contacts)
@@ -3456,6 +3669,8 @@ Promote a verified contact to primary.
3456
3669
 
3457
3670
  Demotes the previous primary of the same type. Refused (409) when the target contact is unverified — promoting an unverified contact would let an attacker who registered the contact bypass verification by routing reset codes through it.
3458
3671
 
3672
+ - Credential: [`end_user_token`](#end_user_token)
3673
+
3459
3674
  | Status | Error class | Codes | When |
3460
3675
  |---|---|---|---|
3461
3676
  | 401 | `AuthenticationError` | `UNAUTHORIZED` | Missing, invalid or expired bearer token, unknown or inactive app, or the account/session is no longer active. |
@@ -3464,7 +3679,7 @@ Demotes the previous primary of the same type. Refused (409) when the target con
3464
3679
  | 429 | `RateLimitError` | `RATE_LIMITED` | Per-IP rate limit (`RATE_LIMITED`): `/me/contacts` is in the strict `auth` bucket (10 requests per minute per IP). |
3465
3680
 
3466
3681
  ```ts
3467
- await auth.consumer("app").me.contacts.promote("id");
3682
+ await auth.consumer("my-app").me.contacts.promote("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f");
3468
3683
  ```
3469
3684
 
3470
3685
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#post-app-slug-v1-me-contacts-id-promote)
@@ -3481,6 +3696,7 @@ Creates a pending factor and returns the type-specific enrollment payload. TOTP:
3481
3696
 
3482
3697
  - Params: `Auth.MeMfaFactorCreateParams`
3483
3698
  - Idempotent: an `Idempotency-Key` is sent automatically and reused on retries
3699
+ - Credential: [`end_user_token`](#end_user_token)
3484
3700
 
3485
3701
  | Status | Error class | Codes | When |
3486
3702
  |---|---|---|---|
@@ -3492,7 +3708,7 @@ Creates a pending factor and returns the type-specific enrollment payload. TOTP:
3492
3708
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
3493
3709
 
3494
3710
  ```ts
3495
- const startEnrollment = await auth.consumer("app").me.mfa.factors.create({ type: "totp" });
3711
+ const startEnrollment = await auth.consumer("my-app").me.mfa.factors.create({ type: "totp" });
3496
3712
  ```
3497
3713
 
3498
3714
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#post-app-slug-v1-me-mfa-factors)
@@ -3505,6 +3721,8 @@ Disable an MFA factor.
3505
3721
 
3506
3722
  Soft-disable. The row is preserved (with `disabled_at` set) for audit. Has no effect on existing sessions — their `amr` claim still reflects whatever satisfied sign-in at the time. Disabling the only enabled factor removes the MFA requirement on future sign-ins.
3507
3723
 
3724
+ - Credential: [`end_user_token`](#end_user_token)
3725
+
3508
3726
  | Status | Error class | Codes | When |
3509
3727
  |---|---|---|---|
3510
3728
  | 401 | `AuthenticationError` | `UNAUTHORIZED` | Missing, invalid or expired bearer token, unknown or inactive app, or the account/session is no longer active. |
@@ -3512,7 +3730,7 @@ Soft-disable. The row is preserved (with `disabled_at` set) for audit. Has no ef
3512
3730
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
3513
3731
 
3514
3732
  ```ts
3515
- await auth.consumer("app").me.mfa.factors.delete("id");
3733
+ await auth.consumer("my-app").me.mfa.factors.delete("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f");
3516
3734
  ```
3517
3735
 
3518
3736
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#delete-app-slug-v1-me-mfa-factors-id)
@@ -3525,13 +3743,15 @@ List the authenticated EndUser's MFA factors.
3525
3743
 
3526
3744
  Includes factors in any state (pending enrollment + enabled). Disabled factors are not listed.
3527
3745
 
3746
+ - Credential: [`end_user_token`](#end_user_token)
3747
+
3528
3748
  | Status | Error class | Codes | When |
3529
3749
  |---|---|---|---|
3530
3750
  | 401 | `AuthenticationError` | `UNAUTHORIZED` | Missing, invalid or expired bearer token, unknown or inactive app, or the account/session is no longer active. |
3531
3751
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
3532
3752
 
3533
3753
  ```ts
3534
- const factorList = await auth.consumer("app").me.mfa.factors.list();
3754
+ const factorList = await auth.consumer("my-app").me.mfa.factors.list();
3535
3755
  ```
3536
3756
 
3537
3757
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#get-app-slug-v1-me-mfa-factors)
@@ -3545,6 +3765,7 @@ Confirm enrollment of a pending factor.
3545
3765
  TOTP: submit two `codes` from consecutive 30-second windows. Email: submit the emailed 6-digit `code`. On success the factor flips to enabled, recovery codes are minted (returned once), and prior recovery codes for the account are invalidated. Subsequent calls with the same `:id` fail with 400.
3546
3766
 
3547
3767
  - Params: `Auth.MeMfaFactorVerifyParams`
3768
+ - Credential: [`end_user_token`](#end_user_token)
3548
3769
 
3549
3770
  | Status | Error class | Codes | When |
3550
3771
  |---|---|---|---|
@@ -3553,7 +3774,7 @@ TOTP: submit two `codes` from consecutive 30-second windows. Email: submit the e
3553
3774
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
3554
3775
 
3555
3776
  ```ts
3556
- const confirmEnrollment = await auth.consumer("app").me.mfa.factors.verify("id", { code: "123456" });
3777
+ const confirmEnrollment = await auth.consumer("my-app").me.mfa.factors.verify("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f", { code: "123456" });
3557
3778
  ```
3558
3779
 
3559
3780
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#post-app-slug-v1-me-mfa-factors-id-verify)
@@ -3568,13 +3789,15 @@ Regenerate the recovery-code set.
3568
3789
 
3569
3790
  Mints a fresh set of single-use recovery codes and invalidates every prior code for the account. The new codes are returned once — store them now.
3570
3791
 
3792
+ - Credential: [`end_user_token`](#end_user_token)
3793
+
3571
3794
  | Status | Error class | Codes | When |
3572
3795
  |---|---|---|---|
3573
3796
  | 401 | `AuthenticationError` | `UNAUTHORIZED` | Missing, invalid or expired bearer token, unknown or inactive app, or the account/session is no longer active. |
3574
3797
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
3575
3798
 
3576
3799
  ```ts
3577
- const recoveryCodes = await auth.consumer("app").me.mfa.recoveryCodes.regenerate();
3800
+ const recoveryCodes = await auth.consumer("my-app").me.mfa.recoveryCodes.regenerate();
3578
3801
  ```
3579
3802
 
3580
3803
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#post-app-slug-v1-me-mfa-recovery-codes-regenerate)
@@ -3590,6 +3813,7 @@ Email a step-up code.
3590
3813
  Mints a single-use 6-digit code against one of the account's enabled email factors and delivers it to the bound address. Follow with `/step-up` submitting the code. Codes expire after 5 minutes; sends are throttled per contact (30s between sends, shared with the sign-in path). Refused while step-up is locked on this session.
3591
3814
 
3592
3815
  - Params: `Auth.MeMfaStepUpSendCodeParams`
3816
+ - Credential: [`end_user_token`](#end_user_token)
3593
3817
 
3594
3818
  | Status | Error class | Codes | When |
3595
3819
  |---|---|---|---|
@@ -3600,7 +3824,7 @@ Mints a single-use 6-digit code against one of the account's enabled email facto
3600
3824
  | 429 | `RateLimitError` | `RATE_LIMITED` | Send throttled, or step-up is locked on this session. |
3601
3825
 
3602
3826
  ```ts
3603
- const stepUpSendCode = await auth.consumer("app").me.mfa.stepUp.sendCode({ factor_id: "9b2f6c1e-0d6a-4a63-9a43-2f1c3e4d5a6b" });
3827
+ const stepUpSendCode = await auth.consumer("my-app").me.mfa.stepUp.sendCode({ factor_id: "9b2f6c1e-0d6a-4a63-9a43-2f1c3e4d5a6b" });
3604
3828
  ```
3605
3829
 
3606
3830
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#post-app-slug-v1-me-mfa-step-up-send-code)
@@ -3614,6 +3838,7 @@ Bump this session's MFA recency.
3614
3838
  Verify a fresh code (TOTP, emailed via `/step-up/send-code`, or recovery) against any enabled factor on the account, and update the session's `auth_methods` + `mfa_at`. Call `/auth/refresh` after this to mint a token whose claims reflect the new state. Customers reading `mfa_at` from access tokens to gate sensitive actions should require a `/step-up` + refresh cycle when the timestamp is older than their policy window. 5 consecutive wrong codes lock step-up on this session for 15 minutes.
3615
3839
 
3616
3840
  - Params: `Auth.MeMfaStepUpVerifyParams`
3841
+ - Credential: [`end_user_token`](#end_user_token)
3617
3842
 
3618
3843
  | Status | Error class | Codes | When |
3619
3844
  |---|---|---|---|
@@ -3622,7 +3847,7 @@ Verify a fresh code (TOTP, emailed via `/step-up/send-code`, or recovery) agains
3622
3847
  | 503 | `InternalServerError` | `SERVICE_UNAVAILABLE` | The service is momentarily busy (`SERVICE_UNAVAILABLE`): too many password hashes or verifications are already running or queued on the instance that took the request. Nothing was changed and no attempt was counted. Retry after the number of seconds in the `Retry-After` header (also in `details.retry_after_seconds`). Decided by instance load only, never by the identifier or the account, so it reveals nothing about whether an account exists. |
3623
3848
 
3624
3849
  ```ts
3625
- const stepUp = await auth.consumer("app").me.mfa.stepUp.verify({ code: "123456" });
3850
+ const stepUp = await auth.consumer("my-app").me.mfa.stepUp.verify({ code: "123456" });
3626
3851
  ```
3627
3852
 
3628
3853
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#post-app-slug-v1-me-mfa-step-up)
@@ -3637,6 +3862,8 @@ Get passkey assertion options for step-up.
3637
3862
 
3638
3863
  Mints a single-use WebAuthn challenge for the enabled passkeys on this account and returns the `publicKey` member for `navigator.credentials.get()`. Submit the result to `/step-up` as `credential`. Refused while step-up is locked on this session — a locked session must not be able to keep minting challenges.
3639
3864
 
3865
+ - Credential: [`end_user_token`](#end_user_token)
3866
+
3640
3867
  | Status | Error class | Codes | When |
3641
3868
  |---|---|---|---|
3642
3869
  | 400 | `BadRequestError` | `BAD_REQUEST`, `VALIDATION_ERROR` | No passkey is enrolled on this account. |
@@ -3645,7 +3872,7 @@ Mints a single-use WebAuthn challenge for the enabled passkeys on this account a
3645
3872
  | 429 | `RateLimitError` | `RATE_LIMITED` | Step-up is locked on this session. |
3646
3873
 
3647
3874
  ```ts
3648
- const webauthnOptions = await auth.consumer("app").me.mfa.stepUp.webauthn.options();
3875
+ const webauthnOptions = await auth.consumer("my-app").me.mfa.stepUp.webauthn.options();
3649
3876
  ```
3650
3877
 
3651
3878
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#post-app-slug-v1-me-mfa-step-up-webauthn-options)
@@ -3660,6 +3887,8 @@ Revoke a session.
3660
3887
 
3661
3888
  Sign the EndUser out of one device. The session's refresh token is destroyed; its access token expires on its TTL. Requires the `session.revoke` permission when the app's `enforce_app_permissions` flag is on; otherwise (the default) any valid token can call this. Self-revoke (revoking your own session as a member with no perms) is something to enforce separately if needed; this guard does not exempt the principal from the role check.
3662
3889
 
3890
+ - Credential: [`end_user_token`](#end_user_token)
3891
+
3663
3892
  | Status | Error class | Codes | When |
3664
3893
  |---|---|---|---|
3665
3894
  | 401 | `AuthenticationError` | `UNAUTHORIZED` | Missing, invalid or expired bearer token, unknown or inactive app, or the account/session is no longer active. |
@@ -3667,7 +3896,7 @@ Sign the EndUser out of one device. The session's refresh token is destroyed; it
3667
3896
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
3668
3897
 
3669
3898
  ```ts
3670
- await auth.consumer("app").me.sessions.delete("id");
3899
+ await auth.consumer("my-app").me.sessions.delete("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f");
3671
3900
  ```
3672
3901
 
3673
3902
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#delete-app-slug-v1-me-sessions-id)
@@ -3681,6 +3910,7 @@ List all active sessions for the EndUser.
3681
3910
  Each session includes a `current` flag identifying the one belonging to the bearer token used for this request.
3682
3911
 
3683
3912
  - Paginated: `for await` iterates every item; `await` gives one page
3913
+ - Credential: [`end_user_token`](#end_user_token)
3684
3914
 
3685
3915
  | Status | Error class | Codes | When |
3686
3916
  |---|---|---|---|
@@ -3688,7 +3918,7 @@ Each session includes a `current` flag identifying the one belonging to the bear
3688
3918
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
3689
3919
 
3690
3920
  ```ts
3691
- for await (const session of auth.consumer("app").me.sessions.list()) {
3921
+ for await (const session of auth.consumer("my-app").me.sessions.list()) {
3692
3922
  console.log(session);
3693
3923
  }
3694
3924
  ```
@@ -3706,6 +3936,7 @@ Accept a tenant invite as the signed-in EndUser.
3706
3936
  Adds the caller to the invited tenant with the role the invite names, and consumes the invite. Requires the invited address to be one of the caller's own VERIFIED email contacts in this app — holding the code is not sufficient, because whoever minted the invite also holds it, and an unverified contact is not proof of owning the address. A refused accept leaves the invite redeemable.
3707
3937
 
3708
3938
  - Params: `Auth.MeTenantInviteAcceptParams`
3939
+ - Credential: [`end_user_token`](#end_user_token)
3709
3940
 
3710
3941
  | Status | Error class | Codes | When |
3711
3942
  |---|---|---|---|
@@ -3717,7 +3948,7 @@ Adds the caller to the invited tenant with the role the invite names, and consum
3717
3948
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
3718
3949
 
3719
3950
  ```ts
3720
- const acceptedTenantInvite = await auth.consumer("app").me.tenantInvites.accept({ code: "tinv_9dK2mQ7pR4vXbN1sT6yZ0hLcJeW3fAgU" });
3951
+ const acceptedTenantInvite = await auth.consumer("my-app").me.tenantInvites.accept({ code: "tinv_9dK2mQ7pR4vXbN1sT6yZ0hLcJeW3fAgU" });
3721
3952
  ```
3722
3953
 
3723
3954
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#post-app-slug-v1-me-tenant-invites-accept)
@@ -3732,6 +3963,8 @@ List the EndUser's tenant memberships in this app.
3732
3963
 
3733
3964
  Returns each tenant the caller is a member of, including the tenant slug + display name and the member's role. Powers a tenant-switcher UI on the customer's product.
3734
3965
 
3966
+ - Credential: [`end_user_token`](#end_user_token)
3967
+
3735
3968
  | Status | Error class | Codes | When |
3736
3969
  |---|---|---|---|
3737
3970
  | 401 | `AuthenticationError` | `UNAUTHORIZED` | Missing / invalid bearer, or the session behind the token is revoked, expired or no longer exists. |
@@ -3740,7 +3973,7 @@ Returns each tenant the caller is a member of, including the tenant slug + displ
3740
3973
  | 429 | `RateLimitError` | `RATE_LIMITED` | Per-IP rate limit (`RATE_LIMITED`): this path is in the `default` bucket — 60 requests per minute per IP over a sliding 60-second window, shared across pods through Redis. Applied by `RateLimitMiddleware` before the handler runs, so it is reachable whatever the body says, and the response carries `Retry-After` in seconds. |
3741
3974
 
3742
3975
  ```ts
3743
- const myTenants = await auth.consumer("app").me.tenants.list();
3976
+ const myTenants = await auth.consumer("my-app").me.tenants.list();
3744
3977
  ```
3745
3978
 
3746
3979
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#get-app-slug-v1-auth-me-tenants)
@@ -3769,7 +4002,7 @@ For apps running `auth_config.mfa: "required"`. When a user proves their passwor
3769
4002
  | 429 | `RateLimitError` | `RATE_LIMITED` | Per-IP rate limit (`RATE_LIMITED`): this path is in the `default` bucket — 60 requests per minute per IP over a sliding 60-second window, shared across pods through Redis. Applied by `RateLimitMiddleware` before the handler runs, so it is reachable whatever the body says, and the response carries `Retry-After` in seconds. |
3770
4003
 
3771
4004
  ```ts
3772
- const startEnrollment = await auth.consumer("app").mfa.enroll({ mfa_token: "string", type: "totp" });
4005
+ const startEnrollment = await auth.consumer("my-app").mfa.enroll({ mfa_token: "3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f", type: "totp" });
3773
4006
  ```
3774
4007
 
3775
4008
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#post-app-slug-v1-auth-mfa-enroll)
@@ -3796,7 +4029,10 @@ Same flow as `/auth/mfa/verify` but consumes a single-use recovery code (16 hex
3796
4029
  | 503 | `InternalServerError` | `SERVICE_UNAVAILABLE` | The service is momentarily busy (`SERVICE_UNAVAILABLE`): too many password hashes or verifications are already running or queued on the instance that took the request. Nothing was changed and no attempt was counted. Retry after the number of seconds in the `Retry-After` header (also in `details.retry_after_seconds`). Decided by instance load only, never by the identifier or the account, so it reveals nothing about whether an account exists. |
3797
4030
 
3798
4031
  ```ts
3799
- const consumerToken = await auth.consumer("app").mfa.recover({ mfa_token: "string", recovery_code: "1a2b-3c4d-5e6f-7890" });
4032
+ const consumerToken = await auth.consumer("my-app").mfa.recover({
4033
+ mfa_token: "3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f",
4034
+ recovery_code: "1a2b-3c4d-5e6f-7890",
4035
+ });
3800
4036
  ```
3801
4037
 
3802
4038
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#post-app-slug-v1-auth-mfa-recover)
@@ -3824,7 +4060,10 @@ Delivers a single-use 6-digit code to the masked address of an `email` factor li
3824
4060
  | 429 | `RateLimitError` | `RATE_LIMITED` | Send throttled (max 3 sends per challenge, 30s apart), or too many failed attempts on the challenge. Per-IP rate limit (`RATE_LIMITED`): this path is in the `default` bucket — 60 requests per minute per IP over a sliding 60-second window, shared across pods through Redis. Applied by `RateLimitMiddleware` before the handler runs, so it is reachable whatever the body says, and the response carries `Retry-After` in seconds. |
3825
4061
 
3826
4062
  ```ts
3827
- const consumerMfaSendCode = await auth.consumer("app").mfa.sendCode({ factor_id: "9b2f6c1e-0d6a-4a63-9a43-2f1c3e4d5a6b", mfa_token: "string" });
4063
+ const consumerMfaSendCode = await auth.consumer("my-app").mfa.sendCode({
4064
+ factor_id: "9b2f6c1e-0d6a-4a63-9a43-2f1c3e4d5a6b",
4065
+ mfa_token: "3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f",
4066
+ });
3828
4067
  ```
3829
4068
 
3830
4069
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#post-app-slug-v1-auth-mfa-send-code)
@@ -3850,7 +4089,7 @@ Submit the `mfa_token` from a `mfa_required: true` signin response, plus EITHER
3850
4089
  | 429 | `RateLimitError` | `RATE_LIMITED` | Too many failed attempts against this challenge (5 locks it). Per-IP rate limit (`RATE_LIMITED`): this path is in the `verify_code` bucket — 5 requests per minute per IP over a sliding 60-second window, shared across pods through Redis. Applied by `RateLimitMiddleware` before the handler runs, so it is reachable whatever the body says, and the response carries `Retry-After` in seconds. |
3851
4090
 
3852
4091
  ```ts
3853
- const consumerToken = await auth.consumer("app").mfa.verify({ mfa_token: "string" });
4092
+ const consumerToken = await auth.consumer("my-app").mfa.verify({ mfa_token: "3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f" });
3854
4093
  ```
3855
4094
 
3856
4095
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#post-app-slug-v1-auth-mfa-verify)
@@ -3876,7 +4115,10 @@ Completes the flow started by `/auth/mfa/enroll`. TOTP: submit two `codes` from
3876
4115
  | 429 | `RateLimitError` | `RATE_LIMITED` | Too many failed attempts against this enrolment token (5 locks it). Per-IP rate limit (`RATE_LIMITED`): this path is in the `verify_code` bucket — 5 requests per minute per IP over a sliding 60-second window, shared across pods through Redis. Applied by `RateLimitMiddleware` before the handler runs, so it is reachable whatever the body says, and the response carries `Retry-After` in seconds. |
3877
4116
 
3878
4117
  ```ts
3879
- const consumerMfaEnrollVerify = await auth.consumer("app").mfa.verifyEnrollment({ factor_id: "9b2f6c1e-0d6a-4a63-9a43-2f1c3e4d5a6b", mfa_token: "string" });
4118
+ const consumerMfaEnrollVerify = await auth.consumer("my-app").mfa.verifyEnrollment({
4119
+ factor_id: "9b2f6c1e-0d6a-4a63-9a43-2f1c3e4d5a6b",
4120
+ mfa_token: "3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f",
4121
+ });
3880
4122
  ```
3881
4123
 
3882
4124
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#post-app-slug-v1-auth-mfa-enroll-verify)
@@ -3905,7 +4147,7 @@ Mints a single-use WebAuthn challenge scoped to the account behind `mfa_token` a
3905
4147
  | 429 | `RateLimitError` | `RATE_LIMITED` | Too many failed attempts on the challenge. Per-IP rate limit (`RATE_LIMITED`): this path is in the `default` bucket — 60 requests per minute per IP over a sliding 60-second window, shared across pods through Redis. Applied by `RateLimitMiddleware` before the handler runs, so it is reachable whatever the body says, and the response carries `Retry-After` in seconds. |
3906
4148
 
3907
4149
  ```ts
3908
- const consumerWebauthnOptions = await auth.consumer("app").mfa.webauthn.options({ mfa_token: "string" });
4150
+ const consumerWebauthnOptions = await auth.consumer("my-app").mfa.webauthn.options({ mfa_token: "3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f" });
3909
4151
  ```
3910
4152
 
3911
4153
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#post-app-slug-v1-auth-mfa-webauthn-options)
@@ -3921,6 +4163,7 @@ Introspect the bearer access token (RFC 7662).
3921
4163
  OAuth 2.0 token introspection. Send the access token via `Authorization: Bearer <token>` and receive either `{ active: false }` (invalid / expired / wrong app, or a session that is revoked, past its `expires_at`, or gone because the end user was deleted) or the active claim set including `scopes` (M2M) or `role` (EndUser), `exp`, `iat`, `iss`, `aid`. The body `token` field is optional and, if present, must match the bearer — RFC 7662 allows a separate `token` body field but we deliberately refuse cross-token introspection so a stolen token can't be probed against another active credential. The endpoint accepts both M2M and EndUser tokens — use it as the canonical way for a service to know what its own token can do, instead of decoding the JWT by hand. Returns 401 when the bearer is missing.
3922
4164
 
3923
4165
  - Params: `Auth.OauthIntrospectParams`
4166
+ - Credential: [`m2m_token`](#m2m_token) or [`end_user_token`](#end_user_token)
3924
4167
 
3925
4168
  | Status | Error class | Codes | When |
3926
4169
  |---|---|---|---|
@@ -3932,7 +4175,7 @@ OAuth 2.0 token introspection. Send the access token via `Authorization: Bearer
3932
4175
  | 429 | `RateLimitError` | `RATE_LIMITED` | Per-IP rate limit (`RATE_LIMITED`): this path is in the `default` bucket — 60 requests per minute per IP over a sliding 60-second window, shared across pods through Redis. Applied by `RateLimitMiddleware` before the handler runs, so it is reachable whatever the body says, and the response carries `Retry-After` in seconds. |
3933
4176
 
3934
4177
  ```ts
3935
- const introspect = await auth.consumer("app").oauth.introspect({ token: "string" });
4178
+ const introspect = await auth.consumer("my-app").oauth.introspect({ token: "eyJhbGciOiJSUzI1NiIsImtpZCI6ImsxIn0.eyJzdWIiOiJ1c3JfMSJ9.c2lnbmF0dXJl" });
3936
4179
  ```
3937
4180
 
3938
4181
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#post-app-slug-v1-oauth-introspect)
@@ -3958,9 +4201,9 @@ Standard OAuth 2.0 client_credentials grant. Returns an app-scoped access token
3958
4201
  | 429 | `RateLimitError` | `RATE_LIMITED` | Per-IP rate limit (`RATE_LIMITED`): this path is in the `token_exchange` bucket — 60 requests per minute per IP over a sliding 60-second window, shared across pods through Redis. Applied by `RateLimitMiddleware` before the handler runs, so it is reachable whatever the body says, and the response carries `Retry-After` in seconds. |
3959
4202
 
3960
4203
  ```ts
3961
- const oAuthToken = await auth.consumer("app").oauth.token({
4204
+ const oAuthToken = await auth.consumer("my-app").oauth.token({
3962
4205
  client_id: "m2m_a1b2c3d4e5f6...",
3963
- client_secret: "string",
4206
+ client_secret: "the-secret-returned-by-credentials-create",
3964
4207
  grant_type: "client_credentials",
3965
4208
  });
3966
4209
  ```
@@ -3990,7 +4233,7 @@ Returns a single-use WebAuthn challenge for `navigator.credentials.get()`. `allo
3990
4233
  | 429 | `RateLimitError` | `RATE_LIMITED` | Per-IP burst limit. The endpoint does no credential work — one random challenge and one insert — so the bucket is the whole bound. Per-IP rate limit (`RATE_LIMITED`): this path is in the `auth` bucket — 10 requests per minute per IP over a sliding 60-second window, shared across pods through Redis. Applied by `RateLimitMiddleware` before the handler runs, so it is reachable whatever the body says, and the response carries `Retry-After` in seconds. |
3991
4234
 
3992
4235
  ```ts
3993
- const consumerWebauthnOptions = await auth.consumer("app").passkey.options({ tenant_id: "9b2f6c1e-0d6a-4a63-9a43-2f1c3e4d5a6b" });
4236
+ const consumerWebauthnOptions = await auth.consumer("my-app").passkey.options({ tenant_id: "9b2f6c1e-0d6a-4a63-9a43-2f1c3e4d5a6b" });
3994
4237
  ```
3995
4238
 
3996
4239
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#post-app-slug-v1-auth-passkey-options)
@@ -4016,7 +4259,7 @@ Verifies the assertion against the challenge from `/auth/passkey/options` and mi
4016
4259
  | 429 | `RateLimitError` | `RATE_LIMITED` | Per-IP burst limit on credential consumption. Per-IP rate limit (`RATE_LIMITED`): this path is in the `verify_code` bucket — 5 requests per minute per IP over a sliding 60-second window, shared across pods through Redis. Applied by `RateLimitMiddleware` before the handler runs, so it is reachable whatever the body says, and the response carries `Retry-After` in seconds. |
4017
4260
 
4018
4261
  ```ts
4019
- const consumerToken = await auth.consumer("app").passkey.verify({ credential: {} });
4262
+ const consumerToken = await auth.consumer("my-app").passkey.verify({ credential: {} });
4020
4263
  ```
4021
4264
 
4022
4265
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#post-app-slug-v1-auth-passkey-verify)
@@ -4033,6 +4276,7 @@ Inserts a `<resource>.<action>` row in the per-app permission catalogue. The res
4033
4276
 
4034
4277
  - Params: `Auth.ConsumerScopedPermissionCreateParams`
4035
4278
  - Idempotent: an `Idempotency-Key` is sent automatically and reused on retries
4279
+ - Credential: [`app_api_key`](#app_api_key) or [`m2m_token`](#m2m_token) or [`end_user_token`](#end_user_token)
4036
4280
 
4037
4281
  | Status | Error class | Codes | When |
4038
4282
  |---|---|---|---|
@@ -4041,7 +4285,7 @@ Inserts a `<resource>.<action>` row in the per-app permission catalogue. The res
4041
4285
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
4042
4286
 
4043
4287
  ```ts
4044
- const consumerAdminPermission = await auth.consumer("app").permissions.create({ action: "read", resource: "project" });
4288
+ const consumerAdminPermission = await auth.consumer("my-app").permissions.create({ action: "read", resource: "project" });
4045
4289
  ```
4046
4290
 
4047
4291
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#post-app-slug-v1-admin-permissions)
@@ -4054,6 +4298,8 @@ Delete a custom permission.
4054
4298
 
4055
4299
  The `permissionKey` path parameter is the literal `<resource>.<action>` string. System permissions cannot be deleted — attempts return 403. Removing a permission also removes every `role_permission` row that bound it (cascade); role lookups dropped of that perm on the next request.
4056
4300
 
4301
+ - Credential: [`app_api_key`](#app_api_key) or [`m2m_token`](#m2m_token) or [`end_user_token`](#end_user_token)
4302
+
4057
4303
  | Status | Error class | Codes | When |
4058
4304
  |---|---|---|---|
4059
4305
  | 400 | `BadRequestError` | `BAD_REQUEST`, `VALIDATION_ERROR` | permissionKey must be in `resource.action` format. |
@@ -4063,7 +4309,7 @@ The `permissionKey` path parameter is the literal `<resource>.<action>` string.
4063
4309
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
4064
4310
 
4065
4311
  ```ts
4066
- await auth.consumer("app").permissions.delete("permission_key");
4312
+ await auth.consumer("my-app").permissions.delete("invoice.read");
4067
4313
  ```
4068
4314
 
4069
4315
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#delete-app-slug-v1-admin-permissions-permission-key)
@@ -4076,6 +4322,8 @@ List all permissions visible to this app.
4076
4322
 
4077
4323
  Returns the union of system permissions (shared across every app — `is_system: true` in the response) and any custom entries this app has defined (`is_system: false`). Useful for driving a role-editor UI that picks from the full catalogue.
4078
4324
 
4325
+ - Credential: [`app_api_key`](#app_api_key) or [`m2m_token`](#m2m_token) or [`end_user_token`](#end_user_token)
4326
+
4079
4327
  | Status | Error class | Codes | When |
4080
4328
  |---|---|---|---|
4081
4329
  | 401 | `AuthenticationError` | `UNAUTHORIZED` | Missing, invalid or expired Consumer-API bearer: an EndUser or M2M token, or an app API key (`hdk_live_*` / `hdk_test_*`); or the account, session, M2M client or key is no longer active, or the key belongs to another app. |
@@ -4084,7 +4332,7 @@ Returns the union of system permissions (shared across every app — `is_system:
4084
4332
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
4085
4333
 
4086
4334
  ```ts
4087
- const result = await auth.consumer("app").permissions.list();
4335
+ const result = await auth.consumer("my-app").permissions.list();
4088
4336
  ```
4089
4337
 
4090
4338
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#get-app-slug-v1-admin-permissions)
@@ -4101,6 +4349,7 @@ Creates a role with no permissions bound yet. Use `PUT /:roleName/permissions` t
4101
4349
 
4102
4350
  - Params: `Auth.ConsumerScopedRoleCreateParams`
4103
4351
  - Idempotent: an `Idempotency-Key` is sent automatically and reused on retries
4352
+ - Credential: [`app_api_key`](#app_api_key) or [`m2m_token`](#m2m_token) or [`end_user_token`](#end_user_token)
4104
4353
 
4105
4354
  | Status | Error class | Codes | When |
4106
4355
  |---|---|---|---|
@@ -4109,7 +4358,7 @@ Creates a role with no permissions bound yet. Use `PUT /:roleName/permissions` t
4109
4358
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
4110
4359
 
4111
4360
  ```ts
4112
- const consumerRole = await auth.consumer("app").roles.create({ name: "editor" });
4361
+ const consumerRole = await auth.consumer("my-app").roles.create({ name: "editor" });
4113
4362
  ```
4114
4363
 
4115
4364
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#post-app-slug-v1-admin-roles)
@@ -4122,6 +4371,8 @@ Delete a custom role.
4122
4371
 
4123
4372
  System roles (`owner`, `admin`, `member`) cannot be deleted — attempts return 403. Roles currently assigned to any member return 409 `ROLE_IN_USE`; reassign the affected members first.
4124
4373
 
4374
+ - Credential: [`app_api_key`](#app_api_key) or [`m2m_token`](#m2m_token) or [`end_user_token`](#end_user_token)
4375
+
4125
4376
  | Status | Error class | Codes | When |
4126
4377
  |---|---|---|---|
4127
4378
  | 401 | `AuthenticationError` | `UNAUTHORIZED` | Missing, invalid or expired bearer token. |
@@ -4130,7 +4381,7 @@ System roles (`owner`, `admin`, `member`) cannot be deleted — attempts return
4130
4381
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
4131
4382
 
4132
4383
  ```ts
4133
- await auth.consumer("app").roles.delete("role_name");
4384
+ await auth.consumer("my-app").roles.delete("admin");
4134
4385
  ```
4135
4386
 
4136
4387
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#delete-app-slug-v1-admin-roles-role-name)
@@ -4145,6 +4396,7 @@ Cursor-paginated by `created_at`. Use the returned `next_cursor` in subsequent c
4145
4396
 
4146
4397
  - Params: `Auth.ConsumerScopedRoleListParams`
4147
4398
  - Paginated: `for await` iterates every item; `await` gives one page
4399
+ - Credential: [`app_api_key`](#app_api_key) or [`m2m_token`](#m2m_token) or [`end_user_token`](#end_user_token)
4148
4400
 
4149
4401
  | Status | Error class | Codes | When |
4150
4402
  |---|---|---|---|
@@ -4155,10 +4407,10 @@ Cursor-paginated by `created_at`. Use the returned `next_cursor` in subsequent c
4155
4407
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
4156
4408
 
4157
4409
  ```ts
4158
- for await (const role of auth.consumer("app").roles.list()) {
4410
+ for await (const role of auth.consumer("my-app").roles.list()) {
4159
4411
  console.log(role);
4160
4412
  }
4161
- const page = await auth.consumer("app").roles.list({}, { timeout: 5_000 });
4413
+ const page = await auth.consumer("my-app").roles.list({}, { timeout: 5_000 });
4162
4414
  ```
4163
4415
 
4164
4416
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#get-app-slug-v1-admin-roles)
@@ -4171,6 +4423,8 @@ Read one role with its permission set.
4171
4423
 
4172
4424
  Returns the role plus the flat `permissions[]` array currently bound. Use this to drive a "configure role" UI in the customer product without having to call `/permissions` separately.
4173
4425
 
4426
+ - Credential: [`app_api_key`](#app_api_key) or [`m2m_token`](#m2m_token) or [`end_user_token`](#end_user_token)
4427
+
4174
4428
  | Status | Error class | Codes | When |
4175
4429
  |---|---|---|---|
4176
4430
  | 401 | `AuthenticationError` | `UNAUTHORIZED` | Missing, invalid or expired bearer token. |
@@ -4178,7 +4432,7 @@ Returns the role plus the flat `permissions[]` array currently bound. Use this t
4178
4432
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
4179
4433
 
4180
4434
  ```ts
4181
- const consumerRoleWithPermissions = await auth.consumer("app").roles.retrieve("role_name");
4435
+ const consumerRoleWithPermissions = await auth.consumer("my-app").roles.retrieve("admin");
4182
4436
  ```
4183
4437
 
4184
4438
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#get-app-slug-v1-admin-roles-role-name)
@@ -4192,6 +4446,7 @@ Update a role's description.
4192
4446
  Renames are intentionally NOT supported — the role name is part of every issued EndUser JWT, so changing it would silently break live sessions. Create a new role and migrate users instead.
4193
4447
 
4194
4448
  - Params: `Auth.ConsumerScopedRoleUpdateParams`
4449
+ - Credential: [`app_api_key`](#app_api_key) or [`m2m_token`](#m2m_token) or [`end_user_token`](#end_user_token)
4195
4450
 
4196
4451
  | Status | Error class | Codes | When |
4197
4452
  |---|---|---|---|
@@ -4200,7 +4455,7 @@ Renames are intentionally NOT supported — the role name is part of every issue
4200
4455
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
4201
4456
 
4202
4457
  ```ts
4203
- const consumerRole = await auth.consumer("app").roles.update("role_name", { description: "Updated description" });
4458
+ const consumerRole = await auth.consumer("my-app").roles.update("admin", { description: "Updated description" });
4204
4459
  ```
4205
4460
 
4206
4461
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#patch-app-slug-v1-admin-roles-role-name)
@@ -4214,6 +4469,7 @@ Replace a role's permission set.
4214
4469
  Destructive — the role's permission set is replaced wholesale with the supplied array. Caller-narrowed: the caller must hold every permission in the request, otherwise 403 `Cannot grant actions you don't have: ...`. For EndUser callers the caller-held set is their app-membership role's effective permissions. For M2M callers the held set is the token's `scopes[]` claim — the M2M cannot grant a role any verb that wasn't configured on the M2M credential. Unknown permission strings return 400.
4215
4470
 
4216
4471
  - Params: `Auth.ConsumerScopedRoleUpdatePermissionsParams`
4472
+ - Credential: [`app_api_key`](#app_api_key) or [`m2m_token`](#m2m_token) or [`end_user_token`](#end_user_token)
4217
4473
 
4218
4474
  | Status | Error class | Codes | When |
4219
4475
  |---|---|---|---|
@@ -4224,7 +4480,7 @@ Destructive — the role's permission set is replaced wholesale with the supplie
4224
4480
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
4225
4481
 
4226
4482
  ```ts
4227
- const result = await auth.consumer("app").roles.updatePermissions("role_name", { permissions: ["user.read", "role.create"] });
4483
+ const result = await auth.consumer("my-app").roles.updatePermissions("admin", { permissions: ["user.read", "role.create"] });
4228
4484
  ```
4229
4485
 
4230
4486
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#put-app-slug-v1-admin-roles-role-name-permissions)
@@ -4241,6 +4497,7 @@ Returns the secret ONCE (`key`). Scopes must be grantable by the app and held by
4241
4497
 
4242
4498
  - Params: `Auth.TenantApiKeyCreateParams`
4243
4499
  - Idempotent: an `Idempotency-Key` is sent automatically and reused on retries
4500
+ - Credential: [`end_user_token`](#end_user_token)
4244
4501
 
4245
4502
  | Status | Error class | Codes | When |
4246
4503
  |---|---|---|---|
@@ -4252,7 +4509,7 @@ Returns the secret ONCE (`key`). Scopes must be grantable by the app and held by
4252
4509
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
4253
4510
 
4254
4511
  ```ts
4255
- const tenantApiKeyWithSecret = await auth.consumer("app").tenantApiKeys.create({ name: "CI e2e", scopes: ["disputes.read", "audit.read"] });
4512
+ const tenantApiKeyWithSecret = await auth.consumer("my-app").tenantApiKeys.create({ name: "CI e2e", scopes: ["disputes.read", "audit.read"] });
4256
4513
  ```
4257
4514
 
4258
4515
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#post-app-slug-v1-tenant-api-keys)
@@ -4265,6 +4522,8 @@ Revoke an API key.
4265
4522
 
4266
4523
  Immediate and final. Idempotent: revoking a revoked key is a 204 that changes nothing.
4267
4524
 
4525
+ - Credential: [`end_user_token`](#end_user_token)
4526
+
4268
4527
  | Status | Error class | Codes | When |
4269
4528
  |---|---|---|---|
4270
4529
  | 401 | `AuthenticationError` | `UNAUTHORIZED` | Missing or invalid end-user bearer token (M2M tokens, PAKs and API keys are refused). |
@@ -4273,7 +4532,7 @@ Immediate and final. Idempotent: revoking a revoked key is a 204 that changes no
4273
4532
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
4274
4533
 
4275
4534
  ```ts
4276
- await auth.consumer("app").tenantApiKeys.delete("key");
4535
+ await auth.consumer("my-app").tenantApiKeys.delete("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f");
4277
4536
  ```
4278
4537
 
4279
4538
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#delete-app-slug-v1-tenant-api-keys-key-id)
@@ -4287,6 +4546,7 @@ Verify a tenant API key (RFC 7662 shape).
4287
4546
  For your backend: is this `pctk_` key, presented to YOUR API, usable right now, for which tenant and with which scopes? A key of another app is `{"active": false}`. Cache answers by the key's SHA-256: positives up to 30 s (never past `expires_at`), negatives up to 60 s. Intersect `scopes` with the permissions of `created_by.tenant_role` in your app. Requires `auth.tenant.api_keys.introspect` on the app. Not audited per call; `last_used_at` records use (5-minute granularity). This route has its own per-app rate bucket (1200 calls a minute by default), separate from your PAK's other calls; over it the answer is 429 with `Retry-After`, never a false `active: false`, so keep serving keys you hold a fresh positive for.
4288
4547
 
4289
4548
  - Params: `Auth.TenantApiKeyIntrospectParams`
4549
+ - Credential: [`workspace_key`](#workspace_key)
4290
4550
 
4291
4551
  | Status | Error class | Codes | When |
4292
4552
  |---|---|---|---|
@@ -4298,7 +4558,7 @@ For your backend: is this `pctk_` key, presented to YOUR API, usable right now,
4298
4558
  | 429 | `RateLimitError` | `RATE_LIMITED` | `RATE_LIMITED`: this app's introspect bucket is exhausted for the current minute. `Retry-After` says when it refills. |
4299
4559
 
4300
4560
  ```ts
4301
- const tenantApiKeyIntrospection = await auth.consumer("app").tenantApiKeys.introspect({ key: "pctk_live_4fQ2aZ0000000000000000000000000000000000000_1a2B3c" });
4561
+ const tenantApiKeyIntrospection = await auth.consumer("my-app").tenantApiKeys.introspect({ key: "pctk_live_4fQ2aZ0000000000000000000000000000000000000_1a2B3c" });
4302
4562
  ```
4303
4563
 
4304
4564
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#post-app-slug-v1-tenant-api-keys-introspect)
@@ -4313,6 +4573,7 @@ Newest first, cursor-paginated (20 per page, max 100). Secrets are never returne
4313
4573
 
4314
4574
  - Params: `Auth.ConsumerScopedTenantApiKeyListParams`
4315
4575
  - Paginated: `for await` iterates every item; `await` gives one page
4576
+ - Credential: [`end_user_token`](#end_user_token)
4316
4577
 
4317
4578
  | Status | Error class | Codes | When |
4318
4579
  |---|---|---|---|
@@ -4323,10 +4584,10 @@ Newest first, cursor-paginated (20 per page, max 100). Secrets are never returne
4323
4584
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
4324
4585
 
4325
4586
  ```ts
4326
- for await (const tenantApiKey of auth.consumer("app").tenantApiKeys.list()) {
4587
+ for await (const tenantApiKey of auth.consumer("my-app").tenantApiKeys.list()) {
4327
4588
  console.log(tenantApiKey);
4328
4589
  }
4329
- const page = await auth.consumer("app").tenantApiKeys.list({}, { timeout: 5_000 });
4590
+ const page = await auth.consumer("my-app").tenantApiKeys.list({}, { timeout: 5_000 });
4330
4591
  ```
4331
4592
 
4332
4593
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#get-app-slug-v1-tenant-api-keys)
@@ -4341,6 +4602,7 @@ Mints a replacement with the same name and scopes and returns its secret ONCE. Y
4341
4602
 
4342
4603
  - Params: `Auth.TenantApiKeyRotateParams`
4343
4604
  - Idempotent: an `Idempotency-Key` is sent automatically and reused on retries
4605
+ - Credential: [`end_user_token`](#end_user_token)
4344
4606
 
4345
4607
  | Status | Error class | Codes | When |
4346
4608
  |---|---|---|---|
@@ -4352,7 +4614,7 @@ Mints a replacement with the same name and scopes and returns its secret ONCE. Y
4352
4614
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
4353
4615
 
4354
4616
  ```ts
4355
- const tenantApiKeyWithSecret = await auth.consumer("app").tenantApiKeys.rotate("key", { expires_in_days: 7 });
4617
+ const tenantApiKeyWithSecret = await auth.consumer("my-app").tenantApiKeys.rotate("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f", { expires_in_days: 7 });
4356
4618
  ```
4357
4619
 
4358
4620
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#post-app-slug-v1-tenant-api-keys-key-id-rotate)
@@ -4365,6 +4627,8 @@ Why a tenant API key is (in)active.
4365
4627
 
4366
4628
  For your own audit trail: record why a key you saw go inactive died (revoked in the console, creator left the tenant, expired, keys suspended). Only keys of this app; another app's key is 404. Requires `auth.tenant.api_keys.introspect` on the app.
4367
4629
 
4630
+ - Credential: [`workspace_key`](#workspace_key)
4631
+
4368
4632
  | Status | Error class | Codes | When |
4369
4633
  |---|---|---|---|
4370
4634
  | 401 | `AuthenticationError` | `UNAUTHORIZED` | Missing, malformed or revoked Platform API Key (`Authorization: Bearer pcft_live_…`). |
@@ -4374,7 +4638,7 @@ For your own audit trail: record why a key you saw go inactive died (revoked in
4374
4638
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
4375
4639
 
4376
4640
  ```ts
4377
- const tenantApiKeyStatus = await auth.consumer("app").tenantApiKeys.status("key");
4641
+ const tenantApiKeyStatus = await auth.consumer("my-app").tenantApiKeys.status("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f");
4378
4642
  ```
4379
4643
 
4380
4644
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#get-app-slug-v1-tenant-api-keys-key-id-status)
@@ -4389,6 +4653,8 @@ Read this app's tenant API key settings.
4389
4653
 
4390
4654
  Requires `auth.tenant.api_keys.configure` on the app.
4391
4655
 
4656
+ - Credential: [`workspace_key`](#workspace_key)
4657
+
4392
4658
  | Status | Error class | Codes | When |
4393
4659
  |---|---|---|---|
4394
4660
  | 401 | `AuthenticationError` | `UNAUTHORIZED` | Missing, malformed or revoked Platform API Key (`Authorization: Bearer pcft_live_…`). |
@@ -4398,7 +4664,7 @@ Requires `auth.tenant.api_keys.configure` on the app.
4398
4664
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
4399
4665
 
4400
4666
  ```ts
4401
- const tenantApiKeysConfig = await auth.consumer("app").tenantApiKeys.config.retrieve();
4667
+ const tenantApiKeysConfig = await auth.consumer("my-app").tenantApiKeys.config.retrieve();
4402
4668
  ```
4403
4669
 
4404
4670
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#get-app-slug-v1-tenant-api-keys-config)
@@ -4413,6 +4679,7 @@ Call it on boot: your code is the source of truth for the scopes a key can carry
4413
4679
 
4414
4680
  - Params: `Auth.ConsumerScopedTenantApiKeyConfigUpdateParams`
4415
4681
  - Idempotent: an `Idempotency-Key` is sent automatically and reused on retries
4682
+ - Credential: [`workspace_key`](#workspace_key)
4416
4683
 
4417
4684
  | Status | Error class | Codes | When |
4418
4685
  |---|---|---|---|
@@ -4424,7 +4691,7 @@ Call it on boot: your code is the source of truth for the scopes a key can carry
4424
4691
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
4425
4692
 
4426
4693
  ```ts
4427
- const tenantApiKeysConfig = await auth.consumer("app").tenantApiKeys.config.update({ default_expiry_days: 7 });
4694
+ const tenantApiKeysConfig = await auth.consumer("my-app").tenantApiKeys.config.update({ default_expiry_days: 7 });
4428
4695
  ```
4429
4696
 
4430
4697
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#put-app-slug-v1-tenant-api-keys-config)
@@ -4439,6 +4706,7 @@ Create a tenant inside this app. Returns the new tenant with a `display_id` (`tn
4439
4706
 
4440
4707
  - Params: `Auth.ConsumerScopedTenantCreateParams`
4441
4708
  - Idempotent: an `Idempotency-Key` is sent automatically and reused on retries
4709
+ - Credential: [`workspace_key`](#workspace_key)
4442
4710
 
4443
4711
  | Status | Error class | Codes | When |
4444
4712
  |---|---|---|---|
@@ -4450,7 +4718,7 @@ Create a tenant inside this app. Returns the new tenant with a `display_id` (`tn
4450
4718
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
4451
4719
 
4452
4720
  ```ts
4453
- const consumerTenant = await auth.consumer("app").tenants.create({ display_name: "Acme Co" });
4721
+ const consumerTenant = await auth.consumer("my-app").tenants.create({ display_name: "Acme Co" });
4454
4722
  ```
4455
4723
 
4456
4724
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#post-app-slug-v1-tenants)
@@ -4461,6 +4729,8 @@ const consumerTenant = await auth.consumer("app").tenants.create({ display_name:
4461
4729
 
4462
4730
  Delete a tenant. Cascades to tenant_membership rows; account rows are unaffected. PAK must hold auth.tenant.delete.
4463
4731
 
4732
+ - Credential: [`workspace_key`](#workspace_key)
4733
+
4464
4734
  | Status | Error class | Codes | When |
4465
4735
  |---|---|---|---|
4466
4736
  | 401 | `AuthenticationError` | `UNAUTHORIZED` | Missing, invalid or expired bearer token. |
@@ -4468,7 +4738,7 @@ Delete a tenant. Cascades to tenant_membership rows; account rows are unaffected
4468
4738
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
4469
4739
 
4470
4740
  ```ts
4471
- await auth.consumer("app").tenants.delete("tenant");
4741
+ await auth.consumer("my-app").tenants.delete("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f");
4472
4742
  ```
4473
4743
 
4474
4744
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#delete-app-slug-v1-tenants-tenant-id)
@@ -4481,6 +4751,7 @@ List tenants in this app, paginated by created_at desc. PAK must hold auth.tenan
4481
4751
 
4482
4752
  - Params: `Auth.ConsumerScopedTenantListParams`
4483
4753
  - Paginated: `for await` iterates every item; `await` gives one page
4754
+ - Credential: [`workspace_key`](#workspace_key)
4484
4755
 
4485
4756
  | Status | Error class | Codes | When |
4486
4757
  |---|---|---|---|
@@ -4492,10 +4763,10 @@ List tenants in this app, paginated by created_at desc. PAK must hold auth.tenan
4492
4763
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
4493
4764
 
4494
4765
  ```ts
4495
- for await (const tenant of auth.consumer("app").tenants.list()) {
4766
+ for await (const tenant of auth.consumer("my-app").tenants.list()) {
4496
4767
  console.log(tenant);
4497
4768
  }
4498
- const page = await auth.consumer("app").tenants.list({}, { timeout: 5_000 });
4769
+ const page = await auth.consumer("my-app").tenants.list({}, { timeout: 5_000 });
4499
4770
  ```
4500
4771
 
4501
4772
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#get-app-slug-v1-tenants)
@@ -4506,6 +4777,8 @@ const page = await auth.consumer("app").tenants.list({}, { timeout: 5_000 });
4506
4777
 
4507
4778
  Fetch a single tenant by its UUID or `tnt_<12hex>` display id. PAK must hold auth.tenant.read.
4508
4779
 
4780
+ - Credential: [`workspace_key`](#workspace_key)
4781
+
4509
4782
  | Status | Error class | Codes | When |
4510
4783
  |---|---|---|---|
4511
4784
  | 401 | `AuthenticationError` | `UNAUTHORIZED` | Missing, invalid or expired bearer token. |
@@ -4513,7 +4786,7 @@ Fetch a single tenant by its UUID or `tnt_<12hex>` display id. PAK must hold aut
4513
4786
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
4514
4787
 
4515
4788
  ```ts
4516
- const consumerTenant = await auth.consumer("app").tenants.retrieve("tenant");
4789
+ const consumerTenant = await auth.consumer("my-app").tenants.retrieve("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f");
4517
4790
  ```
4518
4791
 
4519
4792
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#get-app-slug-v1-tenants-tenant-id)
@@ -4525,6 +4798,7 @@ const consumerTenant = await auth.consumer("app").tenants.retrieve("tenant");
4525
4798
  Patch a tenant. Any subset of {display_name, slug, status, metadata}. PAK must hold auth.tenant.update.
4526
4799
 
4527
4800
  - Params: `Auth.ConsumerScopedTenantUpdateParams`
4801
+ - Credential: [`workspace_key`](#workspace_key)
4528
4802
 
4529
4803
  | Status | Error class | Codes | When |
4530
4804
  |---|---|---|---|
@@ -4535,7 +4809,7 @@ Patch a tenant. Any subset of {display_name, slug, status, metadata}. PAK must h
4535
4809
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
4536
4810
 
4537
4811
  ```ts
4538
- const consumerTenant = await auth.consumer("app").tenants.update("tenant", { display_name: "string" });
4812
+ const consumerTenant = await auth.consumer("my-app").tenants.update("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f", { display_name: "Acme Inc" });
4539
4813
  ```
4540
4814
 
4541
4815
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#patch-app-slug-v1-tenants-tenant-id)
@@ -4552,6 +4826,7 @@ Unlike `POST /members`, nothing is granted until the invitee accepts at `POST /:
4552
4826
 
4553
4827
  - Params: `Auth.ConsumerScopedTenantInviteCreateParams`
4554
4828
  - Idempotent: an `Idempotency-Key` is sent automatically and reused on retries
4829
+ - Credential: [`workspace_key`](#workspace_key)
4555
4830
 
4556
4831
  | Status | Error class | Codes | When |
4557
4832
  |---|---|---|---|
@@ -4563,7 +4838,7 @@ Unlike `POST /members`, nothing is granted until the invitee accepts at `POST /:
4563
4838
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
4564
4839
 
4565
4840
  ```ts
4566
- const createTenantInvite = await auth.consumer("app").tenants.invites.create("tenant", { email: "alice@example.com", role_name: "member" });
4841
+ const createTenantInvite = await auth.consumer("my-app").tenants.invites.create("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f", { email: "alice@example.com", role_name: "member" });
4567
4842
  ```
4568
4843
 
4569
4844
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#post-app-slug-v1-tenants-tenant-id-invites)
@@ -4574,6 +4849,8 @@ const createTenantInvite = await auth.consumer("app").tenants.invites.create("te
4574
4849
 
4575
4850
  Revoke a pending invite. Idempotent — revoking an already-revoked invite is a 204 no-op and fires no second webhook. PAK must hold auth.tenant.members.remove.
4576
4851
 
4852
+ - Credential: [`workspace_key`](#workspace_key)
4853
+
4577
4854
  | Status | Error class | Codes | When |
4578
4855
  |---|---|---|---|
4579
4856
  | 401 | `AuthenticationError` | `UNAUTHORIZED` | Missing, invalid or expired bearer token. |
@@ -4581,7 +4858,7 @@ Revoke a pending invite. Idempotent — revoking an already-revoked invite is a
4581
4858
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
4582
4859
 
4583
4860
  ```ts
4584
- await auth.consumer("app").tenants.invites.delete("tenant", "invite");
4861
+ await auth.consumer("my-app").tenants.invites.delete("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f", "3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f");
4585
4862
  ```
4586
4863
 
4587
4864
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#delete-app-slug-v1-tenants-tenant-id-invites-invite-id)
@@ -4596,6 +4873,7 @@ Codes are never returned here — only the digest is stored. Use `status=pending
4596
4873
 
4597
4874
  - Params: `Auth.ConsumerScopedTenantInviteListParams`
4598
4875
  - Paginated: `for await` iterates every item; `await` gives one page
4876
+ - Credential: [`workspace_key`](#workspace_key)
4599
4877
 
4600
4878
  | Status | Error class | Codes | When |
4601
4879
  |---|---|---|---|
@@ -4605,10 +4883,10 @@ Codes are never returned here — only the digest is stored. Use `status=pending
4605
4883
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
4606
4884
 
4607
4885
  ```ts
4608
- for await (const invite of auth.consumer("app").tenants.invites.list("tenant")) {
4886
+ for await (const invite of auth.consumer("my-app").tenants.invites.list("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f")) {
4609
4887
  console.log(invite);
4610
4888
  }
4611
- const page = await auth.consumer("app").tenants.invites.list("tenant", {}, { timeout: 5_000 });
4889
+ const page = await auth.consumer("my-app").tenants.invites.list("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f", {}, { timeout: 5_000 });
4612
4890
  ```
4613
4891
 
4614
4892
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#get-app-slug-v1-tenants-tenant-id-invites)
@@ -4623,6 +4901,7 @@ Add a member to a tenant. Pass `accountId` for an existing user, or `email` (+ o
4623
4901
 
4624
4902
  - Params: `Auth.ConsumerScopedTenantMemberCreateParams`
4625
4903
  - Idempotent: an `Idempotency-Key` is sent automatically and reused on retries
4904
+ - Credential: [`workspace_key`](#workspace_key)
4626
4905
 
4627
4906
  | Status | Error class | Codes | When |
4628
4907
  |---|---|---|---|
@@ -4633,7 +4912,7 @@ Add a member to a tenant. Pass `accountId` for an existing user, or `email` (+ o
4633
4912
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
4634
4913
 
4635
4914
  ```ts
4636
- const consumerAddMember = await auth.consumer("app").tenants.members.create("tenant", { role_name: "member" });
4915
+ const consumerAddMember = await auth.consumer("my-app").tenants.members.create("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f", { role_name: "member" });
4637
4916
  ```
4638
4917
 
4639
4918
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#post-app-slug-v1-tenants-tenant-id-members)
@@ -4644,6 +4923,8 @@ const consumerAddMember = await auth.consumer("app").tenants.members.create("ten
4644
4923
 
4645
4924
  Remove a member from a tenant. The underlying account row stays in the app — only the tenant_membership row is deleted. PAK must hold auth.tenant.members.remove.
4646
4925
 
4926
+ - Credential: [`workspace_key`](#workspace_key)
4927
+
4647
4928
  | Status | Error class | Codes | When |
4648
4929
  |---|---|---|---|
4649
4930
  | 401 | `AuthenticationError` | `UNAUTHORIZED` | Missing, invalid or expired bearer token. |
@@ -4651,7 +4932,7 @@ Remove a member from a tenant. The underlying account row stays in the app — o
4651
4932
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
4652
4933
 
4653
4934
  ```ts
4654
- await auth.consumer("app").tenants.members.delete("tenant", "account");
4935
+ await auth.consumer("my-app").tenants.members.delete("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f", "3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f");
4655
4936
  ```
4656
4937
 
4657
4938
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#delete-app-slug-v1-tenants-tenant-id-members-account-id)
@@ -4664,6 +4945,7 @@ List members of a tenant. Cursor-paginated by joined_at desc. Filter by `?search
4664
4945
 
4665
4946
  - Params: `Auth.ConsumerScopedTenantMemberListParams`
4666
4947
  - Paginated: `for await` iterates every item; `await` gives one page
4948
+ - Credential: [`workspace_key`](#workspace_key)
4667
4949
 
4668
4950
  | Status | Error class | Codes | When |
4669
4951
  |---|---|---|---|
@@ -4675,10 +4957,10 @@ List members of a tenant. Cursor-paginated by joined_at desc. Filter by `?search
4675
4957
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
4676
4958
 
4677
4959
  ```ts
4678
- for await (const member of auth.consumer("app").tenants.members.list("tenant")) {
4960
+ for await (const member of auth.consumer("my-app").tenants.members.list("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f")) {
4679
4961
  console.log(member);
4680
4962
  }
4681
- const page = await auth.consumer("app").tenants.members.list("tenant", {}, { timeout: 5_000 });
4963
+ const page = await auth.consumer("my-app").tenants.members.list("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f", {}, { timeout: 5_000 });
4682
4964
  ```
4683
4965
 
4684
4966
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#get-app-slug-v1-tenants-tenant-id-members)
@@ -4690,6 +4972,7 @@ const page = await auth.consumer("app").tenants.members.list("tenant", {}, { tim
4690
4972
  Change a member's role. PAK must hold auth.tenant.members.update-role.
4691
4973
 
4692
4974
  - Params: `Auth.ConsumerScopedTenantMemberUpdateParams`
4975
+ - Credential: [`workspace_key`](#workspace_key)
4693
4976
 
4694
4977
  | Status | Error class | Codes | When |
4695
4978
  |---|---|---|---|
@@ -4699,7 +4982,7 @@ Change a member's role. PAK must hold auth.tenant.members.update-role.
4699
4982
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
4700
4983
 
4701
4984
  ```ts
4702
- const consumerTenantMembership = await auth.consumer("app").tenants.members.update("tenant", "account", { role_name: "admin" });
4985
+ const consumerTenantMembership = await auth.consumer("my-app").tenants.members.update("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f", "3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f", { role_name: "admin" });
4703
4986
  ```
4704
4987
 
4705
4988
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#patch-app-slug-v1-tenants-tenant-id-members-account-id)
@@ -4718,6 +5001,7 @@ What is NOT distinct: the password policy. A supplied `password` goes through th
4718
5001
 
4719
5002
  - Params: `Auth.UserCreateParams`
4720
5003
  - Idempotent: an `Idempotency-Key` is sent automatically and reused on retries
5004
+ - Credential: [`app_api_key`](#app_api_key) or [`m2m_token`](#m2m_token) or [`end_user_token`](#end_user_token)
4721
5005
 
4722
5006
  | Status | Error class | Codes | When |
4723
5007
  |---|---|---|---|
@@ -4728,7 +5012,7 @@ What is NOT distinct: the password policy. A supplied `password` goes through th
4728
5012
  | 503 | `InternalServerError` | `SERVICE_UNAVAILABLE` | The service is momentarily busy (`SERVICE_UNAVAILABLE`): too many password hashes or verifications are already running or queued on the instance that took the request. Nothing was changed and no attempt was counted. Retry after the number of seconds in the `Retry-After` header (also in `details.retry_after_seconds`). Decided by instance load only, never by the identifier or the account, so it reveals nothing about whether an account exists. |
4729
5013
 
4730
5014
  ```ts
4731
- const consumerAdminCreatedUser = await auth.consumer("app").users.create({ email: "string" });
5015
+ const consumerAdminCreatedUser = await auth.consumer("my-app").users.create({ email: "ada@example.com" });
4732
5016
  ```
4733
5017
 
4734
5018
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#post-app-slug-v1-admin-users)
@@ -4741,6 +5025,8 @@ Permanently delete an end-user.
4741
5025
 
4742
5026
  Removes the account, its sessions, contacts, and tenant memberships. Audit rows are preserved, and the deletion itself is audited as `auth.account.deleted` with `via: consumer_admin` and the caller as actor. Fires `user.deleted` with `via: "consumer_admin"`.
4743
5027
 
5028
+ - Credential: [`app_api_key`](#app_api_key) or [`m2m_token`](#m2m_token) or [`end_user_token`](#end_user_token)
5029
+
4744
5030
  | Status | Error class | Codes | When |
4745
5031
  |---|---|---|---|
4746
5032
  | 401 | `AuthenticationError` | `UNAUTHORIZED` | Missing, invalid or expired Consumer-API bearer: an EndUser or M2M token, or an app API key (`hdk_live_*` / `hdk_test_*`); or the account, session, M2M client or key is no longer active, or the key belongs to another app. |
@@ -4749,7 +5035,7 @@ Removes the account, its sessions, contacts, and tenant memberships. Audit rows
4749
5035
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
4750
5036
 
4751
5037
  ```ts
4752
- await auth.consumer("app").users.delete("user");
5038
+ await auth.consumer("my-app").users.delete("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f");
4753
5039
  ```
4754
5040
 
4755
5041
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#delete-app-slug-v1-admin-users-user-id)
@@ -4764,6 +5050,7 @@ Cursor-paginated. Supports the same `status` and `search` filters as the auth-ad
4764
5050
 
4765
5051
  - Params: `Auth.UserListParams`
4766
5052
  - Paginated: `for await` iterates every item; `await` gives one page
5053
+ - Credential: [`app_api_key`](#app_api_key) or [`m2m_token`](#m2m_token) or [`end_user_token`](#end_user_token)
4767
5054
 
4768
5055
  | Status | Error class | Codes | When |
4769
5056
  |---|---|---|---|
@@ -4774,10 +5061,10 @@ Cursor-paginated. Supports the same `status` and `search` filters as the auth-ad
4774
5061
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
4775
5062
 
4776
5063
  ```ts
4777
- for await (const user of auth.consumer("app").users.list()) {
5064
+ for await (const user of auth.consumer("my-app").users.list()) {
4778
5065
  console.log(user);
4779
5066
  }
4780
- const page = await auth.consumer("app").users.list({}, { timeout: 5_000 });
5067
+ const page = await auth.consumer("my-app").users.list({}, { timeout: 5_000 });
4781
5068
  ```
4782
5069
 
4783
5070
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#get-app-slug-v1-admin-users)
@@ -4788,6 +5075,8 @@ const page = await auth.consumer("app").users.list({}, { timeout: 5_000 });
4788
5075
 
4789
5076
  Read one end-user.
4790
5077
 
5078
+ - Credential: [`app_api_key`](#app_api_key) or [`m2m_token`](#m2m_token) or [`end_user_token`](#end_user_token)
5079
+
4791
5080
  | Status | Error class | Codes | When |
4792
5081
  |---|---|---|---|
4793
5082
  | 401 | `AuthenticationError` | `UNAUTHORIZED` | Missing, invalid or expired bearer token. |
@@ -4795,7 +5084,7 @@ Read one end-user.
4795
5084
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
4796
5085
 
4797
5086
  ```ts
4798
- const consumerAdminEndUser = await auth.consumer("app").users.retrieve("user");
5087
+ const consumerAdminEndUser = await auth.consumer("my-app").users.retrieve("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f");
4799
5088
  ```
4800
5089
 
4801
5090
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#get-app-slug-v1-admin-users-user-id)
@@ -4809,6 +5098,7 @@ Update an end-user's display name or primary email.
4809
5098
  Email update promotes an existing contact to primary (not for creating new contacts — that goes through the per-contact flow).
4810
5099
 
4811
5100
  - Params: `Auth.UserUpdateParams`
5101
+ - Credential: [`app_api_key`](#app_api_key) or [`m2m_token`](#m2m_token) or [`end_user_token`](#end_user_token)
4812
5102
 
4813
5103
  | Status | Error class | Codes | When |
4814
5104
  |---|---|---|---|
@@ -4819,7 +5109,7 @@ Email update promotes an existing contact to primary (not for creating new conta
4819
5109
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
4820
5110
 
4821
5111
  ```ts
4822
- const consumerAdminEndUser = await auth.consumer("app").users.update("user", { display_name: "string" });
5112
+ const consumerAdminEndUser = await auth.consumer("my-app").users.update("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f", { display_name: "Ada Lovelace" });
4823
5113
  ```
4824
5114
 
4825
5115
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#patch-app-slug-v1-admin-users-user-id)
@@ -4833,6 +5123,7 @@ Assign an end-user a role.
4833
5123
  Sets `app_membership.role_id` for the target user. For M2M callers the assignable role is bounded by the token's scopes (the guard already enforced `role.assign`); the role's own permission set is NOT additionally narrowed against the M2M scopes because M2M scopes are typically broader than per-user role perms by design (a backend cron with `user.list` + `role.assign` SHOULD be able to promote a user into a role carrying any per-app perm). EndUser admins are bounded by their own role's perms transitively — they couldn't authenticate as an admin without holding the perm.
4834
5124
 
4835
5125
  - Params: `Auth.UserUpdateRoleParams`
5126
+ - Credential: [`app_api_key`](#app_api_key) or [`m2m_token`](#m2m_token) or [`end_user_token`](#end_user_token)
4836
5127
 
4837
5128
  | Status | Error class | Codes | When |
4838
5129
  |---|---|---|---|
@@ -4843,7 +5134,7 @@ Sets `app_membership.role_id` for the target user. For M2M callers the assignabl
4843
5134
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
4844
5135
 
4845
5136
  ```ts
4846
- const consumerAdminRoleAssignment = await auth.consumer("app").users.updateRole("user", { role_name: "string" });
5137
+ const consumerAdminRoleAssignment = await auth.consumer("my-app").users.updateRole("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f", { role_name: "member" });
4847
5138
  ```
4848
5139
 
4849
5140
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#patch-app-slug-v1-admin-users-user-id-role)
@@ -4857,6 +5148,7 @@ Change an end-user status (active / suspended / deactivated).
4857
5148
  Setting status to `suspended` or `deactivated` also revokes all active sessions for the user (same behaviour as the auth-admin equivalent).
4858
5149
 
4859
5150
  - Params: `Auth.UserUpdateStatusParams`
5151
+ - Credential: [`app_api_key`](#app_api_key) or [`m2m_token`](#m2m_token) or [`end_user_token`](#end_user_token)
4860
5152
 
4861
5153
  | Status | Error class | Codes | When |
4862
5154
  |---|---|---|---|
@@ -4867,7 +5159,7 @@ Setting status to `suspended` or `deactivated` also revokes all active sessions
4867
5159
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
4868
5160
 
4869
5161
  ```ts
4870
- const consumerAdminEndUser = await auth.consumer("app").users.updateStatus("user", { status: "active" });
5162
+ const consumerAdminEndUser = await auth.consumer("my-app").users.updateStatus("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f", { status: "active" });
4871
5163
  ```
4872
5164
 
4873
5165
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#patch-app-slug-v1-admin-users-user-id-status)
@@ -4884,6 +5176,7 @@ Creates the factor in the enabled state. TOTP: returns the cleartext secret + ot
4884
5176
 
4885
5177
  - Params: `Auth.UserFactorCreateParams`
4886
5178
  - Idempotent: an `Idempotency-Key` is sent automatically and reused on retries
5179
+ - Credential: [`workspace_key`](#workspace_key)
4887
5180
 
4888
5181
  | Status | Error class | Codes | When |
4889
5182
  |---|---|---|---|
@@ -4896,7 +5189,7 @@ Creates the factor in the enabled state. TOTP: returns the cleartext secret + ot
4896
5189
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
4897
5190
 
4898
5191
  ```ts
4899
- const adminProvisionFactor = await auth.consumer("app").users.factors.create("user", { type: "totp" });
5192
+ const adminProvisionFactor = await auth.consumer("my-app").users.factors.create("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f", { type: "totp" });
4900
5193
  ```
4901
5194
 
4902
5195
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#post-app-slug-v1-users-user-id-factors)
@@ -4907,6 +5200,8 @@ const adminProvisionFactor = await auth.consumer("app").users.factors.create("us
4907
5200
 
4908
5201
  Disable a user's MFA factor.
4909
5202
 
5203
+ - Credential: [`workspace_key`](#workspace_key)
5204
+
4910
5205
  | Status | Error class | Codes | When |
4911
5206
  |---|---|---|---|
4912
5207
  | 401 | `AuthenticationError` | `UNAUTHORIZED` | Missing bearer token, or the Platform API Key is malformed, unknown or revoked. |
@@ -4916,7 +5211,7 @@ Disable a user's MFA factor.
4916
5211
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
4917
5212
 
4918
5213
  ```ts
4919
- await auth.consumer("app").users.factors.delete("user", "factor");
5214
+ await auth.consumer("my-app").users.factors.delete("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f", "3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f");
4920
5215
  ```
4921
5216
 
4922
5217
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#delete-app-slug-v1-users-user-id-factors-factor-id)
@@ -4927,6 +5222,8 @@ await auth.consumer("app").users.factors.delete("user", "factor");
4927
5222
 
4928
5223
  List a user's MFA factors.
4929
5224
 
5225
+ - Credential: [`workspace_key`](#workspace_key)
5226
+
4930
5227
  | Status | Error class | Codes | When |
4931
5228
  |---|---|---|---|
4932
5229
  | 401 | `AuthenticationError` | `UNAUTHORIZED` | Missing bearer token, or the Platform API Key is malformed, unknown or revoked. |
@@ -4936,7 +5233,7 @@ List a user's MFA factors.
4936
5233
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
4937
5234
 
4938
5235
  ```ts
4939
- const factorList = await auth.consumer("app").users.factors.list("user");
5236
+ const factorList = await auth.consumer("my-app").users.factors.list("3f0c3c1e-8a2b-4c55-9d3f-0a1b2c3d4e5f");
4940
5237
  ```
4941
5238
 
4942
5239
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#get-app-slug-v1-users-user-id-factors)
@@ -4963,7 +5260,7 @@ Token source: `body.token` if provided, else the `Authorization: Bearer <jwt>` h
4963
5260
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
4964
5261
 
4965
5262
  ```ts
4966
- const authorize = await auth.consumer("app").verify.authorize({ permission: "string" });
5263
+ const authorize = await auth.consumer("my-app").verify.authorize({ permission: "invoice.read" });
4967
5264
  ```
4968
5265
 
4969
5266
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#post-app-slug-v1-authorize)
@@ -4988,7 +5285,7 @@ Token source: `body.token` if provided, else the `Authorization: Bearer <jwt>` h
4988
5285
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
4989
5286
 
4990
5287
  ```ts
4991
- const authorizeBatch = await auth.consumer("app").verify.authorizeBatch({ checks: [{}] });
5288
+ const authorizeBatch = await auth.consumer("my-app").verify.authorizeBatch({ checks: [{}] });
4992
5289
  ```
4993
5290
 
4994
5291
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#post-app-slug-v1-authorize-batch)
@@ -5013,7 +5310,7 @@ Token source: `body.token` if provided, else the `Authorization: Bearer <jwt>` h
5013
5310
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
5014
5311
 
5015
5312
  ```ts
5016
- const verify = await auth.consumer("app").verify.token({ token: "string" });
5313
+ const verify = await auth.consumer("my-app").verify.token({ token: "eyJhbGciOiJSUzI1NiIsImtpZCI6ImsxIn0.eyJzdWIiOiJ1c3JfMSJ9.c2lnbmF0dXJl" });
5017
5314
  ```
5018
5315
 
5019
5316
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#post-app-slug-v1-verify)
@@ -5036,7 +5333,7 @@ Returns the JSON Web Key Set used to sign EndUser and M2M access tokens for this
5036
5333
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
5037
5334
 
5038
5335
  ```ts
5039
- const jwks = await auth.consumer("app").wellKnown.jwks();
5336
+ const jwks = await auth.consumer("my-app").wellKnown.jwks();
5040
5337
  ```
5041
5338
 
5042
5339
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#get-app-slug-well-known-jwks-json)
@@ -5057,11 +5354,31 @@ Returns `issuer`, `jwks_uri`, `token_endpoint`, `introspection_endpoint`, `useri
5057
5354
  | 429 | `RateLimitError` | `RATE_LIMITED` | Rate limited: too many requests from this client IP in the last 60 seconds (`RATE_LIMITED`); wait `Retry-After` seconds. |
5058
5355
 
5059
5356
  ```ts
5060
- const openIdConfiguration = await auth.consumer("app").wellKnown.openidConfiguration();
5357
+ const openIdConfiguration = await auth.consumer("my-app").wellKnown.openidConfiguration();
5061
5358
  ```
5062
5359
 
5063
5360
  [HTTP reference](https://productcraft.co/docs/auth/api-reference#get-app-slug-well-known-openid-configuration)
5064
5361
 
5362
+ ## Credentials
5363
+
5364
+ Routes take different credentials; each method above says which. Every one is sent as `Authorization: Bearer`. A workspace API key is the client's `apiKey` (or `PRODUCTCRAFT_API_KEY`); pass any other as `new Auth({ auth: { type: "bearer", token } })`. The wrong one answers 401.
5365
+
5366
+ ### `app_api_key`
5367
+
5368
+ An app API key (`hdk_live_…` or `hdk_test_…`) minted for this app (`POST /v1/apps/{app_id}/api-keys`). Its permissions are always enforced. A workspace key is refused.
5369
+
5370
+ ### `end_user_token`
5371
+
5372
+ An end user's access token for this app, from `signin`, `signup` or `refresh`. Acts as that user.
5373
+
5374
+ ### `m2m_token`
5375
+
5376
+ An access token from this app's client-credentials grant (`POST /{app_slug}/v1/oauth/token`).
5377
+
5378
+ ### `workspace_key`
5379
+
5380
+ A workspace API key (`pcft_live_…`, what the SDKs read from `PRODUCTCRAFT_API_KEY`) or a signed-in ProductCraft platform session token. Its workspace policy decides what it may do.
5381
+
5065
5382
  ## Error codes
5066
5383
 
5067
5384
  `err.code` on an `APIError` is one of these (an open set: new codes may appear).