@whooshbang/contracts 1.0.0-rc.21 → 1.0.0-rc.23

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 (125) hide show
  1. package/CHANGELOG.md +32 -0
  2. package/README.md +9 -3
  3. package/contract-manifest.json +43 -41
  4. package/dist/generated/artifact-index.d.ts +35 -35
  5. package/dist/generated/artifact-index.js +43 -41
  6. package/dist/generated/artifact-index.js.map +1 -1
  7. package/dist/generated/validators-standalone.d.ts +340 -288
  8. package/dist/generated/validators-standalone.js +5560 -2925
  9. package/dist/generated/validators-standalone.js.map +1 -1
  10. package/dist/generated/validators.d.ts +12 -8
  11. package/dist/generated/validators.js +22 -18
  12. package/dist/generated/validators.js.map +1 -1
  13. package/dist/generated/whooshbang-event-v1.d.ts +1 -1
  14. package/dist/generated/whooshbang-v1.d.ts +211 -121
  15. package/dist/index.d.ts +2 -2
  16. package/dist/index.js +1 -1
  17. package/dist/index.js.map +1 -1
  18. package/fixtures/core-v1/fixture-index.json +50 -34
  19. package/fixtures/core-v1/problems/credential-organization-mismatch.json +9 -0
  20. package/fixtures/core-v1/problems/live-simulator-refused.json +16 -0
  21. package/fixtures/core-v1/problems/project-slug-conflict.json +1 -1
  22. package/fixtures/core-v1/requests/create-organization-credential.json +4 -0
  23. package/fixtures/core-v1/requests/update-project.json +3 -0
  24. package/fixtures/core-v1/scenarios/api-credential-invalid-indistinguishable.json +1 -1
  25. package/fixtures/core-v1/scenarios/api-credential-one-time-reveal.json +1 -1
  26. package/fixtures/core-v1/scenarios/api-credential-revocation-replay.json +1 -1
  27. package/fixtures/core-v1/scenarios/api-credential-rotation-overlap.json +1 -1
  28. package/fixtures/core-v1/scenarios/cancellation-too-late.json +1 -1
  29. package/fixtures/core-v1/scenarios/cross-organization-project.json +14 -0
  30. package/fixtures/core-v1/scenarios/cross-project-resource.json +1 -1
  31. package/fixtures/core-v1/scenarios/environment-mismatch.json +1 -1
  32. package/fixtures/core-v1/scenarios/idempotency-conflict.json +1 -1
  33. package/fixtures/core-v1/scenarios/idempotency-equivalent-replay.json +1 -1
  34. package/fixtures/core-v1/scenarios/malformed-bearer.json +1 -1
  35. package/fixtures/core-v1/scenarios/metadata-overrun.json +1 -1
  36. package/fixtures/core-v1/scenarios/missing-scope.json +1 -1
  37. package/fixtures/core-v1/scenarios/project-create-equivalent-replay.json +2 -2
  38. package/fixtures/core-v1/scenarios/project-slug-conflict.json +2 -2
  39. package/fixtures/core-v1/scenarios/provider-outcome-unknown.json +1 -1
  40. package/fixtures/core-v1/scenarios/subscription-activation-replay.json +1 -1
  41. package/fixtures/core-v1/scenarios/subscription-authorization-capability-opacity.json +1 -1
  42. package/fixtures/core-v1/scenarios/subscription-cross-connection-callback-rejected.json +1 -1
  43. package/fixtures/core-v1/scenarios/subscription-expired-replay.json +1 -1
  44. package/fixtures/core-v1/scenarios/subscription-selected-connection-replay.json +1 -1
  45. package/fixtures/core-v1/success/{account-credential-collection.json → organization-credential-collection.json} +3 -3
  46. package/fixtures/core-v1/success/organization-credential-created.json +12 -0
  47. package/fixtures/core-v1/success/{account-credential-revoked.json → organization-credential-revoked.json} +3 -3
  48. package/fixtures/core-v1/success/{account-credential-rotated.json → organization-credential-rotated.json} +7 -7
  49. package/fixtures/core-v1/success/{account-projection.json → organization-projection.json} +2 -2
  50. package/fixtures/core-v1/success/project-collection.json +3 -2
  51. package/fixtures/core-v1/success/project-created.json +3 -2
  52. package/fixtures/interaction-machine-v1/scenarios/ack-conflict.json +1 -1
  53. package/fixtures/interaction-machine-v1/scenarios/ack-cross-machine.json +1 -1
  54. package/fixtures/interaction-machine-v1/scenarios/ack-out-of-order.json +1 -1
  55. package/fixtures/interaction-machine-v1/scenarios/ack-replay.json +1 -1
  56. package/fixtures/interaction-machine-v1/scenarios/ambiguous-free-text.json +1 -1
  57. package/fixtures/interaction-machine-v1/scenarios/callback-token-opacity.json +1 -1
  58. package/fixtures/interaction-machine-v1/scenarios/credential-registration-conflict.json +1 -1
  59. package/fixtures/interaction-machine-v1/scenarios/credential-registration-replay.json +1 -1
  60. package/fixtures/interaction-machine-v1/scenarios/credential-revocation-replay.json +1 -1
  61. package/fixtures/interaction-machine-v1/scenarios/credential-rotation.json +1 -1
  62. package/fixtures/interaction-machine-v1/scenarios/first-credential-activates.json +1 -1
  63. package/fixtures/interaction-machine-v1/scenarios/interaction-duplicate.json +1 -1
  64. package/fixtures/interaction-machine-v1/scenarios/interaction-expired.json +1 -1
  65. package/fixtures/interaction-machine-v1/scenarios/interaction-mismatch.json +1 -1
  66. package/fixtures/interaction-machine-v1/scenarios/interaction-retired.json +1 -1
  67. package/fixtures/interaction-machine-v1/scenarios/interaction-stale.json +1 -1
  68. package/fixtures/interaction-machine-v1/scenarios/machine-admin-denied.json +1 -1
  69. package/fixtures/interaction-machine-v1/scenarios/machine-bound-message-create.json +1 -1
  70. package/fixtures/interaction-machine-v1/scenarios/machine-cancel-denied.json +1 -1
  71. package/fixtures/interaction-machine-v1/scenarios/machine-crash-before-ack.json +1 -1
  72. package/fixtures/interaction-machine-v1/scenarios/machine-notifier-mismatch.json +1 -1
  73. package/fixtures/interaction-machine-v1/scenarios/machine-other-message-read.json +1 -1
  74. package/fixtures/interaction-machine-v1/scenarios/machine-owned-message-read.json +1 -1
  75. package/fixtures/interaction-machine-v1/scenarios/machine-subscriber-mismatch.json +1 -1
  76. package/fixtures/interaction-machine-v1/scenarios/poll-uncommitted-cursor-rejected.json +1 -1
  77. package/fixtures/interaction-machine-v1/scenarios/project-credential-machine-endpoint-denied.json +1 -1
  78. package/fixtures/interaction-machine-v1/scenarios/quarantine-integrity-rejected.json +1 -1
  79. package/fixtures/interaction-machine-v1/scenarios/quarantine-signature-rejected.json +1 -1
  80. package/fixtures/interaction-machine-v1/scenarios/quarantine-valid.json +1 -1
  81. package/fixtures/interaction-machine-v1/scenarios/revoked-machine-credential.json +1 -1
  82. package/fixtures/recipient-experience-v1/requests/create-recipient-group.json +1 -1
  83. package/fixtures/recipient-experience-v1/requests/create-subscriber-with-groups.json +1 -1
  84. package/fixtures/recipient-experience-v1/scenarios/membership-broadcast-snapshot.json +1 -1
  85. package/fixtures/recipient-experience-v1/scenarios/preferred-binding-resolution.json +1 -1
  86. package/fixtures/recipient-experience-v1/scenarios/recipient-session-security.json +1 -1
  87. package/fixtures/recipient-experience-v1/scenarios/subscriber-initial-groups-transaction.json +2 -2
  88. package/fixtures/recipient-experience-v1/success/recipient-group.json +1 -1
  89. package/fixtures/whooshbang-events-v1/events/agent-binding-degraded.json +1 -1
  90. package/fixtures/whooshbang-events-v1/events/interaction-expired.json +1 -1
  91. package/fixtures/whooshbang-events-v1/events/interaction-received.json +1 -1
  92. package/fixtures/whooshbang-events-v1/events/message-delivered.json +1 -1
  93. package/fixtures/whooshbang-events-v1/events/message-expired.json +1 -1
  94. package/fixtures/whooshbang-events-v1/events/message-failed.json +1 -1
  95. package/fixtures/whooshbang-events-v1/events/message-provider-accepted.json +1 -1
  96. package/fixtures/whooshbang-events-v1/events/subscription-activated.json +1 -1
  97. package/fixtures/whooshbang-events-v1/events/subscription-revoked.json +1 -1
  98. package/fixtures/whooshbang-events-v1/signing/vectors.json +29 -29
  99. package/openapi/generated/whooshbang-v1.bundle.json +641 -248
  100. package/openapi/whooshbang-v1.yaml +403 -189
  101. package/package.json +1 -1
  102. package/schemas/core-scenario.schema.json +1 -1
  103. package/schemas/events/agent-binding-degraded-v1.schema.json +1 -1
  104. package/schemas/events/interaction-expired-v1.schema.json +1 -1
  105. package/schemas/events/interaction-received-v1.schema.json +1 -1
  106. package/schemas/events/message-delivered-v1.schema.json +1 -1
  107. package/schemas/events/message-expired-v1.schema.json +1 -1
  108. package/schemas/events/message-failed-v1.schema.json +1 -1
  109. package/schemas/events/message-provider-accepted-v1.schema.json +1 -1
  110. package/schemas/events/subscription-activated-v1.schema.json +1 -1
  111. package/schemas/events/subscription-revoked-v1.schema.json +1 -1
  112. package/schemas/events/whooshbang-event-v1.schema.json +3 -3
  113. package/schemas/fixture-index.schema.json +1 -1
  114. package/schemas/interaction-machine-scenario.schema.json +1 -1
  115. package/schemas/interaction-v1.schema.json +1 -1
  116. package/schemas/machine-v1.schema.json +1 -1
  117. package/schemas/provider-capabilities-v1.schema.json +1 -1
  118. package/schemas/webhooks/webhook-delivery-case-v1.schema.json +1 -1
  119. package/schemas/webhooks/webhook-signing-vector-v1.schema.json +1 -1
  120. package/schemas/whooshbang-v1.schema.json +289 -67
  121. package/fixtures/core-v1/problems/credential-account-mismatch.json +0 -9
  122. package/fixtures/core-v1/requests/create-account-credential.json +0 -4
  123. package/fixtures/core-v1/scenarios/cross-account-project.json +0 -14
  124. package/fixtures/core-v1/success/account-credential-created.json +0 -12
  125. /package/fixtures/core-v1/requests/{rotate-account-credential.json → rotate-organization-credential.json} +0 -0
