@stapel/workspaces-react 0.6.0 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (105) hide show
  1. package/CHANGELOG.md +56 -0
  2. package/dist/api/generated/schema.d.ts +774 -7
  3. package/dist/api/generated/schema.d.ts.map +1 -1
  4. package/dist/api/types.d.ts +104 -7
  5. package/dist/api/types.d.ts.map +1 -1
  6. package/dist/api/workspacesApi.d.ts +55 -1
  7. package/dist/api/workspacesApi.d.ts.map +1 -1
  8. package/dist/api/workspacesApi.js +23 -0
  9. package/dist/api/workspacesApi.js.map +1 -1
  10. package/dist/default/InviteAcceptPage.d.ts +28 -0
  11. package/dist/default/InviteAcceptPage.d.ts.map +1 -0
  12. package/dist/default/InviteAcceptPage.js +141 -0
  13. package/dist/default/InviteAcceptPage.js.map +1 -0
  14. package/dist/default/MembersManager.d.ts +4 -4
  15. package/dist/default/MembersManager.d.ts.map +1 -1
  16. package/dist/default/MembersManager.js +61 -56
  17. package/dist/default/MembersManager.js.map +1 -1
  18. package/dist/default/index.d.ts +2 -0
  19. package/dist/default/index.d.ts.map +1 -1
  20. package/dist/default/index.js +1 -0
  21. package/dist/default/index.js.map +1 -1
  22. package/dist/flows/inviteAcceptFlow.d.ts +155 -0
  23. package/dist/flows/inviteAcceptFlow.d.ts.map +1 -0
  24. package/dist/flows/inviteAcceptFlow.js +126 -0
  25. package/dist/flows/inviteAcceptFlow.js.map +1 -0
  26. package/dist/headless/Can.d.ts +40 -0
  27. package/dist/headless/Can.d.ts.map +1 -0
  28. package/dist/headless/Can.js +30 -0
  29. package/dist/headless/Can.js.map +1 -0
  30. package/dist/headless/InviteAcceptFlow.d.ts +44 -0
  31. package/dist/headless/InviteAcceptFlow.d.ts.map +1 -0
  32. package/dist/headless/InviteAcceptFlow.js +67 -0
  33. package/dist/headless/InviteAcceptFlow.js.map +1 -0
  34. package/dist/headless/RoleSelect.d.ts +41 -0
  35. package/dist/headless/RoleSelect.d.ts.map +1 -0
  36. package/dist/headless/RoleSelect.js +34 -0
  37. package/dist/headless/RoleSelect.js.map +1 -0
  38. package/dist/i18n/generated/errors.gen.d.ts +48 -0
  39. package/dist/i18n/generated/errors.gen.d.ts.map +1 -1
  40. package/dist/i18n/generated/errors.gen.js +24 -0
  41. package/dist/i18n/generated/errors.gen.js.map +1 -1
  42. package/dist/i18n/generated/errors.ru.gen.d.ts.map +1 -1
  43. package/dist/i18n/generated/errors.ru.gen.js +8 -0
  44. package/dist/i18n/generated/errors.ru.gen.js.map +1 -1
  45. package/dist/i18n/keys.d.ts +28 -0
  46. package/dist/i18n/keys.d.ts.map +1 -1
  47. package/dist/i18n/keys.js +64 -0
  48. package/dist/i18n/keys.js.map +1 -1
  49. package/dist/i18n/ru.d.ts.map +1 -1
  50. package/dist/i18n/ru.js +30 -0
  51. package/dist/i18n/ru.js.map +1 -1
  52. package/dist/index.d.ts +17 -4
  53. package/dist/index.d.ts.map +1 -1
  54. package/dist/index.js +10 -2
  55. package/dist/index.js.map +1 -1
  56. package/dist/model/capabilities.d.ts +21 -0
  57. package/dist/model/capabilities.d.ts.map +1 -0
  58. package/dist/model/capabilities.js +32 -0
  59. package/dist/model/capabilities.js.map +1 -0
  60. package/dist/model/emailMask.d.ts +22 -0
  61. package/dist/model/emailMask.d.ts.map +1 -0
  62. package/dist/model/emailMask.js +36 -0
  63. package/dist/model/emailMask.js.map +1 -0
  64. package/dist/model/mutations.d.ts +107 -3
  65. package/dist/model/mutations.d.ts.map +1 -1
  66. package/dist/model/mutations.js +190 -1
  67. package/dist/model/mutations.js.map +1 -1
  68. package/dist/model/queries.d.ts +108 -2
  69. package/dist/model/queries.d.ts.map +1 -1
  70. package/dist/model/queries.js +139 -1
  71. package/dist/model/queries.js.map +1 -1
  72. package/dist/model/queryKeys.d.ts +12 -1
  73. package/dist/model/queryKeys.d.ts.map +1 -1
  74. package/dist/model/queryKeys.js +25 -0
  75. package/dist/model/queryKeys.js.map +1 -1
  76. package/dist/model/stepUp.d.ts +25 -0
  77. package/dist/model/stepUp.d.ts.map +1 -0
  78. package/dist/model/stepUp.js +79 -0
  79. package/dist/model/stepUp.js.map +1 -0
  80. package/llms.txt +49 -15
  81. package/manifest.json +346 -3
  82. package/package.json +5 -5
  83. package/src/analytics/generated/events.json +1 -1
  84. package/src/api/generated/schema.ts +774 -7
  85. package/src/api/types.ts +113 -7
  86. package/src/api/workspacesApi.ts +137 -0
  87. package/src/default/InviteAcceptPage.tsx +373 -0
  88. package/src/default/MembersManager.tsx +155 -145
  89. package/src/default/index.ts +2 -0
  90. package/src/flows/inviteAcceptFlow.ts +289 -0
  91. package/src/headless/Can.tsx +49 -0
  92. package/src/headless/InviteAcceptFlow.tsx +105 -0
  93. package/src/headless/RoleSelect.tsx +58 -0
  94. package/src/i18n/generated/errors.gen.ts +24 -0
  95. package/src/i18n/generated/errors.json +62 -0
  96. package/src/i18n/generated/errors.ru.gen.ts +8 -0
  97. package/src/i18n/keys.ts +68 -0
  98. package/src/i18n/ru.ts +34 -0
  99. package/src/index.ts +54 -1
  100. package/src/model/capabilities.ts +37 -0
  101. package/src/model/emailMask.ts +37 -0
  102. package/src/model/mutations.ts +248 -4
  103. package/src/model/queries.ts +205 -2
  104. package/src/model/queryKeys.ts +45 -1
  105. package/src/model/stepUp.ts +96 -0
@@ -86,6 +86,113 @@ export interface paths {
86
86
  patch: operations["workspaces_api_v1_partial_update"];
87
87
  trace?: never;
88
88
  };
89
+ "/workspaces/api/v1/{workspace_id}/invitations": {
90
+ parameters: {
91
+ query?: never;
92
+ header?: never;
93
+ path?: never;
94
+ cookie?: never;
95
+ };
96
+ /**
97
+ * @description Who was invited and has not accepted (#109).
98
+ *
99
+ * Before this endpoint an admin could send invitations and never see
100
+ * them again: acceptance produced a member row, and everything else —
101
+ * the unopened mail, the wrong address, the person who declined — was
102
+ * invisible from the product. The roster answered "who is in"; nothing
103
+ * answered "who is still out, and since when".
104
+ *
105
+ * Gated on ``members.invite``: the mandate that creates invitations is
106
+ * the mandate that sees and manages them. Reading the list is strictly
107
+ * less than sending one, so no separate capability is minted — a role
108
+ * that may invite but may not audit its own invitations would be a
109
+ * distinction without a use.
110
+ *
111
+ * The invite token is never in the response. It is a bearer credential;
112
+ * a list endpoint that carried it would hand every admin a working login
113
+ * link for every invited address.
114
+ *
115
+ * **Permissions:** `IsAuthenticated`
116
+ */
117
+ get: operations["workspaces_api_v1_invitations_list"];
118
+ put?: never;
119
+ post?: never;
120
+ delete?: never;
121
+ options?: never;
122
+ head?: never;
123
+ patch?: never;
124
+ trace?: never;
125
+ };
126
+ "/workspaces/api/v1/{workspace_id}/invitations/{invitation_id}/resend": {
127
+ parameters: {
128
+ query?: never;
129
+ header?: never;
130
+ path?: never;
131
+ cookie?: never;
132
+ };
133
+ get?: never;
134
+ put?: never;
135
+ /**
136
+ * @description Send the invitation email again (#109).
137
+ *
138
+ * Accepts an **expired** invitation on purpose — a dead TTL is the most
139
+ * common reason to resend — and refuses the three stored terminal states,
140
+ * which are decisions rather than delivery failures. The token is
141
+ * rotated and the TTL restarts, so the fresh letter carries a fresh link
142
+ * and any stale copy of the old one stops working.
143
+ *
144
+ * Reviving an expired invitation re-reserves a seat, so the plan ceiling
145
+ * is re-checked here exactly as it is on invite: capability first ("may
146
+ * YOU", 403), then the org's plan ("may the ORG", 402). An invitation
147
+ * that is already pending costs no additional seat and is never blocked
148
+ * by that check.
149
+ *
150
+ * **Permissions:** `IsAuthenticated`
151
+ */
152
+ post: operations["workspaces_api_v1_invitations_resend_create"];
153
+ delete?: never;
154
+ options?: never;
155
+ head?: never;
156
+ patch?: never;
157
+ trace?: never;
158
+ };
159
+ "/workspaces/api/v1/{workspace_id}/invitations/{invitation_id}/revoke": {
160
+ parameters: {
161
+ query?: never;
162
+ header?: never;
163
+ path?: never;
164
+ cookie?: never;
165
+ };
166
+ get?: never;
167
+ put?: never;
168
+ /**
169
+ * @description Withdraw a live invitation (#109).
170
+ *
171
+ * The workspace's terminal "no", the mirror of the invitee's decline —
172
+ * the two stay distinguishable in ``status`` forever. The seat the
173
+ * invitation reserved is freed on commit.
174
+ *
175
+ * Only a **pending** invitation is revocable: an accepted one produced a
176
+ * membership (remove the member instead), a declined or already-revoked
177
+ * one is terminal, and an expired one is already harmless. Each of those
178
+ * gets its own error key rather than a shrug.
179
+ *
180
+ * The transition is a compare-and-set under a row lock, not a blind
181
+ * write: an accept committing between this view's state check and the
182
+ * lock wins, and the revocation then fails honestly with
183
+ * ``error.400.invitation_already_used``. The reverse — a revocation
184
+ * committing between an accept's check and its lock — is the race fixed
185
+ * in 0.10.0, and it is the same predicate holding both ends.
186
+ *
187
+ * **Permissions:** `IsAuthenticated`
188
+ */
189
+ post: operations["workspaces_api_v1_invitations_revoke_create"];
190
+ delete?: never;
191
+ options?: never;
192
+ head?: never;
193
+ patch?: never;
194
+ trace?: never;
195
+ };
89
196
  "/workspaces/api/v1/{workspace_id}/members": {
90
197
  parameters: {
91
198
  query?: never;
@@ -151,6 +258,78 @@ export interface paths {
151
258
  patch: operations["workspaces_api_v1_members_partial_update"];
152
259
  trace?: never;
153
260
  };
261
+ "/workspaces/api/v1/{workspace_id}/members/{user_id}/password/reset": {
262
+ parameters: {
263
+ query?: never;
264
+ header?: never;
265
+ path?: never;
266
+ cookie?: never;
267
+ };
268
+ get?: never;
269
+ put?: never;
270
+ /**
271
+ * @description Reset a member's password on the organization's order (#110).
272
+ *
273
+ * Five questions this endpoint has to answer out loud, because a
274
+ * password reset is an account takeover performed on purpose and every
275
+ * one of them is a way to get it wrong.
276
+ *
277
+ * **Who may do it.** A mandate, not a session: capability
278
+ * ``members.password.reset`` (builtin ``admin`` and ``owner``), declared
279
+ * ``high``, so ``@requires_verification(scope="sensitive")`` demands a
280
+ * fresh step-up on top — an ambient cookie is not enough to hand
281
+ * somebody else's account over. Only an owner may reset an OWNER's
282
+ * password, the same hardcoded owner protection role changes and
283
+ * removals carry: otherwise an admin resets the owner and inherits the
284
+ * organization. And auth refuses a staff/superuser target outright
285
+ * (``error.403.privileged_account``) — org admin is a role inside one
286
+ * workspace, deployment staff is a role above every workspace, and the
287
+ * first must never be a route to the second.
288
+ *
289
+ * **Whether the user finds out.** Always. A
290
+ * ``workspace.member_password_reset`` letter names the workspace and the
291
+ * admin who did it — a reset is indistinguishable from a takeover
292
+ * unless the account holder is told which one it was. ``notified`` in
293
+ * the response says honestly whether a channel existed; the letter
294
+ * never carries the new password (see
295
+ * :func:`~stapel_workspaces.services.reset_member_password`).
296
+ *
297
+ * **Whether the new password is temporary.** Yes — auth raises the
298
+ * workspace's ``provisioned_user_policies`` (#90), defaulting to
299
+ * ``password_change``. A password the admin knows must stop working the
300
+ * first time it is used, and since auth 0.15.0 that demand holds on all
301
+ * 19 session-issuance paths rather than only the password form. An org
302
+ * may pass an explicit ``[]`` to suppress it, and that lands in auth's
303
+ * audit row.
304
+ *
305
+ * **Whether it is an existence oracle.** No: a target that is not a
306
+ * resettable member of THIS workspace — an unknown UUID, a real account
307
+ * that is not a member, a member of a different workspace, or the
308
+ * caller's own id — all get one byte-identical 404. And the capability
309
+ * check runs before any target lookup, so a caller without the mandate
310
+ * learns nothing at all about anybody. ``tests/
311
+ * test_api_member_password_reset.py`` compares those responses byte for
312
+ * byte.
313
+ *
314
+ * **Whether it is logged with the actor.** Twice, on purpose:
315
+ * ``workspace.member_password_reset`` through the transactional outbox
316
+ * (the org's activity log) and auth's own ``AuthAuditLog`` row carrying
317
+ * ``actor_id`` and ``via=admin_reset`` (the deployment's security
318
+ * journal). Neither carries credential material.
319
+ *
320
+ * Own password: use auth's ``POST /password/change/``. This endpoint is
321
+ * for acting on somebody else, and answers about yourself with the same
322
+ * 404 as about a stranger.
323
+ *
324
+ * **Permissions:** `IsAuthenticated`
325
+ */
326
+ post: operations["workspaces_api_v1_members_password_reset_create"];
327
+ delete?: never;
328
+ options?: never;
329
+ head?: never;
330
+ patch?: never;
331
+ trace?: never;
332
+ };
154
333
  "/workspaces/api/v1/{workspace_id}/members/invite": {
155
334
  parameters: {
156
335
  query?: never;
@@ -178,6 +357,49 @@ export interface paths {
178
357
  patch?: never;
179
358
  trace?: never;
180
359
  };
360
+ "/workspaces/api/v1/{workspace_id}/members/provision": {
361
+ parameters: {
362
+ query?: never;
363
+ header?: never;
364
+ path?: never;
365
+ cookie?: never;
366
+ };
367
+ get?: never;
368
+ put?: never;
369
+ /**
370
+ * @description Provision an org-created (synthetic) member (org-program spec §C1).
371
+ *
372
+ * The org mints its own login/password account: full username is
373
+ * ``{workspace_slug}/{username_local}`` (namespaced — the slug is
374
+ * globally unique, so orgs cannot collide), the account is created by
375
+ * ``auth.provision_user`` with the workspace's first-login policy
376
+ * (``settings.security.provisioned_user_policy``: forced password change
377
+ * or mandatory MFA enrollment) and joins immediately
378
+ * (``accepted_at=now``, ``provisioned=True``).
379
+ *
380
+ * Gate stack, in order: HIGH step-up (``@requires_verification``, scope
381
+ * ``sensitive`` — the same store as admin step-up) → capability
382
+ * ``members.provision`` (403) → entitlement ``workspaces.provision_user``
383
+ * (402; degrades to allow without billing) → optional per-user debit
384
+ * (``STAPEL_WORKSPACES["PROVISION_USER_CREDITS"]`` > 0).
385
+ *
386
+ * Credentials: a synthetic account normally has no email — when the
387
+ * request omits ``email``, the server-generated password comes back in
388
+ * the response (``generated_password``) exactly once and no letter is
389
+ * sent. With ``email``, the ``workspace.provisioned_account`` letter
390
+ * carries the credentials as well. Auth's structured failures pass
391
+ * through keyed (``error.409.username_taken`` / auth's 400s); auth not
392
+ * wired → honest 503, this seam never degrades to allow.
393
+ *
394
+ * **Permissions:** `IsAuthenticated`
395
+ */
396
+ post: operations["workspaces_api_v1_members_provision_create"];
397
+ delete?: never;
398
+ options?: never;
399
+ head?: never;
400
+ patch?: never;
401
+ trace?: never;
402
+ };
181
403
  "/workspaces/api/v1/internal/{workspace_id}/members/{user_id}": {
182
404
  parameters: {
183
405
  query?: never;
@@ -220,6 +442,90 @@ export interface paths {
220
442
  patch?: never;
221
443
  trace?: never;
222
444
  };
445
+ "/workspaces/api/v1/invitations/{token}": {
446
+ parameters: {
447
+ query?: never;
448
+ header?: never;
449
+ path?: never;
450
+ cookie?: never;
451
+ };
452
+ /**
453
+ * @description Public invitation preview — what the /invite/{token} page renders.
454
+ *
455
+ * AllowAny by design (org-program spec §B2): the invitee has no session
456
+ * yet; the token in the URL is the bearer secret. The response leaks
457
+ * nothing harvestable — the email is masked, and ``status`` /
458
+ * ``email_registered`` are exactly what the frontend flow machine needs
459
+ * to route (login vs claim vs terminal-state screen). Throttled as an
460
+ * enumeration backstop. The token is never logged.
461
+ *
462
+ * **Permissions:** `AllowAny`
463
+ */
464
+ get: operations["workspaces_api_v1_invitations_retrieve"];
465
+ put?: never;
466
+ post?: never;
467
+ delete?: never;
468
+ options?: never;
469
+ head?: never;
470
+ patch?: never;
471
+ trace?: never;
472
+ };
473
+ "/workspaces/api/v1/invitations/{token}/claim": {
474
+ parameters: {
475
+ query?: never;
476
+ header?: never;
477
+ path?: never;
478
+ cookie?: never;
479
+ };
480
+ get?: never;
481
+ put?: never;
482
+ /**
483
+ * @description Mint a login grant for a not-yet-registered invitee (spec §B2-B3).
484
+ *
485
+ * AllowAny — the whole point is that no account exists yet. Only valid
486
+ * for ``email_registered == false``: an existing account gets 409 and the
487
+ * frontend switches to login. The grant comes from auth's
488
+ * ``auth.issue_login_grant`` comm Function (``create_if_missing`` — the
489
+ * verified account materializes on exchange); if that Function is not
490
+ * wired, the answer is an honest 503 — an invite flow without auth is
491
+ * meaningless, so this seam never degrades to allow. The invitation is
492
+ * NOT consumed here: accept stays a separate, deliberate step after
493
+ * setup. Neither the invite token nor the grant token is ever logged.
494
+ *
495
+ * **Permissions:** `AllowAny`
496
+ */
497
+ post: operations["workspaces_api_v1_invitations_claim_create"];
498
+ delete?: never;
499
+ options?: never;
500
+ head?: never;
501
+ patch?: never;
502
+ trace?: never;
503
+ };
504
+ "/workspaces/api/v1/invitations/{token}/decline": {
505
+ parameters: {
506
+ query?: never;
507
+ header?: never;
508
+ path?: never;
509
+ cookie?: never;
510
+ };
511
+ get?: never;
512
+ put?: never;
513
+ /**
514
+ * @description Decline an invitation — the invitee's terminal "no" (spec §B2).
515
+ *
516
+ * Authenticated + email-match, exactly like accept: only the invited
517
+ * account may resolve the invitation, in either direction. Decline ≠
518
+ * revoke — both states stay distinguishable in the preview ``status``.
519
+ *
520
+ * **Permissions:** `IsAuthenticated`
521
+ */
522
+ post: operations["workspaces_api_v1_invitations_decline_create"];
523
+ delete?: never;
524
+ options?: never;
525
+ head?: never;
526
+ patch?: never;
527
+ trace?: never;
528
+ };
223
529
  "/workspaces/api/v1/invitations/accept": {
224
530
  parameters: {
225
531
  query?: never;
@@ -247,6 +553,31 @@ export interface paths {
247
553
  patch?: never;
248
554
  trace?: never;
249
555
  };
556
+ "/workspaces/api/v1/roles": {
557
+ parameters: {
558
+ query?: never;
559
+ header?: never;
560
+ path?: never;
561
+ cookie?: never;
562
+ };
563
+ /**
564
+ * @description The effective role registry — metadata for frontends (spec §A2).
565
+ *
566
+ * Lets a RoleSelect stop hardcoding the builtin four: builtin roles plus
567
+ * the deployment's ``STAPEL_WORKSPACES["ROLES"]`` overlay, capability
568
+ * strings verbatim (wildcards included), ordered by descending rank.
569
+ *
570
+ * **Permissions:** `IsAuthenticated`
571
+ */
572
+ get: operations["workspaces_api_v1_roles_retrieve"];
573
+ put?: never;
574
+ post?: never;
575
+ delete?: never;
576
+ options?: never;
577
+ head?: never;
578
+ patch?: never;
579
+ trace?: never;
580
+ };
250
581
  }
251
582
  export type webhooks = Record<string, never>;
252
583
  export interface components {
@@ -264,18 +595,100 @@ export interface components {
264
595
  /** @description Invite token from the email link */
265
596
  token: string;
266
597
  };
267
- /** @description InvitationResponse(id: uuid.UUID, workspace_id: uuid.UUID, email: str, role: str, expires_at: str, accepted_at: Optional[str], revoked_at: Optional[str], created_at: str) */
598
+ /** @description Login-grant mint for an unregistered invitee (claim step). */
599
+ InvitationClaimResponse: {
600
+ /** @description Single-use, short-lived login grant token — exchange it at auth's POST /grant/exchange/ for a session (creates the verified account). A credential: never log it */
601
+ grant_token: string;
602
+ };
603
+ /** @description Public (AllowAny) invitation preview — what the /invite/{token} page renders before any auth decision. */
604
+ InvitationPreviewResponse: {
605
+ /**
606
+ * @description Display name of the inviting workspace
607
+ * @example Acme Engineering
608
+ */
609
+ workspace_name: string;
610
+ /**
611
+ * @description Role granted on acceptance
612
+ * @example member
613
+ */
614
+ role: string;
615
+ /**
616
+ * @description Invited email, masked for the public page
617
+ * @example m***@e***.com
618
+ */
619
+ email_masked: string;
620
+ /**
621
+ * @description Invitation state. One of pending / expired / revoked / accepted / declined
622
+ * @example pending
623
+ */
624
+ status: string;
625
+ /** @description Whether an account already exists for the invited email — steers the frontend to login vs claim */
626
+ email_registered: boolean;
627
+ /**
628
+ * @description ISO 8601 expiry time
629
+ * @example 2026-07-31T10:00:00Z
630
+ */
631
+ expires_at: string;
632
+ };
633
+ /**
634
+ * @description One invitation row — the admin's "who has not accepted yet" table (#109).
635
+ *
636
+ * ``status`` is the derived label (``WorkspaceInvitation.status``), not a
637
+ * stored column: it is what the table renders and what the frontend
638
+ * routes the revoke/resend affordances on. The raw timestamps travel
639
+ * alongside it because "expires in 2 days" and "declined last Tuesday"
640
+ * are different rows on the same screen; the label alone cannot say
641
+ * when.
642
+ *
643
+ * The invite token is deliberately absent — it is a bearer secret that
644
+ * only ever leaves this service inside the invitation email.
645
+ */
268
646
  InvitationResponse: {
269
- /** Format: uuid */
647
+ /**
648
+ * Format: uuid
649
+ * @description Invitation UUID
650
+ * @example 0192f...
651
+ */
270
652
  id: string;
271
- /** Format: uuid */
653
+ /**
654
+ * Format: uuid
655
+ * @description Workspace UUID
656
+ */
272
657
  workspace_id: string;
658
+ /**
659
+ * @description Invited email address (normalized lowercase)
660
+ * @example alice@example.com
661
+ */
273
662
  email: string;
663
+ /**
664
+ * @description Role granted on acceptance
665
+ * @example member
666
+ */
274
667
  role: string;
668
+ /**
669
+ * @description Derived state. One of pending / accepted / declined / revoked / expired
670
+ * @example pending
671
+ */
672
+ status: string;
673
+ /**
674
+ * @description ISO 8601 expiry time
675
+ * @example 2026-07-31T10:00:00Z
676
+ */
275
677
  expires_at: string;
678
+ /** @description ISO 8601 acceptance time; null unless accepted */
276
679
  accepted_at: string | null;
680
+ /** @description ISO 8601 decline time (the invitee said no); null unless declined */
681
+ declined_at: string | null;
682
+ /** @description ISO 8601 revocation time (the workspace withdrew it); null unless revoked */
277
683
  revoked_at: string | null;
684
+ /** @description ISO 8601 creation time */
278
685
  created_at: string;
686
+ /**
687
+ * Format: uuid
688
+ * @description UUID of the admin who sent it; null when that account is gone
689
+ * @example 0192a...
690
+ */
691
+ invited_by_id?: string | null;
279
692
  };
280
693
  /** @description Invite payload. */
281
694
  MemberInviteRequest: {
@@ -296,6 +709,50 @@ export interface components {
296
709
  MemberInviteResponse: {
297
710
  invitations?: components["schemas"]["InvitationResponse"][];
298
711
  };
712
+ /** @description Reset a member's password on the organization's order (#110). */
713
+ MemberPasswordResetRequest: {
714
+ /** @description New password chosen by the admin. Omitted: the server generates a crypto-strong one, returned once as generated_password */
715
+ password?: string | null;
716
+ /**
717
+ * @description First-login steps to demand of the member afterwards — any subset of password_change / mfa_enroll. Omitted: the workspace's own security policies, falling back to password_change. A password somebody else chose has to stop working at its first use, so an EMPTY list is a deliberate, auditable choice, not a shortcut
718
+ * @example [
719
+ * "password_change"
720
+ * ]
721
+ */
722
+ first_login_policies?: string[] | null;
723
+ /**
724
+ * @description Free-text note recorded on auth's audit row (e.g. the ticket the request came in on)
725
+ * @example SUP-42
726
+ */
727
+ reason?: string | null;
728
+ };
729
+ /** @description Result of an administrative password reset (#110). */
730
+ MemberPasswordResetResponse: {
731
+ /**
732
+ * Format: uuid
733
+ * @description UUID of the member whose password was reset
734
+ */
735
+ user_id: string;
736
+ /**
737
+ * @description How many live sessions the reset ended. A reset that left them standing would not recover the account
738
+ * @example 2
739
+ */
740
+ sessions_revoked?: number;
741
+ /** @description Server-generated password — returned exactly ONCE, only when the request omitted password. Hand it to the member out of band; it cannot be re-fetched. A credential: never log it */
742
+ generated_password?: string | null;
743
+ /**
744
+ * @description The steps now demanded of the member before their next session
745
+ * @example [
746
+ * "password_change"
747
+ * ]
748
+ */
749
+ first_login_policies_applied?: string[];
750
+ /**
751
+ * @description Whether the member was told their password was reset. False means the account has no contact channel — the admin is the only one who can tell them
752
+ * @example true
753
+ */
754
+ notified?: boolean;
755
+ };
299
756
  /** @description Workspace member. */
300
757
  MemberResponse: {
301
758
  /**
@@ -327,6 +784,25 @@ export interface components {
327
784
  accepted_at: string | null;
328
785
  /** @description ISO 8601 last access; null if never accessed */
329
786
  last_accessed_at: string | null;
787
+ /** @description Whether this is an org-created (synthetic) member joined via members/provision */
788
+ provisioned?: boolean;
789
+ /** @description ISO 8601 suspension timestamp; null while active. Suspension is not removal — the role stays but access is closed */
790
+ suspended_at?: string | null;
791
+ /** @description Why the membership is suspended (canonical value no_mfa); null while active */
792
+ suspension_reason?: string | null;
793
+ };
794
+ PaginatedInvitationResponseList: {
795
+ items: components["schemas"]["InvitationResponse"][];
796
+ /** @description Anchor value for next page */
797
+ next_anchor?: string | null;
798
+ /** @description Anchor value for previous page */
799
+ prev_anchor?: string | null;
800
+ /** @description Whether there are more items after this page */
801
+ has_next: boolean;
802
+ /** @description Whether there are items before this page */
803
+ has_prev: boolean;
804
+ /** @description Number of items in current page */
805
+ count: number;
330
806
  };
331
807
  PaginatedMemberResponseList: {
332
808
  items: components["schemas"]["MemberResponse"][];
@@ -353,6 +829,78 @@ export interface components {
353
829
  [key: string]: unknown;
354
830
  } | null;
355
831
  };
832
+ /** @description Provision an org-created (synthetic) member (org-program spec §C1). */
833
+ ProvisionMemberRequest: {
834
+ /**
835
+ * @description Local part of the login; the full username becomes "{workspace_slug}/{username_local}". Stock username alphabet, no slash
836
+ * @example jdoe
837
+ */
838
+ username_local: string;
839
+ /**
840
+ * @description Role to grant (effective registry; owner is not provisionable)
841
+ * @example member
842
+ */
843
+ role?: string;
844
+ /** @description Initial password chosen by the admin. Omitted: the server generates a crypto-strong one, returned once as generated_password */
845
+ password?: string | null;
846
+ /**
847
+ * @description Display-name hint forwarded to auth/profiles
848
+ * @example Jane Doe
849
+ */
850
+ display_name?: string | null;
851
+ /** @description Optional email anchor (stored UNVERIFIED by auth). When present, the provisioned-account email with the credentials is sent there. Normally omitted — synthetic accounts have no email */
852
+ email?: string | null;
853
+ };
854
+ /** @description Result of provisioning an org member. */
855
+ ProvisionMemberResponse: {
856
+ /**
857
+ * Format: uuid
858
+ * @description UUID of the created account
859
+ */
860
+ user_id: string;
861
+ /**
862
+ * @description Full namespaced login
863
+ * @example acme-eng/jdoe
864
+ */
865
+ username: string;
866
+ /**
867
+ * @description Granted role
868
+ * @example member
869
+ */
870
+ role: string;
871
+ /** @description Server-generated initial password — returned exactly ONCE, only when the request omitted password. Store it or hand it to the user now; it cannot be re-fetched. A credential: never log it */
872
+ generated_password?: string | null;
873
+ };
874
+ /** @description RoleListResponse(roles: List[stapel_workspaces.dto.RoleResponse] = <factory>) */
875
+ RoleListResponse: {
876
+ roles?: components["schemas"]["RoleResponse"][];
877
+ };
878
+ /** @description One role of the effective registry (builtin + STAPEL_WORKSPACES overlay). */
879
+ RoleResponse: {
880
+ /**
881
+ * @description Role key
882
+ * @example admin
883
+ */
884
+ role: string;
885
+ /**
886
+ * @description Ordering weight; higher = more powerful
887
+ * @example 300
888
+ */
889
+ rank: number;
890
+ /**
891
+ * @description Granted capability strings, verbatim (wildcards like * included)
892
+ * @example [
893
+ * "workspace.view",
894
+ * "members.invite"
895
+ * ]
896
+ */
897
+ capabilities: string[];
898
+ /**
899
+ * @description Whether the role key is one of the builtin four
900
+ * @example true
901
+ */
902
+ builtin: boolean;
903
+ };
356
904
  /** @description Structured error returned by all Stapel API endpoints. */
357
905
  StapelError: {
358
906
  /**
@@ -360,10 +908,7 @@ export interface components {
360
908
  * @example error.404.not_found
361
909
  */
362
910
  localizable_error: string;
363
- /**
364
- * @description Human-readable message in English
365
- * @example Requested resource not found
366
- */
911
+ /** @description Human-readable fallback/debug message — not reliably English, */
367
912
  error: string;
368
913
  /**
369
914
  * @description Context values for template placeholders
@@ -374,6 +919,8 @@ export interface components {
374
919
  params?: {
375
920
  [key: string]: unknown;
376
921
  };
922
+ /** @description Active Django locale `error` was rendered in (e.g */
923
+ error_language?: string;
377
924
  };
378
925
  /** @description Create-workspace payload. */
379
926
  WorkspaceCreateRequest: {
@@ -451,6 +998,14 @@ export interface components {
451
998
  * @example 2026-05-20T10:00:00Z
452
999
  */
453
1000
  updated_at: string;
1001
+ /**
1002
+ * @description Granted capability strings of the requesting user's role, verbatim from the registry (wildcards like * included)
1003
+ * @example [
1004
+ * "workspace.view",
1005
+ * "members.view"
1006
+ * ]
1007
+ */
1008
+ my_capabilities?: string[];
454
1009
  };
455
1010
  };
456
1011
  responses: never;
@@ -573,6 +1128,82 @@ export interface operations {
573
1128
  };
574
1129
  };
575
1130
  };
1131
+ workspaces_api_v1_invitations_list: {
1132
+ parameters: {
1133
+ query?: {
1134
+ /** @description Anchor value to paginate from (exclusive) */
1135
+ anchor?: string;
1136
+ /** @description Pagination direction */
1137
+ direction?: "next" | "prev" | "center";
1138
+ /** @description Number of items (default 100, max 500) */
1139
+ limit?: number;
1140
+ /** @description Case-insensitive substring filter on the invited email address. */
1141
+ search?: string;
1142
+ /** @description Which invitations to return. `pending` (default) — live, actionable, seat-reserving ones, i.e. who has not accepted yet. `never_accepted` — those plus the declined, revoked and expired ones. `all` — the full history, accepted rows included. */
1143
+ status?: "all" | "never_accepted" | "pending";
1144
+ };
1145
+ header?: never;
1146
+ path: {
1147
+ workspace_id: string;
1148
+ };
1149
+ cookie?: never;
1150
+ };
1151
+ requestBody?: never;
1152
+ responses: {
1153
+ 200: {
1154
+ headers: {
1155
+ [name: string]: unknown;
1156
+ };
1157
+ content: {
1158
+ "application/json": components["schemas"]["PaginatedInvitationResponseList"];
1159
+ };
1160
+ };
1161
+ };
1162
+ };
1163
+ workspaces_api_v1_invitations_resend_create: {
1164
+ parameters: {
1165
+ query?: never;
1166
+ header?: never;
1167
+ path: {
1168
+ invitation_id: string;
1169
+ workspace_id: string;
1170
+ };
1171
+ cookie?: never;
1172
+ };
1173
+ requestBody?: never;
1174
+ responses: {
1175
+ 200: {
1176
+ headers: {
1177
+ [name: string]: unknown;
1178
+ };
1179
+ content: {
1180
+ "application/json": components["schemas"]["InvitationResponse"];
1181
+ };
1182
+ };
1183
+ };
1184
+ };
1185
+ workspaces_api_v1_invitations_revoke_create: {
1186
+ parameters: {
1187
+ query?: never;
1188
+ header?: never;
1189
+ path: {
1190
+ invitation_id: string;
1191
+ workspace_id: string;
1192
+ };
1193
+ cookie?: never;
1194
+ };
1195
+ requestBody?: never;
1196
+ responses: {
1197
+ 200: {
1198
+ headers: {
1199
+ [name: string]: unknown;
1200
+ };
1201
+ content: {
1202
+ "application/json": components["schemas"]["InvitationResponse"];
1203
+ };
1204
+ };
1205
+ };
1206
+ };
576
1207
  workspaces_api_v1_members_list: {
577
1208
  parameters: {
578
1209
  query?: {
@@ -652,6 +1283,34 @@ export interface operations {
652
1283
  };
653
1284
  };
654
1285
  };
1286
+ workspaces_api_v1_members_password_reset_create: {
1287
+ parameters: {
1288
+ query?: never;
1289
+ header?: never;
1290
+ path: {
1291
+ user_id: string;
1292
+ workspace_id: string;
1293
+ };
1294
+ cookie?: never;
1295
+ };
1296
+ requestBody?: {
1297
+ content: {
1298
+ "application/json": components["schemas"]["MemberPasswordResetRequest"];
1299
+ "application/x-www-form-urlencoded": components["schemas"]["MemberPasswordResetRequest"];
1300
+ "multipart/form-data": components["schemas"]["MemberPasswordResetRequest"];
1301
+ };
1302
+ };
1303
+ responses: {
1304
+ 200: {
1305
+ headers: {
1306
+ [name: string]: unknown;
1307
+ };
1308
+ content: {
1309
+ "application/json": components["schemas"]["MemberPasswordResetResponse"];
1310
+ };
1311
+ };
1312
+ };
1313
+ };
655
1314
  workspaces_api_v1_members_invite_create: {
656
1315
  parameters: {
657
1316
  query?: never;
@@ -679,6 +1338,33 @@ export interface operations {
679
1338
  };
680
1339
  };
681
1340
  };
1341
+ workspaces_api_v1_members_provision_create: {
1342
+ parameters: {
1343
+ query?: never;
1344
+ header?: never;
1345
+ path: {
1346
+ workspace_id: string;
1347
+ };
1348
+ cookie?: never;
1349
+ };
1350
+ requestBody: {
1351
+ content: {
1352
+ "application/json": components["schemas"]["ProvisionMemberRequest"];
1353
+ "application/x-www-form-urlencoded": components["schemas"]["ProvisionMemberRequest"];
1354
+ "multipart/form-data": components["schemas"]["ProvisionMemberRequest"];
1355
+ };
1356
+ };
1357
+ responses: {
1358
+ 201: {
1359
+ headers: {
1360
+ [name: string]: unknown;
1361
+ };
1362
+ content: {
1363
+ "application/json": components["schemas"]["ProvisionMemberResponse"];
1364
+ };
1365
+ };
1366
+ };
1367
+ };
682
1368
  workspaces_api_v1_internal_members_retrieve: {
683
1369
  parameters: {
684
1370
  query?: never;
@@ -730,6 +1416,68 @@ export interface operations {
730
1416
  };
731
1417
  };
732
1418
  };
1419
+ workspaces_api_v1_invitations_retrieve: {
1420
+ parameters: {
1421
+ query?: never;
1422
+ header?: never;
1423
+ path: {
1424
+ token: string;
1425
+ };
1426
+ cookie?: never;
1427
+ };
1428
+ requestBody?: never;
1429
+ responses: {
1430
+ 200: {
1431
+ headers: {
1432
+ [name: string]: unknown;
1433
+ };
1434
+ content: {
1435
+ "application/json": components["schemas"]["InvitationPreviewResponse"];
1436
+ };
1437
+ };
1438
+ };
1439
+ };
1440
+ workspaces_api_v1_invitations_claim_create: {
1441
+ parameters: {
1442
+ query?: never;
1443
+ header?: never;
1444
+ path: {
1445
+ token: string;
1446
+ };
1447
+ cookie?: never;
1448
+ };
1449
+ requestBody?: never;
1450
+ responses: {
1451
+ 200: {
1452
+ headers: {
1453
+ [name: string]: unknown;
1454
+ };
1455
+ content: {
1456
+ "application/json": components["schemas"]["InvitationClaimResponse"];
1457
+ };
1458
+ };
1459
+ };
1460
+ };
1461
+ workspaces_api_v1_invitations_decline_create: {
1462
+ parameters: {
1463
+ query?: never;
1464
+ header?: never;
1465
+ path: {
1466
+ token: string;
1467
+ };
1468
+ cookie?: never;
1469
+ };
1470
+ requestBody?: never;
1471
+ responses: {
1472
+ /** @description No response body */
1473
+ 204: {
1474
+ headers: {
1475
+ [name: string]: unknown;
1476
+ };
1477
+ content?: never;
1478
+ };
1479
+ };
1480
+ };
733
1481
  workspaces_api_v1_invitations_accept_create: {
734
1482
  parameters: {
735
1483
  query?: never;
@@ -755,5 +1503,24 @@ export interface operations {
755
1503
  };
756
1504
  };
757
1505
  };
1506
+ workspaces_api_v1_roles_retrieve: {
1507
+ parameters: {
1508
+ query?: never;
1509
+ header?: never;
1510
+ path?: never;
1511
+ cookie?: never;
1512
+ };
1513
+ requestBody?: never;
1514
+ responses: {
1515
+ 200: {
1516
+ headers: {
1517
+ [name: string]: unknown;
1518
+ };
1519
+ content: {
1520
+ "application/json": components["schemas"]["RoleListResponse"];
1521
+ };
1522
+ };
1523
+ };
1524
+ };
758
1525
  }
759
1526
  //# sourceMappingURL=schema.d.ts.map