@@ -1,12 +1,18 @@
1
1
  {
2
2
  "$schema": "https://json-schema.org/draft/2020-12/schema",
3
3
  "$id": "https://whooshbang.flowxo.com/schemas/whooshbang-v1.schema.json",
4
- "x-contract-version": "1.0.0-rc.21",
4
+ "x-contract-version": "1.0.0-rc.23",
5
5
  "title": "WhooshBangV1ContractDocument",
6
6
  "description": "Public request, resource, summary, and Problem Details models for the WhooshBang V1 REST API.",
7
7
  "anyOf": [
8
8
  {
9
- "$ref": "#/$defs/AccountProjection"
9
+ "$ref": "#/$defs/OrganizationProjection"
10
+ },
11
+ {
12
+ "$ref": "#/$defs/CloseOrganizationRequest"
13
+ },
14
+ {
15
+ "$ref": "#/$defs/OrganizationClosureResult"
10
16
  },
11
17
  {
12
18
  "$ref": "#/$defs/CreateProjectRequest"
@@ -14,6 +20,12 @@
14
20
  {
15
21
  "$ref": "#/$defs/Project"
16
22
  },
23
+ {
24
+ "$ref": "#/$defs/UpdateProjectRequest"
25
+ },
26
+ {
27
+ "$ref": "#/$defs/PrivacyMode"
28
+ },
17
29
  {
18
30
  "$ref": "#/$defs/ProjectCollection"
19
31
  },
@@ -42,22 +54,22 @@
42
54
  "$ref": "#/$defs/ApiCredentialRotation"
43
55
  },
44
56
  {
45
- "$ref": "#/$defs/CreateAccountCredentialRequest"
57
+ "$ref": "#/$defs/CreateOrganizationCredentialRequest"
46
58
  },
47
59
  {
48
- "$ref": "#/$defs/RotateAccountCredentialRequest"
60
+ "$ref": "#/$defs/RotateOrganizationCredentialRequest"
49
61
  },
50
62
  {
51
- "$ref": "#/$defs/AccountCredential"
63
+ "$ref": "#/$defs/OrganizationCredential"
52
64
  },
53
65
  {
54
- "$ref": "#/$defs/AccountCredentialCollection"
66
+ "$ref": "#/$defs/OrganizationCredentialCollection"
55
67
  },
56
68
  {
57
- "$ref": "#/$defs/AccountCredentialSecretReveal"
69
+ "$ref": "#/$defs/OrganizationCredentialSecretReveal"
58
70
  },
59
71
  {
60
- "$ref": "#/$defs/AccountCredentialRotation"
72
+ "$ref": "#/$defs/OrganizationCredentialRotation"
61
73
  },
62
74
  {
63
75
  "$ref": "#/$defs/CreateCustomerEndpointRequest"
@@ -182,6 +194,9 @@
182
194
  {
183
195
  "$ref": "#/$defs/Subscriber"
184
196
  },
197
+ {
198
+ "$ref": "#/$defs/SubscriberDataExport"
199
+ },
185
200
  {
186
201
  "$ref": "#/$defs/CreateRecipientGroupRequest"
187
202
  },
@@ -317,22 +332,22 @@
317
332
  },
318
333
  "x-max-json-bytes": 4096
319
334
  },
320
- "AccountRole": {
335
+ "OrganizationRole": {
321
336
  "type": "string",
322
337
  "enum": ["admin", "member"],
323
338
  "description": "The stable WhooshBang authorization role projected from the current Clerk organization membership."
324
339
  },
325
- "AccountMembershipSummary": {
340
+ "OrganizationMembershipSummary": {
326
341
  "type": "object",
327
342
  "required": ["role"],
328
343
  "properties": {
329
344
  "role": {
330
- "$ref": "#/$defs/AccountRole"
345
+ "$ref": "#/$defs/OrganizationRole"
331
346
  }
332
347
  },
333
348
  "additionalProperties": false
334
349
  },
335
- "AccountProjection": {
350
+ "OrganizationProjection": {
336
351
  "type": "object",
337
352
  "description": "The current Clerk organization projected as a WhooshBang tenant. Clerk organization and user identifiers are not public fields.",
338
353
  "required": [
@@ -358,7 +373,7 @@
358
373
  "enum": ["active", "suspended"]
359
374
  },
360
375
  "membership": {
361
- "$ref": "#/$defs/AccountMembershipSummary"
376
+ "$ref": "#/$defs/OrganizationMembershipSummary"
362
377
  },
363
378
  "created_at": {
364
379
  "$ref": "#/$defs/Timestamp"
@@ -372,9 +387,62 @@
372
387
  },
373
388
  "additionalProperties": false
374
389
  },
390
+ "CloseOrganizationRequest": {
391
+ "type": "object",
392
+ "description": "Exact, explicit confirmation that the current organization administrator intends to close the whole organization.",
393
+ "required": ["confirmation"],
394
+ "properties": {
395
+ "confirmation": {
396
+ "type": "string",
397
+ "const": "close-organization"
398
+ }
399
+ },
400
+ "additionalProperties": false
401
+ },
402
+ "OrganizationClosureProviderAction": {
403
+ "type": "object",
404
+ "description": "One provider-owned resource the customer must retire outside WhooshBang after organization closure. It contains safe identifiers only, never provider credentials.",
405
+ "required": ["provider", "action", "connection_id"],
406
+ "properties": {
407
+ "provider": {
408
+ "type": "string",
409
+ "enum": ["telegram", "slack"]
410
+ },
411
+ "action": {
412
+ "type": "string",
413
+ "enum": ["revoke_bot_token", "delete_provider_application"]
414
+ },
415
+ "connection_id": {
416
+ "$ref": "#/$defs/OpaqueIdentifier"
417
+ }
418
+ },
419
+ "additionalProperties": false
420
+ },
421
+ "OrganizationClosureResult": {
422
+ "type": "object",
423
+ "description": "Terminal organization-closure result. Repeating closure returns the same closed_at and any provider-owned resources that still require manual retirement.",
424
+ "required": ["status", "closed_at", "provider_actions_required"],
425
+ "properties": {
426
+ "status": {
427
+ "type": "string",
428
+ "const": "closed"
429
+ },
430
+ "closed_at": {
431
+ "$ref": "#/$defs/Timestamp"
432
+ },
433
+ "provider_actions_required": {
434
+ "type": "array",
435
+ "uniqueItems": true,
436
+ "items": {
437
+ "$ref": "#/$defs/OrganizationClosureProviderAction"
438
+ }
439
+ }
440
+ },
441
+ "additionalProperties": false
442
+ },
375
443
  "CreateProjectRequest": {
376
444
  "type": "object",
377
- "description": "Creates one project inside the account selected by the authenticated human session.",
445
+ "description": "Creates one project inside the organization selected by the authenticated human session.",
378
446
  "required": ["name", "slug"],
379
447
  "properties": {
380
448
  "name": {
@@ -392,6 +460,10 @@
392
460
  "$ref": "#/$defs/RecipientLanguage",
393
461
  "default": "en",
394
462
  "description": "The application fallback locale for recipient-owned surfaces when no session, subscriber, or supported provider locale is available."
463
+ },
464
+ "privacy_mode": {
465
+ "$ref": "#/$defs/PrivacyMode",
466
+ "default": "minimal"
395
467
  }
396
468
  },
397
469
  "additionalProperties": false
@@ -452,10 +524,10 @@
452
524
  },
453
525
  "Project": {
454
526
  "type": "object",
455
- "description": "An account-scoped application project with its complete test/live bootstrap.",
527
+ "description": "An organization-scoped application project with its complete test/live bootstrap.",
456
528
  "required": [
457
529
  "id",
458
- "account_id",
530
+ "organization_id",
459
531
  "name",
460
532
  "slug",
461
533
  "status",
@@ -463,13 +535,14 @@
463
535
  "environments",
464
536
  "created_at",
465
537
  "updated_at",
466
- "diagnostic_id"
538
+ "diagnostic_id",
539
+ "privacy_mode"
467
540
  ],
468
541
  "properties": {
469
542
  "id": {
470
543
  "$ref": "#/$defs/OpaqueIdentifier"
471
544
  },
472
- "account_id": {
545
+ "organization_id": {
473
546
  "$ref": "#/$defs/OpaqueIdentifier"
474
547
  },
475
548
  "name": {
@@ -507,13 +580,16 @@
507
580
  },
508
581
  "diagnostic_id": {
509
582
  "$ref": "#/$defs/DiagnosticId"
583
+ },
584
+ "privacy_mode": {
585
+ "$ref": "#/$defs/PrivacyMode"
510
586
  }
511
587
  },
512
588
  "additionalProperties": false
513
589
  },
514
590
  "ProjectCollection": {
515
591
  "type": "object",
516
- "description": "A stable page of projects inside the authenticated account.",
592
+ "description": "A stable page of projects inside the authenticated organization.",
517
593
  "required": ["items"],
518
594
  "properties": {
519
595
  "items": {
@@ -761,7 +837,7 @@
761
837
  "connections:write",
762
838
  "connections:read"
763
839
  ],
764
- "description": "One project-credential capability. Environment authority always comes from the credential record, never a scope or request field. `projects:write` configures the credential's own project and environment; it never creates a project, which remains an account authority. The project-wide singletons — brand, default locale, logo and recipient copy — are what a live recipient sees, so writing them requires a live credential; a test credential may read them and may write its own environment's policy."
840
+ "description": "One project-credential capability. Environment authority always comes from the credential record, never a scope or request field. `projects:write` configures the credential's own project and environment; it never creates a project, which remains an organization authority. The project-wide singletons — brand, default locale, logo and recipient copy — are what a live recipient sees, so writing them requires a live credential; a test credential may read them and may write its own environment's policy."
765
841
  },
766
842
  "ApiCredentialScopeSet": {
767
843
  "type": "array",
@@ -946,25 +1022,25 @@
946
1022
  },
947
1023
  "additionalProperties": false
948
1024
  },
949
- "AccountCredentialId": {
1025
+ "OrganizationCredentialId": {
950
1026
  "type": "string",
951
- "pattern": "^acred_[A-Za-z0-9_-]{21}[AQgw]$",
952
- "description": "acred_ followed by the canonical unpadded base64url encoding of exactly 16 random bytes."
1027
+ "pattern": "^ocred_[A-Za-z0-9_-]{21}[AQgw]$",
1028
+ "description": "ocred_ followed by the canonical unpadded base64url encoding of exactly 16 random bytes."
953
1029
  },
954
- "AccountCredentialPrefix": {
1030
+ "OrganizationCredentialPrefix": {
955
1031
  "type": "string",
956
- "pattern": "^wb_ac1\\.acred_[A-Za-z0-9_-]{8}$",
957
- "description": "A safe display prefix containing the account-credential format marker and first eight base64url identity characters, never secret bytes."
1032
+ "pattern": "^wb_oc1\\.ocred_[A-Za-z0-9_-]{8}$",
1033
+ "description": "A safe display prefix containing the organization-credential format marker and first eight base64url identity characters, never secret bytes."
958
1034
  },
959
- "AccountCredentialSecret": {
1035
+ "OrganizationCredentialSecret": {
960
1036
  "type": "string",
961
- "pattern": "^wb_ac1\\.acred_[A-Za-z0-9_-]{21}[AQgw]\\.[A-Za-z0-9_-]{42}[AEIMQUYcgkosw048]$",
1037
+ "pattern": "^wb_oc1\\.ocred_[A-Za-z0-9_-]{21}[AQgw]\\.[A-Za-z0-9_-]{42}[AEIMQUYcgkosw048]$",
962
1038
  "description": "The exact one-time runtime bearer syntax. The final segment is canonical unpadded base64url for exactly 32 random secret bytes."
963
1039
  },
964
- "AccountCredentialScope": {
1040
+ "OrganizationCredentialScope": {
965
1041
  "type": "string",
966
1042
  "enum": [
967
- "account:read",
1043
+ "organization:read",
968
1044
  "projects:write",
969
1045
  "projects:read",
970
1046
  "messages:write",
@@ -977,20 +1053,20 @@
977
1053
  "connections:write",
978
1054
  "connections:read"
979
1055
  ],
980
- "description": "One account-credential capability. `account:read` is the account's project directory — listing the account's projects and reading one — and is the only scope here a project credential can never carry; it authorizes nothing below the account. Every other name is the project-credential vocabulary unchanged, and is exercised only on the environment-scoped routes, where the project and environment come from the path and the credential's authority is the intersection of these scopes with the project-credential vocabulary. An account credential is therefore never wider than a project credential would have been for the same project environment."
1056
+ "description": "One organization-credential capability. `organization:read` is the organization's project directory — listing the organization's projects and reading one — and is the only scope here a project credential can never carry; it authorizes nothing below the organization. Every other name is the project-credential vocabulary unchanged, and is exercised only on the environment-scoped routes, where the project and environment come from the path and the credential's authority is the intersection of these scopes with the project-credential vocabulary. An organization credential is therefore never wider than a project credential would have been for the same project environment."
981
1057
  },
982
- "AccountCredentialScopeSet": {
1058
+ "OrganizationCredentialScopeSet": {
983
1059
  "type": "array",
984
1060
  "minItems": 1,
985
1061
  "maxItems": 12,
986
1062
  "uniqueItems": true,
987
1063
  "items": {
988
- "$ref": "#/$defs/AccountCredentialScope"
1064
+ "$ref": "#/$defs/OrganizationCredentialScope"
989
1065
  }
990
1066
  },
991
- "CreateAccountCredentialRequest": {
1067
+ "CreateOrganizationCredentialRequest": {
992
1068
  "type": "object",
993
- "description": "Creates one credential against the whole account rather than one project environment.",
1069
+ "description": "Creates one credential against the whole organization rather than one project environment.",
994
1070
  "required": ["label", "scopes"],
995
1071
  "properties": {
996
1072
  "label": {
@@ -999,12 +1075,12 @@
999
1075
  "maxLength": 100
1000
1076
  },
1001
1077
  "scopes": {
1002
- "$ref": "#/$defs/AccountCredentialScopeSet"
1078
+ "$ref": "#/$defs/OrganizationCredentialScopeSet"
1003
1079
  }
1004
1080
  },
1005
1081
  "additionalProperties": false
1006
1082
  },
1007
- "RotateAccountCredentialRequest": {
1083
+ "RotateOrganizationCredentialRequest": {
1008
1084
  "type": "object",
1009
1085
  "description": "Creates a replacement with the same label and scopes and schedules terminal revocation of the replaced credential after the declared overlap.",
1010
1086
  "required": ["overlap_seconds"],
@@ -1017,9 +1093,9 @@
1017
1093
  },
1018
1094
  "additionalProperties": false
1019
1095
  },
1020
- "AccountCredential": {
1096
+ "OrganizationCredential": {
1021
1097
  "type": "object",
1022
- "description": "Safe account-scoped API credential metadata. It carries no project or environment, because it names neither: a request does, in its path. It never contains a bearer secret, digest, Clerk identifier, or digest-key reference.",
1098
+ "description": "Safe organization-scoped API credential metadata. It carries no project or environment, because it names neither: a request does, in its path. It never contains a bearer secret, digest, Clerk identifier, or digest-key reference.",
1023
1099
  "required": [
1024
1100
  "id",
1025
1101
  "prefix",
@@ -1031,10 +1107,10 @@
1031
1107
  ],
1032
1108
  "properties": {
1033
1109
  "id": {
1034
- "$ref": "#/$defs/AccountCredentialId"
1110
+ "$ref": "#/$defs/OrganizationCredentialId"
1035
1111
  },
1036
1112
  "prefix": {
1037
- "$ref": "#/$defs/AccountCredentialPrefix"
1113
+ "$ref": "#/$defs/OrganizationCredentialPrefix"
1038
1114
  },
1039
1115
  "label": {
1040
1116
  "type": "string",
@@ -1042,7 +1118,7 @@
1042
1118
  "maxLength": 100
1043
1119
  },
1044
1120
  "scopes": {
1045
- "$ref": "#/$defs/AccountCredentialScopeSet"
1121
+ "$ref": "#/$defs/OrganizationCredentialScopeSet"
1046
1122
  },
1047
1123
  "created_by": {
1048
1124
  "$ref": "#/$defs/OpaqueIdentifier",
@@ -1092,16 +1168,16 @@
1092
1168
  ],
1093
1169
  "additionalProperties": false
1094
1170
  },
1095
- "AccountCredentialCollection": {
1171
+ "OrganizationCredentialCollection": {
1096
1172
  "type": "object",
1097
- "description": "A stable page of safe account-credential metadata for one account.",
1173
+ "description": "A stable page of safe organization-credential metadata for one organization.",
1098
1174
  "required": ["items"],
1099
1175
  "properties": {
1100
1176
  "items": {
1101
1177
  "type": "array",
1102
1178
  "maxItems": 100,
1103
1179
  "items": {
1104
- "$ref": "#/$defs/AccountCredential"
1180
+ "$ref": "#/$defs/OrganizationCredential"
1105
1181
  }
1106
1182
  },
1107
1183
  "next_cursor": {
@@ -1122,30 +1198,30 @@
1122
1198
  },
1123
1199
  "additionalProperties": false
1124
1200
  },
1125
- "AccountCredentialSecretReveal": {
1201
+ "OrganizationCredentialSecretReveal": {
1126
1202
  "type": "object",
1127
1203
  "description": "The creation-only response. The secret is emitted in this response exactly once and is never available from list, rotate-target, revoke, or later read responses.",
1128
1204
  "required": ["credential", "secret"],
1129
1205
  "properties": {
1130
1206
  "credential": {
1131
- "$ref": "#/$defs/AccountCredential"
1207
+ "$ref": "#/$defs/OrganizationCredential"
1132
1208
  },
1133
1209
  "secret": {
1134
- "$ref": "#/$defs/AccountCredentialSecret"
1210
+ "$ref": "#/$defs/OrganizationCredentialSecret"
1135
1211
  }
1136
1212
  },
1137
1213
  "additionalProperties": false
1138
1214
  },
1139
- "AccountCredentialRotation": {
1215
+ "OrganizationCredentialRotation": {
1140
1216
  "type": "object",
1141
1217
  "description": "A creation-only replacement secret plus safe metadata for the credential it replaces.",
1142
1218
  "required": ["credential", "replaced_credential"],
1143
1219
  "properties": {
1144
1220
  "credential": {
1145
- "$ref": "#/$defs/AccountCredentialSecretReveal"
1221
+ "$ref": "#/$defs/OrganizationCredentialSecretReveal"
1146
1222
  },
1147
1223
  "replaced_credential": {
1148
- "$ref": "#/$defs/AccountCredential"
1224
+ "$ref": "#/$defs/OrganizationCredential"
1149
1225
  }
1150
1226
  },
1151
1227
  "additionalProperties": false
@@ -1590,7 +1666,7 @@
1590
1666
  },
1591
1667
  "RecipientSession": {
1592
1668
  "type": "object",
1593
- "description": "A browser-safe projection for the exact subscriber selected when the session was created. Account, project, environment, subscriber, origin, resolved return URL, provider identity, credentials, and group state are structurally absent.",
1669
+ "description": "A browser-safe projection for the exact subscriber selected when the session was created. Organization, project, environment, subscriber, origin, resolved return URL, provider identity, credentials, and group state are structurally absent.",
1594
1670
  "required": [
1595
1671
  "id",
1596
1672
  "capability_prefix",
@@ -1969,6 +2045,115 @@
1969
2045
  },
1970
2046
  "additionalProperties": false
1971
2047
  },
2048
+ "SubscriberDataExportRecord": {
2049
+ "type": "object",
2050
+ "description": "One semantic record held about the subscriber. The source names the durable store or recovered semantic value; data contains plaintext business fields and excludes custody metadata and secrets.",
2051
+ "required": ["source", "data"],
2052
+ "properties": {
2053
+ "source": {
2054
+ "type": "string",
2055
+ "minLength": 1,
2056
+ "maxLength": 96,
2057
+ "pattern": "^[a-z][a-z0-9_.-]*$"
2058
+ },
2059
+ "data": {
2060
+ "type": "object",
2061
+ "additionalProperties": true
2062
+ }
2063
+ },
2064
+ "additionalProperties": false
2065
+ },
2066
+ "SubscriberDataExportExcludedControllerData": {
2067
+ "type": "object",
2068
+ "description": "A category deliberately outside the customer-controller artifact because WhooshBang is controller for it.",
2069
+ "required": ["category", "controller", "access"],
2070
+ "properties": {
2071
+ "category": {
2072
+ "type": "string",
2073
+ "enum": ["consent_lifecycle_audit", "service_logs"]
2074
+ },
2075
+ "controller": {
2076
+ "type": "string",
2077
+ "const": "whooshbang"
2078
+ },
2079
+ "access": {
2080
+ "type": "string",
2081
+ "const": "separate_recipient_request_required"
2082
+ }
2083
+ },
2084
+ "additionalProperties": false
2085
+ },
2086
+ "SubscriberDataExport": {
2087
+ "type": "object",
2088
+ "description": "A complete customer-controller access artifact for one subscriber across every environment in one project. provided_by_subscriber is the Article 20 portable subset; all three record groups together are the Article 15 access copy. WhooshBang-controller records are named but never mixed into the customer export.",
2089
+ "required": [
2090
+ "format",
2091
+ "generated_at",
2092
+ "controller_scope",
2093
+ "project_id",
2094
+ "environment_ids",
2095
+ "subscriber_id",
2096
+ "provided_by_subscriber",
2097
+ "provided_by_customer",
2098
+ "derived_by_service",
2099
+ "excluded_controller_data"
2100
+ ],
2101
+ "properties": {
2102
+ "format": {
2103
+ "type": "string",
2104
+ "const": "whooshbang.subscriber-data-export.v1"
2105
+ },
2106
+ "generated_at": {
2107
+ "$ref": "#/$defs/Timestamp"
2108
+ },
2109
+ "controller_scope": {
2110
+ "type": "string",
2111
+ "const": "customer"
2112
+ },
2113
+ "project_id": {
2114
+ "$ref": "#/$defs/OpaqueIdentifier"
2115
+ },
2116
+ "environment_ids": {
2117
+ "type": "array",
2118
+ "minItems": 1,
2119
+ "uniqueItems": true,
2120
+ "items": {
2121
+ "$ref": "#/$defs/OpaqueIdentifier"
2122
+ }
2123
+ },
2124
+ "subscriber_id": {
2125
+ "$ref": "#/$defs/OpaqueIdentifier"
2126
+ },
2127
+ "provided_by_subscriber": {
2128
+ "type": "array",
2129
+ "items": {
2130
+ "$ref": "#/$defs/SubscriberDataExportRecord"
2131
+ }
2132
+ },
2133
+ "provided_by_customer": {
2134
+ "type": "array",
2135
+ "items": {
2136
+ "$ref": "#/$defs/SubscriberDataExportRecord"
2137
+ }
2138
+ },
2139
+ "derived_by_service": {
2140
+ "type": "array",
2141
+ "items": {
2142
+ "$ref": "#/$defs/SubscriberDataExportRecord"
2143
+ }
2144
+ },
2145
+ "excluded_controller_data": {
2146
+ "type": "array",
2147
+ "minItems": 2,
2148
+ "maxItems": 2,
2149
+ "uniqueItems": true,
2150
+ "items": {
2151
+ "$ref": "#/$defs/SubscriberDataExportExcludedControllerData"
2152
+ }
2153
+ }
2154
+ },
2155
+ "additionalProperties": false
2156
+ },
1972
2157
  "CreateRecipientGroupRequest": {
1973
2158
  "type": "object",
1974
2159
  "description": "Creates one static, developer-managed recipient group.",
@@ -3237,7 +3422,7 @@
3237
3422
  },
3238
3423
  "delivery_target": {
3239
3424
  "$ref": "#/$defs/MessageDeliveryTarget",
3240
- "description": "Optional routing override. Omit for notifier-default connection delivery, except reserved simulator destinations retained for compatibility."
3425
+ "description": "Optional routing override. Simulator delivery and the reserved test: subscriber prefix are test-environment-only; live requests are refused with 400 request_invalid. Omit for notifier-default connection delivery or the reserved test-environment simulator default."
3241
3426
  },
3242
3427
  "connection_id": {
3243
3428
  "allOf": [{ "$ref": "#/$defs/OpaqueIdentifier" }],
@@ -3828,6 +4013,7 @@
3828
4013
  "environment_mismatch",
3829
4014
  "idempotency_key_required",
3830
4015
  "idempotency_conflict",
4016
+ "privacy_operation_in_progress",
3831
4017
  "project_slug_conflict",
3832
4018
  "request_invalid",
3833
4019
  "replay_unavailable",
@@ -3845,7 +4031,7 @@
3845
4031
  "resource_not_found",
3846
4032
  "resource_expired",
3847
4033
  "cancellation_too_late",
3848
- "credential_account_mismatch",
4034
+ "credential_organization_mismatch",
3849
4035
  "endpoint_rotation_conflict",
3850
4036
  "rate_limited",
3851
4037
  "provider_retryable",
@@ -4253,6 +4439,26 @@
4253
4439
  }
4254
4440
  }
4255
4441
  },
4442
+ {
4443
+ "type": "object",
4444
+ "properties": {
4445
+ "type": {
4446
+ "const": "https://whooshbang.flowxo.com/problems/privacy_operation_in_progress"
4447
+ },
4448
+ "title": {
4449
+ "const": "Privacy operation in progress"
4450
+ },
4451
+ "status": {
4452
+ "const": 409
4453
+ },
4454
+ "code": {
4455
+ "const": "privacy_operation_in_progress"
4456
+ },
4457
+ "retryable": {
4458
+ "const": true
4459
+ }
4460
+ }
4461
+ },
4256
4462
  {
4257
4463
  "type": "object",
4258
4464
  "properties": {
@@ -4557,16 +4763,16 @@
4557
4763
  "type": "object",
4558
4764
  "properties": {
4559
4765
  "type": {
4560
- "const": "https://whooshbang.flowxo.com/problems/credential_account_mismatch"
4766
+ "const": "https://whooshbang.flowxo.com/problems/credential_organization_mismatch"
4561
4767
  },
4562
4768
  "title": {
4563
- "const": "Credential account mismatch"
4769
+ "const": "Credential organization mismatch"
4564
4770
  },
4565
4771
  "status": {
4566
4772
  "const": 403
4567
4773
  },
4568
4774
  "code": {
4569
- "const": "credential_account_mismatch"
4775
+ "const": "credential_organization_mismatch"
4570
4776
  },
4571
4777
  "retryable": {
4572
4778
  "const": false
@@ -4656,16 +4862,16 @@
4656
4862
  ],
4657
4863
  "additionalProperties": false
4658
4864
  },
4659
- "AccountScope": {
4865
+ "OrganizationScope": {
4660
4866
  "type": "string",
4661
4867
  "enum": ["projects:write", "projects:read"],
4662
- "description": "One account-level capability. Only a human session or an OAuth grant can hold one: a project credential lives inside a project and can never create or enumerate its siblings."
4868
+ "description": "One organization-level capability. Only a human session or an OAuth grant can hold one: a project credential lives inside a project and can never create or enumerate its siblings."
4663
4869
  },
4664
4870
  "OAuthScope": {
4665
- "description": "A scope an OAuth grant may carry. Project-level scopes are the credential vocabulary unchanged; account-level scopes exist only here and on a human session.",
4871
+ "description": "A scope an OAuth grant may carry. Project-level scopes are the credential vocabulary unchanged; organization-level scopes exist only here and on a human session.",
4666
4872
  "oneOf": [
4667
4873
  {
4668
- "$ref": "#/$defs/AccountScope"
4874
+ "$ref": "#/$defs/OrganizationScope"
4669
4875
  },
4670
4876
  {
4671
4877
  "$ref": "#/$defs/ApiCredentialScope"
@@ -4708,7 +4914,7 @@
4708
4914
  "failed",
4709
4915
  "expired"
4710
4916
  ],
4711
- "description": "How far a guided setup has reached, which is the only thing a caller can act on while a connection is still `pending`. `awaiting_handoff` waits for the administrator to open the link on the Telegram account that will own the bot; `awaiting_creation` means that account is bound and the bot does not exist yet; `provisioning` means the bot exists and WhooshBang is installing delivery on it. `failed` and `expired` end the attempt without ending the connection \u2014 another attempt can be started on the same `pending` connection \u2014 and neither withdraws a bot Telegram has already created, because no provider API can delete one."
4917
+ "description": "How far a guided setup has reached, which is the only thing a caller can act on while a connection is still `pending`. `awaiting_handoff` waits for the administrator to open the link on the Telegram account that will own the bot; `awaiting_creation` means that Telegram account is bound and the bot does not exist yet; `provisioning` means the bot exists and WhooshBang is installing delivery on it. `failed` and `expired` end the attempt without ending the connection \u2014 another attempt can be started on the same `pending` connection \u2014 and neither withdraws a bot Telegram has already created, because no provider API can delete one."
4712
4918
  },
4713
4919
  "HostedChannelConnectionSetupStatus": {
4714
4920
  "type": "string",
@@ -4877,7 +5083,7 @@
4877
5083
  "format": "uri",
4878
5084
  "pattern": "^https://[^\\s]+$",
4879
5085
  "maxLength": 2048,
4880
- "description": "The link the administrator opens to put their own Telegram account behind this setup; for Telegram it is `https://t.me/<manager>?start=<payload>`. It is a capability for exactly one setup session and nothing else: it names no account, no project, and no environment, it stops working at `expires_at`, and only the first Telegram account to open it is bound. It is not credential material, so displaying it costs nothing beyond letting whoever sees it first become the owner of the bot this setup creates. After `expired` or `failed` it binds nothing, and restarting the setup issues a new one rather than reviving this."
5086
+ "description": "The link the administrator opens to put their own Telegram account behind this setup; for Telegram it is `https://t.me/<manager>?start=<payload>`. It is a capability for exactly one setup session and nothing else: it names no organization, no project, and no environment, it stops working at `expires_at`, and only the first Telegram account to open it is bound. It is not credential material, so displaying it costs nothing beyond letting whoever sees it first become the owner of the bot this setup creates. After `expired` or `failed` it binds nothing, and restarting the setup issues a new one rather than reviving this."
4881
5087
  },
4882
5088
  "expires_at": {
4883
5089
  "$ref": "#/$defs/Timestamp",
@@ -4891,7 +5097,7 @@
4891
5097
  "type": "string",
4892
5098
  "minLength": 1,
4893
5099
  "maxLength": 200,
4894
- "description": "Why this attempt ended, in terms the administrator can act on. Absent when there is nothing both safe and useful to say: an attempt refused because that Telegram account already holds a live setup says only that, because naming the account, project, or environment holding the other one would disclose another customer."
5100
+ "description": "Why this attempt ended, in terms the administrator can act on. Absent when there is nothing both safe and useful to say: an attempt refused because that Telegram account already holds a live setup says only that, because naming the organization, project, or environment holding the other one would disclose another customer."
4895
5101
  }
4896
5102
  },
4897
5103
  "additionalProperties": false
@@ -4981,7 +5187,7 @@
4981
5187
  "format": "uri",
4982
5188
  "pattern": "^https://api\\.slack\\.com/apps\\?[^\\s]+$",
4983
5189
  "maxLength": 8192,
4984
- "description": "Slack's own app-creation flow, opened with this app's manifest already filled in, so the owner reviews and presses Create rather than writing or pasting YAML. The URL carries configuration only: it names no account, project, or environment and contains no credential. The opaque handle it does carry selects candidate setup state and authorizes nothing."
5190
+ "description": "Slack's own app-creation flow, opened with this app's manifest already filled in, so the owner reviews and presses Create rather than writing or pasting YAML. The URL carries configuration only: it names no organization, project, or environment and contains no credential. The opaque handle it does carry selects candidate setup state and authorizes nothing."
4985
5191
  },
4986
5192
  "expires_at": {
4987
5193
  "$ref": "#/$defs/Timestamp",
@@ -5364,7 +5570,7 @@
5364
5570
  "additionalProperties": false
5365
5571
  },
5366
5572
  "CreateChannelConnectionRequest": {
5367
- "description": "Selects the provider identity this environment will send through, discriminated on `mode`, and naming the provider it is for. The two customer-owned modes differ only in where the bot comes from: `customer_byok` connects one the customer already has, and `customer_managed` has Telegram create a new one on the administrator's own account. Neither supersedes the other, because a bot that already exists can never be adopted into managed mode \u2014 the manager that may act for a bot is fixed when the bot is created.",
5573
+ "description": "Selects the provider identity this environment will send through, discriminated on `mode`, and naming the provider it is for. The two customer-owned modes differ only in where the bot comes from: `customer_byok` connects one the customer already has, and `customer_managed` has Telegram create a new one on the administrator's own Telegram account. Neither supersedes the other, because a bot that already exists can never be adopted into managed mode \u2014 the manager that may act for a bot is fixed when the bot is created.",
5368
5574
  "oneOf": [
5369
5575
  {
5370
5576
  "type": "object",
@@ -5796,7 +6002,7 @@
5796
6002
  },
5797
6003
  "CustomerOwnedSlackAppManifest": {
5798
6004
  "type": "object",
5799
- "description": "The exact configuration a customer-owned Slack app is created from, as data rather than as a URL-encoded blob.\n\nIt exists so the owner can read what they are about to approve, and so a reviewer can check it, without opening Slack. It carries configuration only: no account, project, or environment, no credential, and no provider-native identifier. The opaque handle inside the request URLs selects candidate setup state and authorizes nothing — a request still has to carry Slack's signature over the untouched body before anything inside it is believed.",
6005
+ "description": "The exact configuration a customer-owned Slack app is created from, as data rather than as a URL-encoded blob.\n\nIt exists so the owner can read what they are about to approve, and so a reviewer can check it, without opening Slack. It carries configuration only: no organization, project, or environment, no credential, and no provider-native identifier. The opaque handle inside the request URLs selects candidate setup state and authorizes nothing — a request still has to carry Slack's signature over the untouched body before anything inside it is believed.",
5800
6006
  "required": [
5801
6007
  "creation_url",
5802
6008
  "app_name",
@@ -6013,6 +6219,22 @@
6013
6219
  { "$ref": "#/$defs/TelegramConnectionIdentity" },
6014
6220
  { "$ref": "#/$defs/SlackConnectionIdentity" }
6015
6221
  ]
6222
+ },
6223
+ "PrivacyMode": {
6224
+ "type": "string",
6225
+ "enum": ["minimal", "standard", "private"],
6226
+ "description": "Project profile collection policy. minimal (the default for new and existing projects) keeps recipient language only; standard permits messenger profile data; private retains delivery routing and the customer-supplied subscriber identifier, with no profile data. Lowering collection erases existing fields the new mode disallows."
6227
+ },
6228
+ "UpdateProjectRequest": {
6229
+ "type": "object",
6230
+ "description": "Sets the project privacy policy. Moving from standard to minimal erases stored identifying profile data; moving to private also erases recipient language. Increasing collection does not restore erased data.",
6231
+ "required": ["privacy_mode"],
6232
+ "properties": {
6233
+ "privacy_mode": {
6234
+ "$ref": "#/$defs/PrivacyMode"
6235
+ }
6236
+ },
6237
+ "additionalProperties": false
6016
6238
  }
6017
6239
  }
6018
6240
  }