@whooshbang/contracts 2.0.0 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (92) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/contract-manifest.json +32 -32
  3. package/dist/generated/artifact-index.d.ts +33 -33
  4. package/dist/generated/artifact-index.js +32 -32
  5. package/dist/generated/validators-standalone.d.ts +577 -447
  6. package/dist/generated/validators-standalone.js +7241 -1096
  7. package/dist/generated/validators-standalone.js.map +1 -1
  8. package/dist/generated/validators.d.ts +11 -1
  9. package/dist/generated/validators.js +11 -1
  10. package/dist/generated/validators.js.map +1 -1
  11. package/dist/generated/whooshbang-v1.d.ts +139 -1
  12. package/dist/index.d.ts +2 -2
  13. package/dist/index.js +1 -1
  14. package/dist/index.js.map +1 -1
  15. package/fixtures/core-v1/fixture-index.json +1 -1
  16. package/fixtures/core-v1/scenarios/api-credential-invalid-indistinguishable.json +1 -1
  17. package/fixtures/core-v1/scenarios/api-credential-one-time-reveal.json +1 -1
  18. package/fixtures/core-v1/scenarios/api-credential-revocation-replay.json +1 -1
  19. package/fixtures/core-v1/scenarios/api-credential-rotation-overlap.json +1 -1
  20. package/fixtures/core-v1/scenarios/cancellation-too-late.json +1 -1
  21. package/fixtures/core-v1/scenarios/cross-organization-project.json +1 -1
  22. package/fixtures/core-v1/scenarios/cross-project-resource.json +1 -1
  23. package/fixtures/core-v1/scenarios/environment-mismatch.json +1 -1
  24. package/fixtures/core-v1/scenarios/idempotency-conflict.json +1 -1
  25. package/fixtures/core-v1/scenarios/idempotency-equivalent-replay.json +1 -1
  26. package/fixtures/core-v1/scenarios/malformed-bearer.json +1 -1
  27. package/fixtures/core-v1/scenarios/metadata-overrun.json +1 -1
  28. package/fixtures/core-v1/scenarios/missing-scope.json +1 -1
  29. package/fixtures/core-v1/scenarios/project-create-equivalent-replay.json +1 -1
  30. package/fixtures/core-v1/scenarios/project-slug-conflict.json +1 -1
  31. package/fixtures/core-v1/scenarios/provider-outcome-unknown.json +1 -1
  32. package/fixtures/core-v1/scenarios/subscription-activation-replay.json +1 -1
  33. package/fixtures/core-v1/scenarios/subscription-authorization-capability-opacity.json +1 -1
  34. package/fixtures/core-v1/scenarios/subscription-cross-connection-callback-rejected.json +1 -1
  35. package/fixtures/core-v1/scenarios/subscription-expired-replay.json +1 -1
  36. package/fixtures/core-v1/scenarios/subscription-selected-connection-replay.json +1 -1
  37. package/fixtures/interaction-machine-v1/scenarios/ack-conflict.json +1 -1
  38. package/fixtures/interaction-machine-v1/scenarios/ack-cross-machine.json +1 -1
  39. package/fixtures/interaction-machine-v1/scenarios/ack-out-of-order.json +1 -1
  40. package/fixtures/interaction-machine-v1/scenarios/ack-replay.json +1 -1
  41. package/fixtures/interaction-machine-v1/scenarios/ambiguous-free-text.json +1 -1
  42. package/fixtures/interaction-machine-v1/scenarios/callback-token-opacity.json +1 -1
  43. package/fixtures/interaction-machine-v1/scenarios/credential-registration-conflict.json +1 -1
  44. package/fixtures/interaction-machine-v1/scenarios/credential-registration-replay.json +1 -1
  45. package/fixtures/interaction-machine-v1/scenarios/credential-revocation-replay.json +1 -1
  46. package/fixtures/interaction-machine-v1/scenarios/credential-rotation.json +1 -1
  47. package/fixtures/interaction-machine-v1/scenarios/first-credential-activates.json +1 -1
  48. package/fixtures/interaction-machine-v1/scenarios/interaction-duplicate.json +1 -1
  49. package/fixtures/interaction-machine-v1/scenarios/interaction-expired.json +1 -1
  50. package/fixtures/interaction-machine-v1/scenarios/interaction-mismatch.json +1 -1
  51. package/fixtures/interaction-machine-v1/scenarios/interaction-retired.json +1 -1
  52. package/fixtures/interaction-machine-v1/scenarios/interaction-stale.json +1 -1
  53. package/fixtures/interaction-machine-v1/scenarios/machine-admin-denied.json +1 -1
  54. package/fixtures/interaction-machine-v1/scenarios/machine-bound-message-create.json +1 -1
  55. package/fixtures/interaction-machine-v1/scenarios/machine-cancel-denied.json +1 -1
  56. package/fixtures/interaction-machine-v1/scenarios/machine-crash-before-ack.json +1 -1
  57. package/fixtures/interaction-machine-v1/scenarios/machine-notifier-mismatch.json +1 -1
  58. package/fixtures/interaction-machine-v1/scenarios/machine-other-message-read.json +1 -1
  59. package/fixtures/interaction-machine-v1/scenarios/machine-owned-message-read.json +1 -1
  60. package/fixtures/interaction-machine-v1/scenarios/machine-subscriber-mismatch.json +1 -1
  61. package/fixtures/interaction-machine-v1/scenarios/poll-uncommitted-cursor-rejected.json +1 -1
  62. package/fixtures/interaction-machine-v1/scenarios/project-credential-machine-endpoint-denied.json +1 -1
  63. package/fixtures/interaction-machine-v1/scenarios/quarantine-integrity-rejected.json +1 -1
  64. package/fixtures/interaction-machine-v1/scenarios/quarantine-signature-rejected.json +1 -1
  65. package/fixtures/interaction-machine-v1/scenarios/quarantine-valid.json +1 -1
  66. package/fixtures/interaction-machine-v1/scenarios/revoked-machine-credential.json +1 -1
  67. package/fixtures/recipient-experience-v1/scenarios/membership-broadcast-snapshot.json +1 -1
  68. package/fixtures/recipient-experience-v1/scenarios/preferred-binding-resolution.json +1 -1
  69. package/fixtures/recipient-experience-v1/scenarios/recipient-session-security.json +1 -1
  70. package/fixtures/recipient-experience-v1/scenarios/subscriber-initial-groups-transaction.json +1 -1
  71. package/openapi/generated/whooshbang-v1.bundle.json +1359 -771
  72. package/openapi/whooshbang-v1.yaml +291 -2
  73. package/package.json +1 -1
  74. package/schemas/core-scenario.schema.json +1 -1
  75. package/schemas/events/agent-binding-degraded-v1.schema.json +1 -1
  76. package/schemas/events/interaction-expired-v1.schema.json +1 -1
  77. package/schemas/events/interaction-received-v1.schema.json +1 -1
  78. package/schemas/events/message-delivered-v1.schema.json +1 -1
  79. package/schemas/events/message-expired-v1.schema.json +1 -1
  80. package/schemas/events/message-failed-v1.schema.json +1 -1
  81. package/schemas/events/message-provider-accepted-v1.schema.json +1 -1
  82. package/schemas/events/subscription-activated-v1.schema.json +1 -1
  83. package/schemas/events/subscription-revoked-v1.schema.json +1 -1
  84. package/schemas/events/whooshbang-event-v1.schema.json +1 -1
  85. package/schemas/fixture-index.schema.json +1 -1
  86. package/schemas/interaction-machine-scenario.schema.json +1 -1
  87. package/schemas/interaction-v1.schema.json +1 -1
  88. package/schemas/machine-v1.schema.json +1 -1
  89. package/schemas/provider-capabilities-v1.schema.json +1 -1
  90. package/schemas/webhooks/webhook-delivery-case-v1.schema.json +1 -1
  91. package/schemas/webhooks/webhook-signing-vector-v1.schema.json +1 -1
  92. package/schemas/whooshbang-v1.schema.json +391 -1
@@ -2,7 +2,7 @@
2
2
  "openapi": "3.1.1",
3
3
  "info": {
4
4
  "title": "WhooshBang API",
5
- "version": "2.0.0",
5
+ "version": "2.2.0",
6
6
  "description": "The V1 contract for human organization/project administration, project-scoped\nsubscription links, and asynchronous text messages. A Clerk session selects\none organization for administration. A project credential selects one organization,\nproject, and `test` or `live` environment for product operations; request\nbodies cannot override either authenticated scope.\n\nA person's OAuth grant selects one organization and carries scopes, but no\nproject. It names the project and environment it acts in through the URL\npath, on the environment-scoped form of each operation; the service checks\nthat path against the granting person's membership on every call. The flat\nforms remain credential-only and are unchanged.\n\nResource identifiers are opaque and are not authorization. Reads outside the\ncredential scope return the same `404` shape as absent resources. Provider\ncredentials, raw Telegram identity, and protected provider responses are\nnever part of this API.\n",
7
7
  "termsOfService": "https://flowxo.com/terms",
8
8
  "contact": {
@@ -84,12 +84,350 @@
84
84
  "name": "Subscriber groups",
85
85
  "description": "Environment-scoped static groups and monotonic membership generations."
86
86
  },
87
+ {
88
+ "name": "Operators",
89
+ "description": "Current teammates, personal notification preferences and membership-derived service email."
90
+ },
87
91
  {
88
92
  "name": "Group broadcasts",
89
93
  "description": "Durable group acceptance and aggregate projection over ordinary child messages."
90
94
  }
91
95
  ],
92
96
  "paths": {
97
+ "/v1/projects/{project_id}/environments/{environment_id}/whoami": {
98
+ "get": {
99
+ "operationId": "whoAmI",
100
+ "summary": "Identify the authenticated caller",
101
+ "description": "Personal login identifies me without a recipient question. Shared or service credentials report their actual principal and require an explicit recipient. No account owner or credential creator is inferred.",
102
+ "tags": ["Operators"],
103
+ "security": [
104
+ { "clerkSession": [] },
105
+ { "bearerCredential": [] },
106
+ { "bearerOrganizationCredential": [] },
107
+ { "bearerGrant": ["messages:read"] },
108
+ { "machineCredential": [] }
109
+ ],
110
+ "x-required-scopes": ["messages:read"],
111
+ "parameters": [
112
+ { "$ref": "#/components/parameters/ProjectId" },
113
+ { "$ref": "#/components/parameters/EnvironmentId" }
114
+ ],
115
+ "responses": {
116
+ "200": {
117
+ "description": "Actual principal and safe authorized messaging context.",
118
+ "content": {
119
+ "application/json": {
120
+ "schema": { "$ref": "#/components/schemas/WhoAmI" }
121
+ }
122
+ }
123
+ },
124
+ "401": { "$ref": "#/components/responses/Unauthorized" },
125
+ "403": { "$ref": "#/components/responses/Forbidden" }
126
+ }
127
+ }
128
+ },
129
+ "/v1/projects/{project_id}/environments/{environment_id}/operators": {
130
+ "get": {
131
+ "operationId": "listOperators",
132
+ "summary": "listOperators",
133
+ "description": "Membership-derived operator messaging. Only the authenticated human can manage personal preferences.",
134
+ "tags": ["Operators"],
135
+ "security": [
136
+ { "clerkSession": [] },
137
+ { "bearerCredential": [] },
138
+ { "bearerGrant": ["messages:read"] }
139
+ ],
140
+ "x-required-scopes": ["messages:read"],
141
+ "parameters": [
142
+ { "$ref": "#/components/parameters/ProjectId" },
143
+ { "$ref": "#/components/parameters/EnvironmentId" },
144
+ { "$ref": "#/components/parameters/ListAfterCursor" },
145
+ { "$ref": "#/components/parameters/ListPageLimit" },
146
+ { "$ref": "#/components/parameters/PageNumber" }
147
+ ],
148
+ "responses": {
149
+ "200": {
150
+ "description": "Authorized result.",
151
+ "content": {
152
+ "application/json": {
153
+ "schema": { "$ref": "#/components/schemas/OperatorCollection" }
154
+ }
155
+ }
156
+ },
157
+ "401": { "$ref": "#/components/responses/Unauthorized" },
158
+ "403": { "$ref": "#/components/responses/Forbidden" }
159
+ }
160
+ }
161
+ },
162
+ "/v1/projects/{project_id}/environments/{environment_id}/operators/resolve": {
163
+ "get": {
164
+ "operationId": "resolveOperator",
165
+ "summary": "resolveOperator",
166
+ "description": "Membership-derived operator messaging. Only the authenticated human can manage personal preferences.",
167
+ "tags": ["Operators"],
168
+ "security": [
169
+ { "clerkSession": [] },
170
+ { "bearerCredential": [] },
171
+ { "bearerGrant": ["messages:read"] }
172
+ ],
173
+ "x-required-scopes": ["messages:read"],
174
+ "parameters": [
175
+ { "$ref": "#/components/parameters/ProjectId" },
176
+ { "$ref": "#/components/parameters/EnvironmentId" },
177
+ {
178
+ "name": "recipient",
179
+ "in": "query",
180
+ "required": true,
181
+ "schema": { "type": "string", "minLength": 1, "maxLength": 128 }
182
+ }
183
+ ],
184
+ "responses": {
185
+ "200": {
186
+ "description": "Authorized result.",
187
+ "content": {
188
+ "application/json": {
189
+ "schema": { "$ref": "#/components/schemas/OperatorResolution" }
190
+ }
191
+ }
192
+ },
193
+ "401": { "$ref": "#/components/responses/Unauthorized" },
194
+ "403": { "$ref": "#/components/responses/Forbidden" }
195
+ }
196
+ }
197
+ },
198
+ "/v1/projects/{project_id}/environments/{environment_id}/operator-messages": {
199
+ "post": {
200
+ "operationId": "sendOperatorMessage",
201
+ "summary": "sendOperatorMessage",
202
+ "description": "Message a current teammate by name or operator ID. Personal callers may omit recipient or use me. Shared credentials must name a recipient. Runtime machine credentials report recipient_required for omitted or me; named operator sends are forbidden because runtime authority remains subscriber-bound.",
203
+ "tags": ["Operators"],
204
+ "security": [
205
+ { "clerkSession": [] },
206
+ { "bearerCredential": [] },
207
+ { "bearerOrganizationCredential": [] },
208
+ { "machineCredential": [] },
209
+ { "bearerGrant": ["messages:write"] }
210
+ ],
211
+ "x-required-scopes": ["messages:write"],
212
+ "parameters": [
213
+ { "$ref": "#/components/parameters/ProjectId" },
214
+ { "$ref": "#/components/parameters/EnvironmentId" },
215
+ { "$ref": "#/components/parameters/IdempotencyKey" }
216
+ ],
217
+ "requestBody": {
218
+ "required": true,
219
+ "content": {
220
+ "application/json": {
221
+ "schema": {
222
+ "$ref": "#/components/schemas/CreateOperatorMessageRequest"
223
+ }
224
+ }
225
+ }
226
+ },
227
+ "responses": {
228
+ "200": {
229
+ "description": "Authorized result.",
230
+ "content": {
231
+ "application/json": {
232
+ "schema": {
233
+ "$ref": "#/components/schemas/OperatorMessageResult"
234
+ }
235
+ }
236
+ }
237
+ },
238
+ "401": { "$ref": "#/components/responses/Unauthorized" },
239
+ "403": { "$ref": "#/components/responses/Forbidden" }
240
+ }
241
+ }
242
+ },
243
+ "/v1/projects/{project_id}/environments/{environment_id}/operators/broadcasts": {
244
+ "post": {
245
+ "operationId": "broadcastOperators",
246
+ "summary": "broadcastOperators",
247
+ "description": "Membership-derived operator messaging. Only the authenticated human can manage personal preferences.",
248
+ "tags": ["Operators"],
249
+ "security": [
250
+ { "clerkSession": [] },
251
+ { "bearerCredential": [] },
252
+ { "bearerGrant": ["messages:write"] }
253
+ ],
254
+ "x-required-scopes": ["messages:write"],
255
+ "parameters": [
256
+ { "$ref": "#/components/parameters/ProjectId" },
257
+ { "$ref": "#/components/parameters/EnvironmentId" },
258
+ { "$ref": "#/components/parameters/IdempotencyKey" }
259
+ ],
260
+ "requestBody": {
261
+ "required": true,
262
+ "content": {
263
+ "application/json": {
264
+ "schema": {
265
+ "$ref": "#/components/schemas/CreateGroupBroadcastRequest"
266
+ }
267
+ }
268
+ }
269
+ },
270
+ "responses": {
271
+ "200": {
272
+ "description": "Authorized result.",
273
+ "content": {
274
+ "application/json": {
275
+ "schema": { "$ref": "#/components/schemas/OperatorBroadcast" }
276
+ }
277
+ }
278
+ },
279
+ "401": { "$ref": "#/components/responses/Unauthorized" },
280
+ "403": { "$ref": "#/components/responses/Forbidden" }
281
+ }
282
+ }
283
+ },
284
+ "/v1/projects/{project_id}/environments/{environment_id}/operators/broadcasts/{broadcast_id}": {
285
+ "get": {
286
+ "operationId": "getOperatorBroadcast",
287
+ "summary": "getOperatorBroadcast",
288
+ "description": "Membership-derived operator messaging. Only the authenticated human can manage personal preferences.",
289
+ "tags": ["Operators"],
290
+ "security": [
291
+ { "clerkSession": [] },
292
+ { "bearerCredential": [] },
293
+ { "bearerGrant": ["messages:read"] }
294
+ ],
295
+ "x-required-scopes": ["messages:read"],
296
+ "parameters": [
297
+ { "$ref": "#/components/parameters/ProjectId" },
298
+ { "$ref": "#/components/parameters/EnvironmentId" },
299
+ {
300
+ "name": "broadcast_id",
301
+ "in": "path",
302
+ "required": true,
303
+ "schema": { "type": "string" }
304
+ }
305
+ ],
306
+ "responses": {
307
+ "200": {
308
+ "description": "Authorized result.",
309
+ "content": {
310
+ "application/json": {
311
+ "schema": { "$ref": "#/components/schemas/OperatorBroadcast" }
312
+ }
313
+ }
314
+ },
315
+ "401": { "$ref": "#/components/responses/Unauthorized" },
316
+ "403": { "$ref": "#/components/responses/Forbidden" }
317
+ }
318
+ }
319
+ },
320
+ "/v1/projects/{project_id}/environments/{environment_id}/notification-preferences": {
321
+ "get": {
322
+ "operationId": "getOperatorPreferences",
323
+ "summary": "getOperatorPreferences",
324
+ "description": "Membership-derived operator messaging. Only the authenticated human can manage personal preferences.",
325
+ "tags": ["Operators"],
326
+ "security": [
327
+ { "clerkSession": [] },
328
+ { "bearerCredential": [] },
329
+ { "bearerGrant": ["messages:read"] }
330
+ ],
331
+ "x-required-scopes": ["messages:read"],
332
+ "parameters": [
333
+ { "$ref": "#/components/parameters/ProjectId" },
334
+ { "$ref": "#/components/parameters/EnvironmentId" }
335
+ ],
336
+ "responses": {
337
+ "200": {
338
+ "description": "Authorized result.",
339
+ "content": {
340
+ "application/json": {
341
+ "schema": { "$ref": "#/components/schemas/OperatorPreferences" }
342
+ }
343
+ }
344
+ },
345
+ "401": { "$ref": "#/components/responses/Unauthorized" },
346
+ "403": { "$ref": "#/components/responses/Forbidden" }
347
+ }
348
+ }
349
+ },
350
+ "/v1/projects/{project_id}/environments/{environment_id}/notification-preferences/preferred-binding": {
351
+ "put": {
352
+ "operationId": "setOperatorPreferredBinding",
353
+ "summary": "setOperatorPreferredBinding",
354
+ "description": "Membership-derived operator messaging. Only the authenticated human can manage personal preferences.",
355
+ "tags": ["Operators"],
356
+ "security": [
357
+ { "clerkSession": [] },
358
+ { "bearerCredential": [] },
359
+ { "bearerGrant": ["messages:write"] }
360
+ ],
361
+ "x-required-scopes": ["messages:write"],
362
+ "parameters": [
363
+ { "$ref": "#/components/parameters/ProjectId" },
364
+ { "$ref": "#/components/parameters/EnvironmentId" }
365
+ ],
366
+ "requestBody": {
367
+ "required": true,
368
+ "content": {
369
+ "application/json": {
370
+ "schema": {
371
+ "$ref": "#/components/schemas/SetPreferredBindingRequest"
372
+ }
373
+ }
374
+ }
375
+ },
376
+ "responses": {
377
+ "200": {
378
+ "description": "Authorized result.",
379
+ "content": {
380
+ "application/json": {
381
+ "schema": { "$ref": "#/components/schemas/OperatorPreferences" }
382
+ }
383
+ }
384
+ },
385
+ "401": { "$ref": "#/components/responses/Unauthorized" },
386
+ "403": { "$ref": "#/components/responses/Forbidden" }
387
+ }
388
+ }
389
+ },
390
+ "/v1/projects/{project_id}/environments/{environment_id}/notification-preferences/connect": {
391
+ "post": {
392
+ "operationId": "connectOperatorChannel",
393
+ "summary": "connectOperatorChannel",
394
+ "description": "Membership-derived operator messaging. Only the authenticated human can manage personal preferences.",
395
+ "tags": ["Operators"],
396
+ "security": [
397
+ { "clerkSession": [] },
398
+ { "bearerCredential": [] },
399
+ { "bearerGrant": ["messages:write"] }
400
+ ],
401
+ "x-required-scopes": ["messages:write"],
402
+ "parameters": [
403
+ { "$ref": "#/components/parameters/ProjectId" },
404
+ { "$ref": "#/components/parameters/EnvironmentId" },
405
+ { "$ref": "#/components/parameters/IdempotencyKey" }
406
+ ],
407
+ "requestBody": {
408
+ "required": true,
409
+ "content": {
410
+ "application/json": {
411
+ "schema": {
412
+ "$ref": "#/components/schemas/OperatorConnectRequest"
413
+ }
414
+ }
415
+ }
416
+ },
417
+ "responses": {
418
+ "201": {
419
+ "description": "Authorized result.",
420
+ "content": {
421
+ "application/json": {
422
+ "schema": { "$ref": "#/components/schemas/SubscriptionLink" }
423
+ }
424
+ }
425
+ },
426
+ "401": { "$ref": "#/components/responses/Unauthorized" },
427
+ "403": { "$ref": "#/components/responses/Forbidden" }
428
+ }
429
+ }
430
+ },
93
431
  "/v1/billing/project-slots": {
94
432
  "get": {
95
433
  "operationId": "getBillingProjectSlots",
@@ -10716,25 +11054,275 @@
10716
11054
  }
10717
11055
  },
10718
11056
  "schemas": {
10719
- "ListPageSize": {
10720
- "type": "integer",
10721
- "minimum": 1,
10722
- "maximum": 100,
10723
- "description": "The page size that produced `items`, echoed so a caller that sent no `limit` can still divide `total` into pages without knowing the server default."
10724
- },
10725
- "MessageState": {
10726
- "type": "string",
10727
- "enum": [
10728
- "accepted",
10729
- "dispatch_pending",
10730
- "queued",
10731
- "sending",
10732
- "provider_accepted",
10733
- "retry_wait",
10734
- "terminal_failed",
10735
- "outcome_unknown",
10736
- "cancelled",
10737
- "expired"
11057
+ "WhoAmI": {
11058
+ "type": "object",
11059
+ "description": "Current principal, authorized context, safe channel readiness and deterministic personal me identity. Contains no email or provider destination.",
11060
+ "required": ["principal", "context", "capabilities", "detail"],
11061
+ "properties": {
11062
+ "principal": {
11063
+ "type": "object",
11064
+ "description": "The credential actually authenticated for this request; service identity never implies its creator is the recipient.",
11065
+ "required": ["type", "id"],
11066
+ "properties": {
11067
+ "type": {
11068
+ "enum": [
11069
+ "personal_session",
11070
+ "personal_mcp",
11071
+ "project_credential",
11072
+ "organization_credential",
11073
+ "machine_client"
11074
+ ]
11075
+ },
11076
+ "id": { "type": "string", "minLength": 1, "maxLength": 128 }
11077
+ },
11078
+ "additionalProperties": false
11079
+ },
11080
+ "context": {
11081
+ "type": "object",
11082
+ "description": "Explicit authorized account and project context. No context is guessed across tenants.",
11083
+ "required": [
11084
+ "organization_id",
11085
+ "project_id",
11086
+ "environment_id",
11087
+ "environment"
11088
+ ],
11089
+ "properties": {
11090
+ "organization_id": {
11091
+ "type": "string",
11092
+ "minLength": 1,
11093
+ "maxLength": 128
11094
+ },
11095
+ "project_id": {
11096
+ "type": "string",
11097
+ "minLength": 1,
11098
+ "maxLength": 128
11099
+ },
11100
+ "environment_id": {
11101
+ "type": "string",
11102
+ "minLength": 1,
11103
+ "maxLength": 128
11104
+ },
11105
+ "environment": { "enum": ["test", "live"] }
11106
+ },
11107
+ "additionalProperties": false
11108
+ },
11109
+ "capabilities": {
11110
+ "type": "object",
11111
+ "description": "Current messaging permissions; quota, binding readiness and provider gates still apply per send.",
11112
+ "required": [
11113
+ "can_message_self",
11114
+ "can_message_teammates",
11115
+ "can_broadcast_operators"
11116
+ ],
11117
+ "properties": {
11118
+ "can_message_self": { "type": "boolean" },
11119
+ "can_message_teammates": { "type": "boolean" },
11120
+ "can_broadcast_operators": { "type": "boolean" }
11121
+ },
11122
+ "additionalProperties": false
11123
+ },
11124
+ "user": {
11125
+ "type": "object",
11126
+ "description": "Verified authenticated human identity. Absent for shared/service credentials.",
11127
+ "required": ["id", "name"],
11128
+ "properties": {
11129
+ "id": { "type": "string", "minLength": 1, "maxLength": 128 },
11130
+ "name": { "type": "string", "minLength": 1, "maxLength": 256 }
11131
+ },
11132
+ "additionalProperties": false
11133
+ },
11134
+ "me": { "$ref": "#/components/schemas/Operator" },
11135
+ "detail": { "type": "string", "minLength": 1, "maxLength": 1024 }
11136
+ },
11137
+ "additionalProperties": false
11138
+ },
11139
+ "Operator": {
11140
+ "type": "object",
11141
+ "description": "A safe, membership-derived operator in the authorized project. No email, provider identifiers or credentials are exposed.",
11142
+ "required": ["id", "name", "role", "readiness", "preferences_url"],
11143
+ "properties": {
11144
+ "id": { "type": "string", "minLength": 1, "maxLength": 128 },
11145
+ "name": { "type": "string", "minLength": 1, "maxLength": 256 },
11146
+ "role": { "enum": ["admin", "member", "messenger"] },
11147
+ "readiness": { "enum": ["ready", "channel_unavailable"] },
11148
+ "preferred_channel": { "$ref": "#/components/schemas/Channel" },
11149
+ "preferences_url": { "type": "string", "format": "uri" },
11150
+ "setup_reason": {
11151
+ "type": "string",
11152
+ "minLength": 1,
11153
+ "maxLength": 512,
11154
+ "description": "An actionable platform setup reason when the default shared email setup is missing, ambiguous or unavailable."
11155
+ }
11156
+ },
11157
+ "additionalProperties": false
11158
+ },
11159
+ "OperatorCollection": {
11160
+ "type": "object",
11161
+ "description": "Read-only predefined operators audience, filtered to current account and project membership.",
11162
+ "required": ["group", "items", "total"],
11163
+ "properties": {
11164
+ "group": { "const": "operators" },
11165
+ "items": {
11166
+ "type": "array",
11167
+ "items": { "$ref": "#/components/schemas/Operator" }
11168
+ },
11169
+ "total": { "$ref": "#/components/schemas/ListTotal" },
11170
+ "next_cursor": { "type": "string", "minLength": 1, "maxLength": 128 },
11171
+ "page": { "$ref": "#/components/schemas/ListPageNumber" },
11172
+ "page_size": { "$ref": "#/components/schemas/ListPageSize" }
11173
+ },
11174
+ "additionalProperties": false
11175
+ },
11176
+ "OperatorResolution": {
11177
+ "type": "object",
11178
+ "description": "Exact normalized name or stable operator identity. Duplicate names are never guessed.",
11179
+ "required": ["outcome", "choices"],
11180
+ "properties": {
11181
+ "outcome": { "enum": ["resolved", "ambiguous", "not_found"] },
11182
+ "choices": {
11183
+ "type": "array",
11184
+ "items": { "$ref": "#/components/schemas/Operator" }
11185
+ }
11186
+ },
11187
+ "additionalProperties": false
11188
+ },
11189
+ "CreateOperatorMessageRequest": {
11190
+ "type": "object",
11191
+ "description": "Send to a teammate by exact name or operator ID, or use me. Omitted recipient means the verified authenticated human only. Shared and service credentials must name a recipient. Preferred channel, service-email billing and marketing restrictions still apply.",
11192
+ "required": ["content"],
11193
+ "properties": {
11194
+ "recipient": { "type": "string", "minLength": 1, "maxLength": 128 },
11195
+ "content": { "$ref": "#/components/schemas/MessageContent" },
11196
+ "expires_at": {
11197
+ "$ref": "#/components/schemas/Timestamp",
11198
+ "description": "Optional server-authoritative expiry no more than 24 hours after acceptance."
11199
+ },
11200
+ "correlation_id": {
11201
+ "type": "string",
11202
+ "minLength": 1,
11203
+ "maxLength": 128
11204
+ },
11205
+ "metadata": { "$ref": "#/components/schemas/Metadata" },
11206
+ "interaction": { "$ref": "#/components/schemas/InteractionRequest" },
11207
+ "purpose": {
11208
+ "enum": ["service", "marketing"],
11209
+ "default": "service",
11210
+ "description": "Omitted means service. Marketing refuses service-only bindings before charging and at every delayed dispatch."
11211
+ }
11212
+ },
11213
+ "additionalProperties": false
11214
+ },
11215
+ "OperatorMessageResult": {
11216
+ "type": "object",
11217
+ "description": "Readable acceptance or refusal. Accepted means queued, never proof of provider delivery.",
11218
+ "required": ["outcome", "choices", "detail"],
11219
+ "properties": {
11220
+ "outcome": {
11221
+ "enum": [
11222
+ "accepted",
11223
+ "ambiguous",
11224
+ "not_found",
11225
+ "recipient_required",
11226
+ "channel_unavailable"
11227
+ ]
11228
+ },
11229
+ "choices": {
11230
+ "type": "array",
11231
+ "items": { "$ref": "#/components/schemas/Operator" }
11232
+ },
11233
+ "detail": { "type": "string", "minLength": 1, "maxLength": 1024 },
11234
+ "message": { "$ref": "#/components/schemas/Message" },
11235
+ "preferences_url": { "type": "string", "format": "uri" }
11236
+ },
11237
+ "additionalProperties": false
11238
+ },
11239
+ "OperatorBroadcastRecipient": {
11240
+ "type": "object",
11241
+ "description": "Per-person broadcast result. Sent requires provider acceptance; skipped includes removed membership or an unavailable destination.",
11242
+ "required": ["operator_id", "outcome"],
11243
+ "properties": {
11244
+ "operator_id": { "type": "string", "minLength": 1, "maxLength": 128 },
11245
+ "name": { "type": "string" },
11246
+ "outcome": { "enum": ["pending", "sent", "skipped", "failed"] },
11247
+ "message_id": { "type": "string", "minLength": 1, "maxLength": 128 },
11248
+ "reason": { "type": "string" }
11249
+ },
11250
+ "additionalProperties": false
11251
+ },
11252
+ "OperatorBroadcast": {
11253
+ "type": "object",
11254
+ "description": "Durable operators broadcast using ordinary child messages and charging. Read again for provider outcomes.",
11255
+ "required": ["broadcast", "recipients"],
11256
+ "properties": {
11257
+ "broadcast": { "$ref": "#/components/schemas/GroupBroadcast" },
11258
+ "recipients": {
11259
+ "type": "array",
11260
+ "items": {
11261
+ "$ref": "#/components/schemas/OperatorBroadcastRecipient"
11262
+ }
11263
+ }
11264
+ },
11265
+ "additionalProperties": false
11266
+ },
11267
+ "OperatorPreferences": {
11268
+ "type": "object",
11269
+ "description": "The authenticated person’s own destinations and preferred channel in this project. Shared credentials cannot manage a human’s preferences.",
11270
+ "required": ["operator", "settings", "connections"],
11271
+ "properties": {
11272
+ "operator": { "$ref": "#/components/schemas/Operator" },
11273
+ "settings": {
11274
+ "$ref": "#/components/schemas/SubscriberChannelSettings"
11275
+ },
11276
+ "connections": {
11277
+ "type": "array",
11278
+ "items": {
11279
+ "type": "object",
11280
+ "description": "An available personal connection route.",
11281
+ "required": ["id", "channel", "name"],
11282
+ "properties": {
11283
+ "id": { "type": "string", "minLength": 1, "maxLength": 128 },
11284
+ "channel": { "$ref": "#/components/schemas/Channel" },
11285
+ "name": { "type": "string" }
11286
+ },
11287
+ "additionalProperties": false
11288
+ }
11289
+ }
11290
+ },
11291
+ "additionalProperties": false
11292
+ },
11293
+ "OperatorConnectRequest": {
11294
+ "type": "object",
11295
+ "description": "Connect the signed-in person’s own destination through a configured channel. This never changes another person’s channels.",
11296
+ "required": ["connection_id", "channel"],
11297
+ "properties": {
11298
+ "connection_id": {
11299
+ "type": "string",
11300
+ "minLength": 1,
11301
+ "maxLength": 128
11302
+ },
11303
+ "channel": { "enum": ["telegram", "slack"] }
11304
+ },
11305
+ "additionalProperties": false
11306
+ },
11307
+ "ListPageSize": {
11308
+ "type": "integer",
11309
+ "minimum": 1,
11310
+ "maximum": 100,
11311
+ "description": "The page size that produced `items`, echoed so a caller that sent no `limit` can still divide `total` into pages without knowing the server default."
11312
+ },
11313
+ "MessageState": {
11314
+ "type": "string",
11315
+ "enum": [
11316
+ "accepted",
11317
+ "dispatch_pending",
11318
+ "queued",
11319
+ "sending",
11320
+ "provider_accepted",
11321
+ "retry_wait",
11322
+ "terminal_failed",
11323
+ "outcome_unknown",
11324
+ "cancelled",
11325
+ "expired"
10738
11326
  ]
10739
11327
  },
10740
11328
  "Timestamp": {
@@ -17647,6 +18235,11 @@
17647
18235
  },
17648
18236
  "additionalProperties": false
17649
18237
  },
18238
+ "Channel": {
18239
+ "type": "string",
18240
+ "enum": ["telegram", "slack", "email"],
18241
+ "description": "A provider-neutral delivery channel. Adding a member makes it representable; it does not claim that every deployment has registered an executable adapter."
18242
+ },
17650
18243
  "ProblemCode": {
17651
18244
  "type": "string",
17652
18245
  "enum": [
@@ -17854,409 +18447,499 @@
17854
18447
  }
17855
18448
  ]
17856
18449
  },
17857
- "Channel": {
17858
- "type": "string",
17859
- "enum": ["telegram", "slack", "email"],
17860
- "description": "A provider-neutral delivery channel. Adding a member makes it representable; it does not claim that every deployment has registered an executable adapter."
17861
- },
17862
18450
  "ListTotal": {
17863
18451
  "type": "integer",
17864
18452
  "minimum": 0,
17865
18453
  "description": "How many records the list holds in total, not only on this page. Counted while the page was read, so a write landing between the count and the page can leave it a record or two out of step with `items`. That is noise on an inspection surface; it never changes which records a page addresses."
17866
18454
  },
17867
- "EmailAddress": {
17868
- "type": "string",
17869
- "minLength": 3,
17870
- "maxLength": 254,
17871
- "pattern": "^(?=[^@]{1,64}@)(?!\\.)(?![^@]*\\.@)(?![^@]*\\.\\.)[A-Za-z0-9!#$%&'*+/=?^_`{|}~.-]+@(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\\.)+[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$",
17872
- "description": "Exactly one mailbox: an unquoted ASCII dot-atom local part plus a canonical lower-case IDNA domain. The comparison key preserves local-part bytes and case and lower-cases only the domain; dot, plus-tag, alias and provider equivalence are never inferred."
17873
- },
17874
- "OrganizationRole": {
17875
- "type": "string",
17876
- "enum": ["admin", "member", "messenger"],
17877
- "description": "The person's WhooshBang role in the organization. `admin` does everything, including billing, API keys and teammates. `member` runs the projects they were given: channels, subscribers, messages and project settings. `messenger` sends messages and reads replies in the projects they were given. Clients must tolerate roles added later."
17878
- },
17879
- "OrganizationMembershipSummary": {
18455
+ "StructuredTextSection": {
17880
18456
  "type": "object",
17881
- "required": ["role"],
18457
+ "required": ["text"],
17882
18458
  "properties": {
17883
- "role": { "$ref": "#/components/schemas/OrganizationRole" }
18459
+ "heading": {
18460
+ "description": "An optional semantic heading for this section.",
18461
+ "type": "string",
18462
+ "minLength": 1,
18463
+ "maxLength": 200,
18464
+ "x-max-utf8-bytes": 800
18465
+ },
18466
+ "text": {
18467
+ "description": "The section body, also represented in the complete fallback text.",
18468
+ "type": "string",
18469
+ "minLength": 1,
18470
+ "maxLength": 1000,
18471
+ "x-max-utf8-bytes": 4000
18472
+ }
17884
18473
  },
17885
18474
  "additionalProperties": false
17886
18475
  },
17887
- "OrganizationClosureProviderAction": {
18476
+ "DocumentSectionBlock": {
18477
+ "description": "One prose section in an ordered portable document.",
17888
18478
  "type": "object",
17889
- "description": "One provider-owned resource the customer must retire outside WhooshBang after organization closure. It contains safe identifiers only, never provider credentials.",
17890
- "required": ["provider", "action", "connection_id"],
18479
+ "required": ["type", "text"],
17891
18480
  "properties": {
17892
- "provider": {
17893
- "$ref": "#/components/schemas/ChannelConnectionProvider"
17894
- },
17895
- "action": {
18481
+ "type": { "const": "section" },
18482
+ "heading": {
18483
+ "description": "An optional semantic heading for this section.",
17896
18484
  "type": "string",
17897
- "enum": ["revoke_bot_token", "delete_provider_application"]
18485
+ "minLength": 1,
18486
+ "maxLength": 200,
18487
+ "x-max-utf8-bytes": 800
17898
18488
  },
17899
- "connection_id": { "$ref": "#/components/schemas/OpaqueIdentifier" }
18489
+ "text": {
18490
+ "description": "The section prose.",
18491
+ "type": "string",
18492
+ "minLength": 1,
18493
+ "maxLength": 1000,
18494
+ "x-max-utf8-bytes": 4000
18495
+ }
17900
18496
  },
17901
18497
  "additionalProperties": false
17902
18498
  },
17903
- "TeammateStatus": {
17904
- "type": "string",
17905
- "enum": ["active", "invited"],
17906
- "description": "`active` has joined. `invited` has an invitation they have not accepted."
17907
- },
17908
- "TeammateProjectAccess": {
18499
+ "DocumentFact": {
17909
18500
  "type": "object",
17910
- "description": "Which projects the teammate can see. An admin sees every project, including ones created later.",
17911
- "required": ["all", "project_ids"],
18501
+ "required": ["label", "value"],
17912
18502
  "properties": {
17913
- "all": { "type": "boolean" },
17914
- "project_ids": {
17915
- "type": "array",
17916
- "maxItems": 1000,
17917
- "uniqueItems": true,
17918
- "items": { "$ref": "#/components/schemas/OpaqueIdentifier" },
17919
- "description": "The projects given to this teammate. Empty when `all` is true."
17920
- }
17921
- },
17922
- "additionalProperties": false
17923
- },
17924
- "ProviderInstallationStatus": {
17925
- "type": "string",
17926
- "enum": [
17927
- "pending",
17928
- "active",
17929
- "reauthorization_required",
17930
- "revoked",
17931
- "invalid"
17932
- ],
17933
- "description": "The safe customer-facing lifecycle of a provider installation. Credential and provider-native state stay private."
18503
+ "label": {
18504
+ "description": "The semantic label for this fact.",
18505
+ "type": "string",
18506
+ "minLength": 1,
18507
+ "maxLength": 200,
18508
+ "x-max-utf8-bytes": 800
18509
+ },
18510
+ "value": {
18511
+ "description": "The semantic value for this fact.",
18512
+ "type": "string",
18513
+ "minLength": 1,
18514
+ "maxLength": 1000,
18515
+ "x-max-utf8-bytes": 4000
18516
+ }
18517
+ },
18518
+ "additionalProperties": false
17934
18519
  },
17935
- "ProviderAuthorizationSetup": {
17936
- "description": "A credential-free provider authorization handoff for one exact connection. The URL is short-lived and safe to render, but never contains a provider credential, team identifier or raw authorization code; actual OAuth state and callback handling remain service-private.",
17937
- "oneOf": [
17938
- {
17939
- "type": "object",
17940
- "required": ["status", "authorization_url", "expires_at"],
17941
- "properties": {
17942
- "status": { "const": "awaiting_authorization" },
17943
- "authorization_url": {
17944
- "type": "string",
17945
- "format": "uri",
17946
- "pattern": "^https://[^\\s]+$",
17947
- "maxLength": 2048,
17948
- "description": "The short-lived HTTPS authorization handoff for this connection. It carries opaque state, not provider credentials or a raw authorization code."
17949
- },
17950
- "expires_at": { "$ref": "#/components/schemas/Timestamp" }
17951
- },
17952
- "additionalProperties": false
18520
+ "DocumentFactsBlock": {
18521
+ "description": "One ordered group of semantic label/value facts.",
18522
+ "type": "object",
18523
+ "required": ["type", "items"],
18524
+ "properties": {
18525
+ "type": { "const": "facts" },
18526
+ "items": {
18527
+ "type": "array",
18528
+ "minItems": 1,
18529
+ "maxItems": 24,
18530
+ "items": { "$ref": "#/components/schemas/DocumentFact" }
18531
+ }
18532
+ },
18533
+ "additionalProperties": false
18534
+ },
18535
+ "DocumentTableCell": {
18536
+ "type": "string",
18537
+ "minLength": 1,
18538
+ "maxLength": 500,
18539
+ "x-max-utf8-bytes": 2000
18540
+ },
18541
+ "DocumentTableRow": {
18542
+ "type": "array",
18543
+ "minItems": 2,
18544
+ "maxItems": 8,
18545
+ "items": { "$ref": "#/components/schemas/DocumentTableCell" }
18546
+ },
18547
+ "DocumentTableBlock": {
18548
+ "description": "One small semantic table. Every row must have exactly one cell for each column.",
18549
+ "type": "object",
18550
+ "required": ["type", "heading", "columns", "rows"],
18551
+ "properties": {
18552
+ "type": { "const": "table" },
18553
+ "heading": {
18554
+ "description": "The semantic heading for this table.",
18555
+ "type": "string",
18556
+ "minLength": 1,
18557
+ "maxLength": 200,
18558
+ "x-max-utf8-bytes": 800
17953
18559
  },
17954
- {
17955
- "type": "object",
17956
- "required": ["status", "expires_at"],
17957
- "properties": {
17958
- "status": { "const": "provisioning" },
17959
- "expires_at": { "$ref": "#/components/schemas/Timestamp" }
17960
- },
17961
- "additionalProperties": false
18560
+ "columns": {
18561
+ "description": "The ordered column headings.",
18562
+ "type": "array",
18563
+ "minItems": 2,
18564
+ "maxItems": 8,
18565
+ "items": { "$ref": "#/components/schemas/DocumentTableCell" }
17962
18566
  },
17963
- {
17964
- "type": "object",
17965
- "required": ["status", "expires_at", "failure"],
17966
- "properties": {
17967
- "status": { "const": "failed" },
17968
- "expires_at": { "$ref": "#/components/schemas/Timestamp" },
17969
- "failure": { "$ref": "#/components/schemas/ProviderFailure" }
17970
- },
17971
- "additionalProperties": false
18567
+ "rows": {
18568
+ "description": "The ordered table rows.",
18569
+ "type": "array",
18570
+ "minItems": 1,
18571
+ "maxItems": 20,
18572
+ "items": { "$ref": "#/components/schemas/DocumentTableRow" }
18573
+ }
18574
+ },
18575
+ "x-rows-match-columns": true,
18576
+ "additionalProperties": false
18577
+ },
18578
+ "DocumentDetailsBlock": {
18579
+ "description": "One supporting disclosure in an ordered portable document.",
18580
+ "type": "object",
18581
+ "required": ["type", "summary", "text"],
18582
+ "properties": {
18583
+ "type": { "const": "details" },
18584
+ "summary": {
18585
+ "description": "The always-visible semantic summary.",
18586
+ "type": "string",
18587
+ "minLength": 1,
18588
+ "maxLength": 200,
18589
+ "x-max-utf8-bytes": 800
17972
18590
  },
17973
- {
17974
- "type": "object",
17975
- "required": ["status", "expires_at"],
17976
- "properties": {
17977
- "status": { "const": "expired" },
17978
- "expires_at": { "$ref": "#/components/schemas/Timestamp" }
17979
- },
17980
- "additionalProperties": false
18591
+ "text": {
18592
+ "description": "The supporting prose.",
18593
+ "type": "string",
18594
+ "minLength": 1,
18595
+ "maxLength": 2000,
18596
+ "x-max-utf8-bytes": 8000
17981
18597
  }
18598
+ },
18599
+ "additionalProperties": false
18600
+ },
18601
+ "DocumentBlock": {
18602
+ "oneOf": [
18603
+ { "$ref": "#/components/schemas/DocumentSectionBlock" },
18604
+ { "$ref": "#/components/schemas/DocumentFactsBlock" },
18605
+ { "$ref": "#/components/schemas/DocumentTableBlock" },
18606
+ { "$ref": "#/components/schemas/DocumentDetailsBlock" }
17982
18607
  ]
17983
18608
  },
17984
- "EmailDomainName": {
18609
+ "DocumentContent": {
18610
+ "description": "An authoritative ordered portable notification document that every delivery adapter renders faithfully.",
18611
+ "type": "object",
18612
+ "required": ["type", "blocks"],
18613
+ "properties": {
18614
+ "type": { "const": "document" },
18615
+ "title": {
18616
+ "description": "An optional semantic document title. The adapter chooses its native presentation.",
18617
+ "type": "string",
18618
+ "minLength": 1,
18619
+ "maxLength": 200,
18620
+ "x-max-utf8-bytes": 800
18621
+ },
18622
+ "blocks": {
18623
+ "description": "The complete portable document in exact authored order.",
18624
+ "type": "array",
18625
+ "minItems": 1,
18626
+ "maxItems": 24,
18627
+ "items": { "$ref": "#/components/schemas/DocumentBlock" }
18628
+ },
18629
+ "image": { "$ref": "#/components/schemas/NotificationImage" }
18630
+ },
18631
+ "additionalProperties": false
18632
+ },
18633
+ "MessageContent": {
18634
+ "oneOf": [
18635
+ { "$ref": "#/components/schemas/TextContent" },
18636
+ { "$ref": "#/components/schemas/DocumentContent" }
18637
+ ]
18638
+ },
18639
+ "MetadataValue": {
18640
+ "oneOf": [
18641
+ { "type": "string" },
18642
+ { "type": "number" },
18643
+ { "type": "boolean" },
18644
+ { "type": "null" }
18645
+ ]
18646
+ },
18647
+ "Metadata": {
18648
+ "type": "object",
18649
+ "description": "Bounded customer correlation data. Do not include secrets or personal data.",
18650
+ "maxProperties": 20,
18651
+ "propertyNames": { "minLength": 1, "maxLength": 64 },
18652
+ "additionalProperties": {
18653
+ "$ref": "#/components/schemas/MetadataValue"
18654
+ },
18655
+ "x-max-json-bytes": 4096
18656
+ },
18657
+ "InteractionPrompt": {
17985
18658
  "type": "string",
17986
- "minLength": 4,
17987
- "maxLength": 253,
17988
- "pattern": "^(?=.{1,253}$)(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\\.){2,}[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$",
17989
- "description": "A canonical lower-case IDNA ASCII customer sending domain with its trailing dot removed. It must be a dedicated subdomain below the registrable domain; runtime validation uses the public suffix list so a registrable root such as example.co.uk is still rejected. URLs, ports, wildcards and mailbox addresses are invalid."
18659
+ "minLength": 1,
18660
+ "maxLength": 1000
17990
18661
  },
17991
- "EmailDomainLifecycle": {
18662
+ "Timestamp-2": {
17992
18663
  "type": "string",
17993
- "enum": [
17994
- "requested",
17995
- "creating",
17996
- "dns_required",
17997
- "verifying",
17998
- "testing",
17999
- "active",
18000
- "provider_action_required",
18001
- "dns_failed",
18002
- "oauth_required",
18003
- "held",
18004
- "deleting",
18005
- "deleted"
18006
- ],
18007
- "description": "The provider-neutral domain lifecycle. Sending and receiving capabilities are reported separately because they verify on different clocks."
18664
+ "format": "date-time",
18665
+ "pattern": "^[0-9]{4}-(?:0[1-9]|1[0-2])-(?:0[1-9]|[12][0-9]|3[01])T(?:[01][0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9](?:\\.[0-9]+)?(?:Z|[+-](?:[01][0-9]|2[0-3]):[0-5][0-9])$"
18008
18666
  },
18009
- "EmailDomainCapabilityState": {
18667
+ "InteractionCorrelationId": {
18010
18668
  "type": "string",
18011
- "enum": ["pending", "verified", "failed", "unsupported"]
18669
+ "minLength": 8,
18670
+ "maxLength": 128,
18671
+ "pattern": "^[^\\u0000-\\u001F\\u007F]+$",
18672
+ "description": "Caller-owned opaque correlation returned as data, never treated as execution authority."
18012
18673
  },
18013
- "EmailDomainCapabilities": {
18674
+ "ConfirmInteractionRequest": {
18675
+ "type": "object",
18676
+ "required": ["type", "prompt", "expires_at"],
18677
+ "properties": {
18678
+ "type": { "const": "confirm" },
18679
+ "prompt": { "$ref": "#/components/schemas/InteractionPrompt" },
18680
+ "confirm_label": {
18681
+ "type": "string",
18682
+ "minLength": 1,
18683
+ "maxLength": 40
18684
+ },
18685
+ "deny_label": { "type": "string", "minLength": 1, "maxLength": 40 },
18686
+ "expires_at": {
18687
+ "$ref": "#/components/schemas/Timestamp-2",
18688
+ "description": "Must be 30 seconds through 24 hours after server acceptance."
18689
+ },
18690
+ "correlation_id": {
18691
+ "$ref": "#/components/schemas/InteractionCorrelationId"
18692
+ }
18693
+ },
18694
+ "additionalProperties": false
18695
+ },
18696
+ "InteractionOption": {
18697
+ "type": "object",
18698
+ "required": ["value", "label"],
18699
+ "properties": {
18700
+ "value": {
18701
+ "type": "string",
18702
+ "minLength": 1,
18703
+ "maxLength": 128,
18704
+ "pattern": "^[^\\u0000-\\u001F\\u007F]+$",
18705
+ "description": "Opaque customer data. It is never interpreted as a command."
18706
+ },
18707
+ "label": { "type": "string", "minLength": 1, "maxLength": 120 },
18708
+ "intent": {
18709
+ "type": "string",
18710
+ "enum": ["accept", "destructive", "informational", "neutral"],
18711
+ "default": "neutral",
18712
+ "description": "What choosing this option does, so a channel can present it accordingly. Describes consequence rather than appearance: a channel renders `accept` green where it can, `destructive` red, `informational` prominent, and `neutral` plainly. A channel that cannot colour a control ignores this and the option still renders. Omitted means `neutral`."
18713
+ }
18714
+ },
18715
+ "additionalProperties": false
18716
+ },
18717
+ "SelectInteractionRequest": {
18718
+ "type": "object",
18719
+ "required": ["type", "prompt", "options", "expires_at"],
18720
+ "properties": {
18721
+ "type": { "const": "select" },
18722
+ "prompt": { "$ref": "#/components/schemas/InteractionPrompt" },
18723
+ "options": {
18724
+ "type": "array",
18725
+ "minItems": 2,
18726
+ "maxItems": 6,
18727
+ "items": { "$ref": "#/components/schemas/InteractionOption" },
18728
+ "x-unique-property": "value"
18729
+ },
18730
+ "expires_at": {
18731
+ "$ref": "#/components/schemas/Timestamp-2",
18732
+ "description": "Must be 30 seconds through 24 hours after server acceptance."
18733
+ },
18734
+ "correlation_id": {
18735
+ "$ref": "#/components/schemas/InteractionCorrelationId"
18736
+ }
18737
+ },
18738
+ "additionalProperties": false
18739
+ },
18740
+ "InputInteractionRequest": {
18741
+ "type": "object",
18742
+ "required": ["type", "prompt", "expires_at"],
18743
+ "properties": {
18744
+ "type": { "const": "input" },
18745
+ "prompt": { "$ref": "#/components/schemas/InteractionPrompt" },
18746
+ "expires_at": {
18747
+ "$ref": "#/components/schemas/Timestamp-2",
18748
+ "description": "Must be 30 seconds through 24 hours after server acceptance."
18749
+ },
18750
+ "correlation_id": {
18751
+ "$ref": "#/components/schemas/InteractionCorrelationId"
18752
+ }
18753
+ },
18754
+ "additionalProperties": false
18755
+ },
18756
+ "HighestProvenProviderState": {
18757
+ "type": "string",
18758
+ "enum": ["none", "provider_accepted"],
18759
+ "description": "The strongest provider assertion the service can prove; it is not an end-device delivery receipt."
18760
+ },
18761
+ "ClassifiedError": {
18762
+ "type": "object",
18763
+ "description": "A stable, safe error classification with no provider response or credential material.",
18764
+ "required": ["code", "retryable"],
18765
+ "properties": {
18766
+ "code": { "$ref": "#/components/schemas/ProblemCode" },
18767
+ "retryable": { "type": "boolean" },
18768
+ "detail": { "type": "string", "minLength": 1, "maxLength": 1024 },
18769
+ "diagnostic_id": { "$ref": "#/components/schemas/DiagnosticId" }
18770
+ },
18771
+ "additionalProperties": false
18772
+ },
18773
+ "Environment": {
18774
+ "type": "string",
18775
+ "enum": ["test", "live"],
18776
+ "description": "The single environment selected by the authenticated credential."
18777
+ },
18778
+ "EmailAddress": {
18779
+ "type": "string",
18780
+ "minLength": 3,
18781
+ "maxLength": 254,
18782
+ "pattern": "^(?=[^@]{1,64}@)(?!\\.)(?![^@]*\\.@)(?![^@]*\\.\\.)[A-Za-z0-9!#$%&'*+/=?^_`{|}~.-]+@(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\\.)+[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$",
18783
+ "description": "Exactly one mailbox: an unquoted ASCII dot-atom local part plus a canonical lower-case IDNA domain. The comparison key preserves local-part bytes and case and lower-cases only the domain; dot, plus-tag, alias and provider equivalence are never inferred."
18784
+ },
18785
+ "BroadcastMessageProvenance": {
18786
+ "type": "object",
18787
+ "description": "Provenance carried by an ordinary message created during group expansion. The broadcast, message subscriber_id, and exact historical membership generation form the child uniqueness boundary.",
18788
+ "required": ["broadcast_id", "group_id", "membership_generation"],
18789
+ "properties": {
18790
+ "broadcast_id": { "$ref": "#/components/schemas/OpaqueIdentifier" },
18791
+ "group_id": { "$ref": "#/components/schemas/OpaqueIdentifier" },
18792
+ "membership_generation": { "type": "integer", "minimum": 1 }
18793
+ },
18794
+ "additionalProperties": false
18795
+ },
18796
+ "MessageDeliveryTarget": {
18797
+ "type": "string",
18798
+ "enum": ["connection", "simulator"],
18799
+ "description": "The effective provider boundary selected for a message. Connection delivery uses the exact authorized environment connection; simulator delivery never calls a provider."
18800
+ },
18801
+ "TrimmedInputAnswer": {
18802
+ "type": "string",
18803
+ "minLength": 1,
18804
+ "maxLength": 4000,
18805
+ "pattern": "^\\S(?:[\\s\\S]*\\S)?$",
18806
+ "description": "Untrusted input text after leading and trailing whitespace is removed."
18807
+ },
18808
+ "OpaqueIdentifier-2": {
18809
+ "type": "string",
18810
+ "minLength": 1,
18811
+ "maxLength": 128,
18812
+ "pattern": "^[^\\u0000-\\u001F\\u007F]+$",
18813
+ "description": "An opaque identifier whose internal encoding is not a public promise."
18814
+ },
18815
+ "InteractionState": {
18816
+ "type": "string",
18817
+ "enum": ["open", "answered", "expired", "cancelled", "retired"]
18818
+ },
18819
+ "DeveloperKey": {
18820
+ "type": "string",
18821
+ "minLength": 1,
18822
+ "maxLength": 63,
18823
+ "pattern": "^[a-z0-9]+(?:-[a-z0-9]+)*$",
18824
+ "description": "A stable, lowercase developer key used by registered return destinations and subscriber groups."
18825
+ },
18826
+ "BroadcastAggregate": {
18014
18827
  "type": "object",
18828
+ "description": "A projection over expansion and ordinary child-message states, not an independent delivery state machine.",
18015
18829
  "required": [
18016
- "sending",
18017
- "receiving",
18018
- "delivery",
18019
- "link_interactions",
18020
- "free_text_reply"
18830
+ "audience",
18831
+ "expanded",
18832
+ "pending",
18833
+ "provider_accepted",
18834
+ "delivered",
18835
+ "skipped",
18836
+ "failed",
18837
+ "cancelled",
18838
+ "expired"
18021
18839
  ],
18022
18840
  "properties": {
18023
- "sending": {
18024
- "$ref": "#/components/schemas/EmailDomainCapabilityState"
18025
- },
18026
- "receiving": {
18027
- "$ref": "#/components/schemas/EmailDomainCapabilityState"
18028
- },
18029
- "delivery": { "type": "boolean" },
18030
- "link_interactions": { "type": "boolean" },
18031
- "free_text_reply": {
18032
- "type": "boolean",
18033
- "description": "False when free-text replies cannot be lowered: receiving may not be verified yet, or the provider team may have message-content storage disabled. Delivery and link-based interaction remain independently available; no undocumented eligibility or storage-state endpoint is assumed."
18034
- }
18841
+ "audience": { "type": "integer", "minimum": 0 },
18842
+ "expanded": { "type": "integer", "minimum": 0 },
18843
+ "pending": { "type": "integer", "minimum": 0 },
18844
+ "provider_accepted": { "type": "integer", "minimum": 0 },
18845
+ "delivered": { "type": "integer", "minimum": 0 },
18846
+ "skipped": { "type": "integer", "minimum": 0 },
18847
+ "failed": { "type": "integer", "minimum": 0 },
18848
+ "cancelled": { "type": "integer", "minimum": 0 },
18849
+ "expired": { "type": "integer", "minimum": 0 }
18035
18850
  },
18036
18851
  "additionalProperties": false
18037
18852
  },
18038
- "EmailDnsRecord": {
18039
- "description": "One exact neutral DNS record. `name` is preserved exactly as the provider returns it and is relative to the registrable domain (for example `send.notify`, not `send.notify.example.com`).",
18853
+ "EmailBindingConsent": {
18854
+ "description": "How consent for this exact subscriber, notifier and connection was established. A customer assertion never becomes recipient-observed verification. Complaint, unsubscribe and suppression override either arm.",
18040
18855
  "oneOf": [
18041
18856
  {
18042
18857
  "type": "object",
18043
- "required": ["type", "name", "value", "priority", "status"],
18858
+ "required": ["kind", "observed_at"],
18044
18859
  "properties": {
18045
- "type": { "const": "MX" },
18046
- "name": {
18047
- "type": "string",
18048
- "minLength": 1,
18049
- "maxLength": 253,
18050
- "pattern": "^[^\u0000-\u001f-Ÿ

]+$"
18051
- },
18052
- "value": {
18053
- "type": "string",
18054
- "minLength": 1,
18055
- "maxLength": 2048,
18056
- "pattern": "^[^\u0000-\u001f-Ÿ

]+$"
18057
- },
18058
- "priority": { "type": "integer", "minimum": 0, "maximum": 65535 },
18059
- "status": {
18060
- "enum": [
18061
- "not_started",
18062
- "pending",
18063
- "verified",
18064
- "failed",
18065
- "temporary_failure"
18066
- ]
18067
- }
18860
+ "kind": { "const": "recipient_observed" },
18861
+ "observed_at": { "$ref": "#/components/schemas/Timestamp" }
18068
18862
  },
18069
18863
  "additionalProperties": false
18070
18864
  },
18071
18865
  {
18072
18866
  "type": "object",
18073
- "required": ["type", "name", "value", "status"],
18867
+ "required": ["kind", "consented_at", "source"],
18074
18868
  "properties": {
18075
- "type": { "enum": ["TXT", "CNAME"] },
18076
- "name": {
18869
+ "kind": { "const": "asserted_prior_consent" },
18870
+ "consented_at": { "$ref": "#/components/schemas/Timestamp" },
18871
+ "source": {
18077
18872
  "type": "string",
18078
18873
  "minLength": 1,
18079
- "maxLength": 253,
18080
- "pattern": "^[^\u0000-\u001f-Ÿ

]+$"
18874
+ "maxLength": 100,
18875
+ "pattern": "^[^\u0000-\u001f-Ÿ

]+$",
18876
+ "description": "A bounded customer description of where consent was collected. The authenticated asserting actor is recorded by the service, never accepted from this object."
18081
18877
  },
18082
- "value": {
18083
- "type": "string",
18084
- "minLength": 1,
18085
- "maxLength": 2048,
18086
- "pattern": "^[^\u0000-\u001f-Ÿ

]+$"
18878
+ "asserted_at": { "$ref": "#/components/schemas/Timestamp" },
18879
+ "asserted_by": {
18880
+ "type": "object",
18881
+ "required": ["kind", "id"],
18882
+ "properties": "^[^\u0000-\u001f-Ÿ

]+$"{
18883
+ "kind": { "enum": ["credential", "grant", "session"] },
18884
+ "id": { "$ref": "#/components/schemas/OpaqueIdentifier" }
18885
+ },
18886
+ "additionalProperties": false
18087
18887
  },
18088
- "status": {
18089
- "enum": [
18090
- "not_started",
18091
- "pending",
18092
- "verified",
18093
- "failed",
18094
- "temporary_failure"
18095
- ]
18096
- }
18888
+ "domain_id": { "$ref": "#/components/schemas/OpaqueIdentifier" }
18097
18889
  },
18098
18890
  "additionalProperties": false
18099
18891
  }
18100
18892
  ]
18101
18893
  },
18102
- "EmailDomainGuide": {
18894
+ "RecipientChannelSummary": {
18103
18895
  "type": "object",
18104
- "required": ["state"],
18896
+ "description": "Safe state for one configured channel. Provider identities, credentials, callback state, and other subscribers are absent.",
18897
+ "required": ["channel", "state", "preferred"],
18105
18898
  "properties": {
18899
+ "channel": { "$ref": "#/components/schemas/Channel" },
18106
18900
  "state": {
18107
- "enum": [
18108
- "detecting",
18109
- "guided",
18110
- "domain_connect_available",
18111
- "complete",
18112
- "unavailable"
18113
- ]
18114
- },
18115
- "dns_provider": {
18116
18901
  "type": "string",
18117
- "minLength": 1,
18118
- "maxLength": 100,
18119
- "pattern": "^[^\u0000-\u001f-Ÿ

]+$"
18902
+ "enum": ["available", "connected", "paused"]
18120
18903
  },
18121
- "instructions_url": {
18904
+ "binding_id": {
18905
+ "$ref": "#/components/schemas/OpaqueIdentifier",
18906
+ "description": "The exact binding owned by this session's subscriber. Present only for connected or paused state."
18907
+ },
18908
+ "preferred": { "type": "boolean" },
18909
+ "connection_id": { "$ref": "#/components/schemas/OpaqueIdentifier" },
18910
+ "masked_address": {
18122
18911
  "type": "string",
18123
- "format": "uri",
18124
- "pattern": "^https://[^\\s]+$",
18125
- "maxLength": 2048,
18126
- "description": "A short-lived secret-free instruction link for a DNS administrator. It contains no WhooshBang session or provider OAuth material."
18127
- }
18128
- },
18129
- "allOf": [
18130
- {
18131
- "if": {
18132
- "properties": {
18133
- "state": {
18134
- "enum": ["guided", "domain_connect_available", "complete"]
18135
- }
18136
- },
18137
- "required": ["state"]
18138
- },
18139
- "then": {
18140
- "properties": { "dns_provider": true },
18141
- "required": ["dns_provider"]
18142
- },
18143
- "else": { "properties": { "dns_provider": false } }
18144
- }
18145
- ],
18146
- "additionalProperties": false
18147
- },
18148
- "EmailSubjectPolicy": {
18149
- "type": "object",
18150
- "description": "The fixed subject rendering contract. Notification title wins; otherwise localized copy equivalent to `Notification from {notifier display name}` is used. Whitespace is normalized and the final value is capped at 200 Unicode code points.",
18151
- "required": ["fallback", "maximum_code_points", "normalize_whitespace"],
18152
- "properties": {
18153
- "fallback": { "const": "localized_notifier_name" },
18154
- "maximum_code_points": { "const": 200 },
18155
- "normalize_whitespace": { "const": true }
18156
- },
18157
- "additionalProperties": false
18158
- },
18159
- "EmailDeliveryPolicy": {
18160
- "type": "object",
18161
- "description": "Privacy-safe email defaults. Return-Path and Message-ID remain provider-owned, arbitrary headers are unsupported, and every ordinary message carries a visible management route plus safe one-click unsubscribe semantics when custom headers are available. A GET never mutates consent; the matching POST resolves the exact binding.",
18162
- "required": [
18163
- "open_tracking",
18164
- "click_tracking",
18165
- "tls",
18166
- "unsubscribe",
18167
- "subject"
18168
- ],
18169
- "properties": {
18170
- "open_tracking": { "const": false },
18171
- "click_tracking": { "const": false },
18172
- "tls": {
18173
- "enum": ["opportunistic", "enforced"],
18174
- "default": "opportunistic"
18912
+ "minLength": 3,
18913
+ "maxLength": 254,
18914
+ "description": "Masked recipient address. Never the full mailbox."
18175
18915
  },
18176
- "unsubscribe": { "const": "visible_and_one_click_where_supported" },
18177
- "subject": { "$ref": "#/components/schemas/EmailSubjectPolicy" }
18178
- },
18179
- "additionalProperties": false
18180
- },
18181
- "OrganizationCredentialPrefix": {
18182
- "type": "string",
18183
- "pattern": "^wb_oc1\\.ocred_[A-Za-z0-9_-]{8}$",
18184
- "description": "A safe display prefix containing the organization-credential format marker and first eight base64url identity characters, never secret bytes."
18185
- },
18186
- "OrganizationCredentialScope": {
18187
- "type": "string",
18188
- "enum": [
18189
- "organization:read",
18190
- "projects:write",
18191
- "projects:read",
18192
- "messages:write",
18193
- "messages:read",
18194
- "subscriptions:write",
18195
- "subscriptions:read",
18196
- "machine-clients:write",
18197
- "machine-clients:read",
18198
- "capabilities:read",
18199
- "connections:write",
18200
- "connections:read"
18201
- ],
18202
- "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."
18203
- },
18204
- "OrganizationCredentialScopeSet": {
18205
- "type": "array",
18206
- "minItems": 1,
18207
- "maxItems": 12,
18208
- "uniqueItems": true,
18209
- "items": { "$ref": "#/components/schemas/OrganizationCredentialScope" }
18210
- },
18211
- "OrganizationCredentialSecret": {
18212
- "type": "string",
18213
- "pattern": "^wb_oc1\\.ocred_[A-Za-z0-9_-]{21}[AQgw]\\.[A-Za-z0-9_-]{42}[AEIMQUYcgkosw048]$",
18214
- "description": "The exact one-time runtime bearer syntax. The final segment is canonical unpadded base64url for exactly 32 random secret bytes."
18215
- },
18216
- "Environment": {
18217
- "type": "string",
18218
- "enum": ["test", "live"],
18219
- "description": "The single environment selected by the authenticated credential."
18220
- },
18221
- "RecipientCopyOverrideValue": {
18222
- "type": "string",
18223
- "minLength": 1,
18224
- "maxLength": 500,
18225
- "description": "Customer-authored plain text with optional plain template placeholders. The service enforces key-specific length and rejects invisible controls. Rendering escapes text; changing labels does not change recipient control actions."
18226
- },
18227
- "ApiCredentialPrefix": {
18228
- "type": "string",
18229
- "pattern": "^wb_pc1\\.pcred_[A-Za-z0-9_-]{8}$",
18230
- "description": "A safe display prefix containing the credential format marker and first eight base64url identity characters, never secret bytes."
18231
- },
18232
- "ApiCredentialScope": {
18233
- "type": "string",
18234
- "enum": [
18235
- "projects:write",
18236
- "projects:read",
18237
- "messages:write",
18238
- "messages:read",
18239
- "subscriptions:write",
18240
- "subscriptions:read",
18241
- "machine-clients:write",
18242
- "machine-clients:read",
18243
- "capabilities:read",
18244
- "connections:write",
18245
- "connections:read"
18916
+ "consent": { "$ref": "#/components/schemas/EmailBindingConsent" }
18917
+ },
18918
+ "allOf": [
18919
+ {
18920
+ "if": {
18921
+ "properties": { "state": { "enum": ["connected", "paused"] } },
18922
+ "required": ["state"]
18923
+ },
18924
+ "then": {
18925
+ "required": ["binding_id"],
18926
+ "properties": { "binding_id": {} }
18927
+ }
18928
+ },
18929
+ {
18930
+ "if": {
18931
+ "properties": { "state": { "const": "available" } },
18932
+ "required": ["state"]
18933
+ },
18934
+ "then": {
18935
+ "properties": {
18936
+ "binding_id": false,
18937
+ "preferred": { "const": false }
18938
+ }
18939
+ }
18940
+ }
18246
18941
  ],
18247
- "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."
18248
- },
18249
- "ApiCredentialScopeSet": {
18250
- "type": "array",
18251
- "minItems": 1,
18252
- "maxItems": 11,
18253
- "uniqueItems": true,
18254
- "items": { "$ref": "#/components/schemas/ApiCredentialScope" }
18255
- },
18256
- "ApiCredentialSecret": {
18257
- "type": "string",
18258
- "pattern": "^wb_pc1\\.pcred_[A-Za-z0-9_-]{21}[AQgw]\\.[A-Za-z0-9_-]{42}[AEIMQUYcgkosw048]$",
18259
- "description": "The exact one-time runtime bearer syntax. The final segment is canonical unpadded base64url for exactly 32 random secret bytes."
18942
+ "additionalProperties": false
18260
18943
  },
18261
18944
  "ConnectionIdentity": {
18262
18945
  "type": "object",
@@ -18290,18 +18973,6 @@
18290
18973
  ],
18291
18974
  "additionalProperties": false
18292
18975
  },
18293
- "ClassifiedError": {
18294
- "type": "object",
18295
- "description": "A stable, safe error classification with no provider response or credential material.",
18296
- "required": ["code", "retryable"],
18297
- "properties": {
18298
- "code": { "$ref": "#/components/schemas/ProblemCode" },
18299
- "retryable": { "type": "boolean" },
18300
- "detail": { "type": "string", "minLength": 1, "maxLength": 1024 },
18301
- "diagnostic_id": { "$ref": "#/components/schemas/DiagnosticId" }
18302
- },
18303
- "additionalProperties": false
18304
- },
18305
18976
  "EmailVerificationSummary": {
18306
18977
  "type": "object",
18307
18978
  "required": ["status", "masked_address", "expires_at"],
@@ -18317,353 +18988,395 @@
18317
18988
  },
18318
18989
  "additionalProperties": false
18319
18990
  },
18320
- "MessageTarget": {
18321
- "type": "object",
18322
- "required": ["subscriber_id"],
18323
- "properties": {
18324
- "subscriber_id": { "$ref": "#/components/schemas/OpaqueIdentifier" }
18325
- },
18326
- "additionalProperties": false
18327
- },
18328
- "MessageDeliveryTarget": {
18991
+ "OrganizationRole": {
18329
18992
  "type": "string",
18330
- "enum": ["connection", "simulator"],
18331
- "description": "The effective provider boundary selected for a message. Connection delivery uses the exact authorized environment connection; simulator delivery never calls a provider."
18993
+ "enum": ["admin", "member", "messenger"],
18994
+ "description": "The person's WhooshBang role in the organization. `admin` does everything, including billing, API keys and teammates. `member` runs the projects they were given: channels, subscribers, messages and project settings. `messenger` sends messages and reads replies in the projects they were given. Clients must tolerate roles added later."
18332
18995
  },
18333
- "StructuredTextSection": {
18996
+ "OrganizationMembershipSummary": {
18334
18997
  "type": "object",
18335
- "required": ["text"],
18998
+ "required": ["role"],
18336
18999
  "properties": {
18337
- "heading": {
18338
- "description": "An optional semantic heading for this section.",
18339
- "type": "string",
18340
- "minLength": 1,
18341
- "maxLength": 200,
18342
- "x-max-utf8-bytes": 800
18343
- },
18344
- "text": {
18345
- "description": "The section body, also represented in the complete fallback text.",
18346
- "type": "string",
18347
- "minLength": 1,
18348
- "maxLength": 1000,
18349
- "x-max-utf8-bytes": 4000
18350
- }
19000
+ "role": { "$ref": "#/components/schemas/OrganizationRole" }
18351
19001
  },
18352
19002
  "additionalProperties": false
18353
19003
  },
18354
- "DocumentSectionBlock": {
18355
- "description": "One prose section in an ordered portable document.",
19004
+ "OrganizationClosureProviderAction": {
18356
19005
  "type": "object",
18357
- "required": ["type", "text"],
19006
+ "description": "One provider-owned resource the customer must retire outside WhooshBang after organization closure. It contains safe identifiers only, never provider credentials.",
19007
+ "required": ["provider", "action", "connection_id"],
18358
19008
  "properties": {
18359
- "type": { "const": "section" },
18360
- "heading": {
18361
- "description": "An optional semantic heading for this section.",
18362
- "type": "string",
18363
- "minLength": 1,
18364
- "maxLength": 200,
18365
- "x-max-utf8-bytes": 800
19009
+ "provider": {
19010
+ "$ref": "#/components/schemas/ChannelConnectionProvider"
18366
19011
  },
18367
- "text": {
18368
- "description": "The section prose.",
18369
- "type": "string",
18370
- "minLength": 1,
18371
- "maxLength": 1000,
18372
- "x-max-utf8-bytes": 4000
18373
- }
18374
- },
18375
- "additionalProperties": false
18376
- },
18377
- "DocumentFact": {
18378
- "type": "object",
18379
- "required": ["label", "value"],
18380
- "properties": {
18381
- "label": {
18382
- "description": "The semantic label for this fact.",
19012
+ "action": {
18383
19013
  "type": "string",
18384
- "minLength": 1,
18385
- "maxLength": 200,
18386
- "x-max-utf8-bytes": 800
19014
+ "enum": ["revoke_bot_token", "delete_provider_application"]
18387
19015
  },
18388
- "value": {
18389
- "description": "The semantic value for this fact.",
18390
- "type": "string",
18391
- "minLength": 1,
18392
- "maxLength": 1000,
18393
- "x-max-utf8-bytes": 4000
18394
- }
18395
- },
18396
- "additionalProperties": false
18397
- },
18398
- "DocumentFactsBlock": {
18399
- "description": "One ordered group of semantic label/value facts.",
18400
- "type": "object",
18401
- "required": ["type", "items"],
18402
- "properties": {
18403
- "type": { "const": "facts" },
18404
- "items": {
18405
- "type": "array",
18406
- "minItems": 1,
18407
- "maxItems": 24,
18408
- "items": { "$ref": "#/components/schemas/DocumentFact" }
18409
- }
19016
+ "connection_id": { "$ref": "#/components/schemas/OpaqueIdentifier" }
18410
19017
  },
18411
19018
  "additionalProperties": false
18412
19019
  },
18413
- "DocumentTableCell": {
19020
+ "TeammateStatus": {
18414
19021
  "type": "string",
18415
- "minLength": 1,
18416
- "maxLength": 500,
18417
- "x-max-utf8-bytes": 2000
18418
- },
18419
- "DocumentTableRow": {
18420
- "type": "array",
18421
- "minItems": 2,
18422
- "maxItems": 8,
18423
- "items": { "$ref": "#/components/schemas/DocumentTableCell" }
19022
+ "enum": ["active", "invited"],
19023
+ "description": "`active` has joined. `invited` has an invitation they have not accepted."
18424
19024
  },
18425
- "DocumentTableBlock": {
18426
- "description": "One small semantic table. Every row must have exactly one cell for each column.",
19025
+ "TeammateProjectAccess": {
18427
19026
  "type": "object",
18428
- "required": ["type", "heading", "columns", "rows"],
19027
+ "description": "Which projects the teammate can see. An admin sees every project, including ones created later.",
19028
+ "required": ["all", "project_ids"],
18429
19029
  "properties": {
18430
- "type": { "const": "table" },
18431
- "heading": {
18432
- "description": "The semantic heading for this table.",
18433
- "type": "string",
18434
- "minLength": 1,
18435
- "maxLength": 200,
18436
- "x-max-utf8-bytes": 800
18437
- },
18438
- "columns": {
18439
- "description": "The ordered column headings.",
18440
- "type": "array",
18441
- "minItems": 2,
18442
- "maxItems": 8,
18443
- "items": { "$ref": "#/components/schemas/DocumentTableCell" }
18444
- },
18445
- "rows": {
18446
- "description": "The ordered table rows.",
19030
+ "all": { "type": "boolean" },
19031
+ "project_ids": {
18447
19032
  "type": "array",
18448
- "minItems": 1,
18449
- "maxItems": 20,
18450
- "items": { "$ref": "#/components/schemas/DocumentTableRow" }
19033
+ "maxItems": 1000,
19034
+ "uniqueItems": true,
19035
+ "items": { "$ref": "#/components/schemas/OpaqueIdentifier" },
19036
+ "description": "The projects given to this teammate. Empty when `all` is true."
18451
19037
  }
18452
19038
  },
18453
- "x-rows-match-columns": true,
18454
19039
  "additionalProperties": false
18455
19040
  },
18456
- "DocumentDetailsBlock": {
18457
- "description": "One supporting disclosure in an ordered portable document.",
18458
- "type": "object",
18459
- "required": ["type", "summary", "text"],
18460
- "properties": {
18461
- "type": { "const": "details" },
18462
- "summary": {
18463
- "description": "The always-visible semantic summary.",
18464
- "type": "string",
18465
- "minLength": 1,
18466
- "maxLength": 200,
18467
- "x-max-utf8-bytes": 800
18468
- },
18469
- "text": {
18470
- "description": "The supporting prose.",
18471
- "type": "string",
18472
- "minLength": 1,
18473
- "maxLength": 2000,
18474
- "x-max-utf8-bytes": 8000
18475
- }
18476
- },
18477
- "additionalProperties": false
19041
+ "ProviderInstallationStatus": {
19042
+ "type": "string",
19043
+ "enum": [
19044
+ "pending",
19045
+ "active",
19046
+ "reauthorization_required",
19047
+ "revoked",
19048
+ "invalid"
19049
+ ],
19050
+ "description": "The safe customer-facing lifecycle of a provider installation. Credential and provider-native state stay private."
18478
19051
  },
18479
- "DocumentBlock": {
19052
+ "ProviderAuthorizationSetup": {
19053
+ "description": "A credential-free provider authorization handoff for one exact connection. The URL is short-lived and safe to render, but never contains a provider credential, team identifier or raw authorization code; actual OAuth state and callback handling remain service-private.",
18480
19054
  "oneOf": [
18481
- { "$ref": "#/components/schemas/DocumentSectionBlock" },
18482
- { "$ref": "#/components/schemas/DocumentFactsBlock" },
18483
- { "$ref": "#/components/schemas/DocumentTableBlock" },
18484
- { "$ref": "#/components/schemas/DocumentDetailsBlock" }
18485
- ]
18486
- },
18487
- "DocumentContent": {
18488
- "description": "An authoritative ordered portable notification document that every delivery adapter renders faithfully.",
18489
- "type": "object",
18490
- "required": ["type", "blocks"],
18491
- "properties": {
18492
- "type": { "const": "document" },
18493
- "title": {
18494
- "description": "An optional semantic document title. The adapter chooses its native presentation.",
18495
- "type": "string",
18496
- "minLength": 1,
18497
- "maxLength": 200,
18498
- "x-max-utf8-bytes": 800
19055
+ {
19056
+ "type": "object",
19057
+ "required": ["status", "authorization_url", "expires_at"],
19058
+ "properties": {
19059
+ "status": { "const": "awaiting_authorization" },
19060
+ "authorization_url": {
19061
+ "type": "string",
19062
+ "format": "uri",
19063
+ "pattern": "^https://[^\\s]+$",
19064
+ "maxLength": 2048,
19065
+ "description": "The short-lived HTTPS authorization handoff for this connection. It carries opaque state, not provider credentials or a raw authorization code."
19066
+ },
19067
+ "expires_at": { "$ref": "#/components/schemas/Timestamp" }
19068
+ },
19069
+ "additionalProperties": false
18499
19070
  },
18500
- "blocks": {
18501
- "description": "The complete portable document in exact authored order.",
18502
- "type": "array",
18503
- "minItems": 1,
18504
- "maxItems": 24,
18505
- "items": { "$ref": "#/components/schemas/DocumentBlock" }
19071
+ {
19072
+ "type": "object",
19073
+ "required": ["status", "expires_at"],
19074
+ "properties": {
19075
+ "status": { "const": "provisioning" },
19076
+ "expires_at": { "$ref": "#/components/schemas/Timestamp" }
19077
+ },
19078
+ "additionalProperties": false
18506
19079
  },
18507
- "image": { "$ref": "#/components/schemas/NotificationImage" }
18508
- },
18509
- "additionalProperties": false
18510
- },
18511
- "MessageContent": {
18512
- "oneOf": [
18513
- { "$ref": "#/components/schemas/TextContent" },
18514
- { "$ref": "#/components/schemas/DocumentContent" }
18515
- ]
18516
- },
18517
- "MetadataValue": {
18518
- "oneOf": [
18519
- { "type": "string" },
18520
- { "type": "number" },
18521
- { "type": "boolean" },
18522
- { "type": "null" }
19080
+ {
19081
+ "type": "object",
19082
+ "required": ["status", "expires_at", "failure"],
19083
+ "properties": {
19084
+ "status": { "const": "failed" },
19085
+ "expires_at": { "$ref": "#/components/schemas/Timestamp" },
19086
+ "failure": { "$ref": "#/components/schemas/ProviderFailure" }
19087
+ },
19088
+ "additionalProperties": false
19089
+ },
19090
+ {
19091
+ "type": "object",
19092
+ "required": ["status", "expires_at"],
19093
+ "properties": {
19094
+ "status": { "const": "expired" },
19095
+ "expires_at": { "$ref": "#/components/schemas/Timestamp" }
19096
+ },
19097
+ "additionalProperties": false
19098
+ }
18523
19099
  ]
18524
19100
  },
18525
- "Metadata": {
18526
- "type": "object",
18527
- "description": "Bounded customer correlation data. Do not include secrets or personal data.",
18528
- "maxProperties": 20,
18529
- "propertyNames": { "minLength": 1, "maxLength": 64 },
18530
- "additionalProperties": {
18531
- "$ref": "#/components/schemas/MetadataValue"
18532
- },
18533
- "x-max-json-bytes": 4096
18534
- },
18535
- "InteractionPrompt": {
19101
+ "EmailDomainName": {
18536
19102
  "type": "string",
18537
- "minLength": 1,
18538
- "maxLength": 1000
19103
+ "minLength": 4,
19104
+ "maxLength": 253,
19105
+ "pattern": "^(?=.{1,253}$)(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\\.){2,}[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$",
19106
+ "description": "A canonical lower-case IDNA ASCII customer sending domain with its trailing dot removed. It must be a dedicated subdomain below the registrable domain; runtime validation uses the public suffix list so a registrable root such as example.co.uk is still rejected. URLs, ports, wildcards and mailbox addresses are invalid."
18539
19107
  },
18540
- "Timestamp-2": {
19108
+ "EmailDomainLifecycle": {
18541
19109
  "type": "string",
18542
- "format": "date-time",
18543
- "pattern": "^[0-9]{4}-(?:0[1-9]|1[0-2])-(?:0[1-9]|[12][0-9]|3[01])T(?:[01][0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9](?:\\.[0-9]+)?(?:Z|[+-](?:[01][0-9]|2[0-3]):[0-5][0-9])$"
19110
+ "enum": [
19111
+ "requested",
19112
+ "creating",
19113
+ "dns_required",
19114
+ "verifying",
19115
+ "testing",
19116
+ "active",
19117
+ "provider_action_required",
19118
+ "dns_failed",
19119
+ "oauth_required",
19120
+ "held",
19121
+ "deleting",
19122
+ "deleted"
19123
+ ],
19124
+ "description": "The provider-neutral domain lifecycle. Sending and receiving capabilities are reported separately because they verify on different clocks."
18544
19125
  },
18545
- "InteractionCorrelationId": {
19126
+ "EmailDomainCapabilityState": {
18546
19127
  "type": "string",
18547
- "minLength": 8,
18548
- "maxLength": 128,
18549
- "pattern": "^[^\\u0000-\\u001F\\u007F]+$",
18550
- "description": "Caller-owned opaque correlation returned as data, never treated as execution authority."
19128
+ "enum": ["pending", "verified", "failed", "unsupported"]
18551
19129
  },
18552
- "ConfirmInteractionRequest": {
19130
+ "EmailDomainCapabilities": {
18553
19131
  "type": "object",
18554
- "required": ["type", "prompt", "expires_at"],
19132
+ "required": [
19133
+ "sending",
19134
+ "receiving",
19135
+ "delivery",
19136
+ "link_interactions",
19137
+ "free_text_reply"
19138
+ ],
18555
19139
  "properties": {
18556
- "type": { "const": "confirm" },
18557
- "prompt": { "$ref": "#/components/schemas/InteractionPrompt" },
18558
- "confirm_label": {
18559
- "type": "string",
18560
- "minLength": 1,
18561
- "maxLength": 40
19140
+ "sending": {
19141
+ "$ref": "#/components/schemas/EmailDomainCapabilityState"
18562
19142
  },
18563
- "deny_label": { "type": "string", "minLength": 1, "maxLength": 40 },
18564
- "expires_at": {
18565
- "$ref": "#/components/schemas/Timestamp-2",
18566
- "description": "Must be 30 seconds through 24 hours after server acceptance."
19143
+ "receiving": {
19144
+ "$ref": "#/components/schemas/EmailDomainCapabilityState"
18567
19145
  },
18568
- "correlation_id": {
18569
- "$ref": "#/components/schemas/InteractionCorrelationId"
19146
+ "delivery": { "type": "boolean" },
19147
+ "link_interactions": { "type": "boolean" },
19148
+ "free_text_reply": {
19149
+ "type": "boolean",
19150
+ "description": "False when free-text replies cannot be lowered: receiving may not be verified yet, or the provider team may have message-content storage disabled. Delivery and link-based interaction remain independently available; no undocumented eligibility or storage-state endpoint is assumed."
18570
19151
  }
18571
19152
  },
18572
19153
  "additionalProperties": false
18573
19154
  },
18574
- "InteractionOption": {
19155
+ "EmailDnsRecord": {
19156
+ "description": "One exact neutral DNS record. `name` is preserved exactly as the provider returns it and is relative to the registrable domain (for example `send.notify`, not `send.notify.example.com`).",
19157
+ "oneOf": [
19158
+ {
19159
+ "type": "object",
19160
+ "required": ["type", "name", "value", "priority", "status"],
19161
+ "properties": {
19162
+ "type": { "const": "MX" },
19163
+ "name": {
19164
+ "type": "string",
19165
+ "minLength": 1,
19166
+ "maxLength": 253,
19167
+ "pattern": "^[^\u0000-\u001f-Ÿ

]+$"
19168
+ },
19169
+ "value": {
19170
+ "type": "string",
19171
+ "minLength": 1,
19172
+ "maxLength": 2048,
19173
+ "pattern": "^[^\u0000-\u001f-Ÿ

]+$"
19174
+ },
19175
+ "priority": { "type": "integer", "minimum": 0, "maximum": 65535 },
19176
+ "status": {
19177
+ "enum": [
19178
+ "not_started",
19179
+ "pending",
19180
+ "verified",
19181
+ "failed",
19182
+ "temporary_failure"
19183
+ ]
19184
+ }
19185
+ },
19186
+ "additionalProperties": false
19187
+ },
19188
+ {
19189
+ "type": "object",
19190
+ "required": ["type", "name", "value", "status"],
19191
+ "properties": {
19192
+ "type": { "enum": ["TXT", "CNAME"] },
19193
+ "name": {
19194
+ "type": "string",
19195
+ "minLength": 1,
19196
+ "maxLength": 253,
19197
+ "pattern": "^[^\u0000-\u001f-Ÿ

]+$"
19198
+ },
19199
+ "value": {
19200
+ "type": "string",
19201
+ "minLength": 1,
19202
+ "maxLength": 2048,
19203
+ "pattern": "^[^\u0000-\u001f-Ÿ

]+$"
19204
+ },
19205
+ "status": {
19206
+ "enum": [
19207
+ "not_started",
19208
+ "pending",
19209
+ "verified",
19210
+ "failed",
19211
+ "temporary_failure"
19212
+ ]
19213
+ }
19214
+ },
19215
+ "additionalProperties": false
19216
+ }
19217
+ ]
19218
+ },
19219
+ "EmailDomainGuide": {
18575
19220
  "type": "object",
18576
- "required": ["value", "label"],
19221
+ "required": ["state"],
18577
19222
  "properties": {
18578
- "value": {
19223
+ "state": {
19224
+ "enum": [
19225
+ "detecting",
19226
+ "guided",
19227
+ "domain_connect_available",
19228
+ "complete",
19229
+ "unavailable"
19230
+ ]
19231
+ },
19232
+ "dns_provider": {
18579
19233
  "type": "string",
18580
19234
  "minLength": 1,
18581
- "maxLength": 128,
18582
- "pattern": "^[^\\u0000-\\u001F\\u007F-\u001f-Ÿ

]+$",
18583
- "description": "Opaque customer data. It is never interpreted as a command."
19235
+ "maxLength": 100,
19236
+ "pattern": "^[^\u0000-\u001f-Ÿ

]+$"
18584
19237
  },
18585
- "label": { "type": "string", "minLength": 1, "maxLength": 120 },
18586
- "intent": {
19238
+ "instructions_url": {
18587
19239
  "type": "string",
18588
- "enum": ["accept", "destructive", "informational", "neutral"],
18589
- "default": "neutral",
18590
- "description": "What choosing this option does, so a channel can present it accordingly. Describes consequence rather than appearance: a channel renders `accept` green where it can, `destructive` red, `informational` prominent, and `neutral` plainly. A channel that cannot colour a control ignores this and the option still renders. Omitted means `neutral`."
19240
+ "format": "uri",
19241
+ "pattern": "^https://[^\\s]+$",
19242
+ "maxLength": 2048,
19243
+ "description": "A short-lived secret-free instruction link for a DNS administrator. It contains no WhooshBang session or provider OAuth material."
18591
19244
  }
18592
19245
  },
19246
+ "allOf": [
19247
+ {
19248
+ "if": {
19249
+ "properties": {
19250
+ "state": {
19251
+ "enum": ["guided", "domain_connect_available", "complete"]
19252
+ }
19253
+ },
19254
+ "required": ["state"]
19255
+ },
19256
+ "then": {
19257
+ "properties": { "dns_provider": true },
19258
+ "required": ["dns_provider"]
19259
+ },
19260
+ "else": { "properties": { "dns_provider": false } }
19261
+ }
19262
+ ],
18593
19263
  "additionalProperties": false
18594
19264
  },
18595
- "SelectInteractionRequest": {
18596
- "type": "object",
18597
- "required": ["type", "prompt", "options", "expires_at"],
18598
- "properties": {
18599
- "type": { "const": "select" },
18600
- "prompt": { "$ref": "#/components/schemas/InteractionPrompt" },
18601
- "options": {
18602
- "type": "array",
18603
- "minItems": 2,
18604
- "maxItems": 6,
18605
- "items": { "$ref": "#/components/schemas/InteractionOption" },
18606
- "x-unique-property": "value"
18607
- },
18608
- "expires_at": {
18609
- "$ref": "#/components/schemas/Timestamp-2",
18610
- "description": "Must be 30 seconds through 24 hours after server acceptance."
18611
- },
18612
- "correlation_id": {
18613
- "$ref": "#/components/schemas/InteractionCorrelationId"
18614
- }
19265
+ "EmailSubjectPolicy": {
19266
+ "type": "object",
19267
+ "description": "The fixed subject rendering contract. Notification title wins; otherwise localized copy equivalent to `Notification from {notifier display name}` is used. Whitespace is normalized and the final value is capped at 200 Unicode code points.",
19268
+ "required": ["fallback", "maximum_code_points", "normalize_whitespace"],
19269
+ "properties": {
19270
+ "fallback": { "const": "localized_notifier_name" },
19271
+ "maximum_code_points": { "const": 200 },
19272
+ "normalize_whitespace": { "const": true }
18615
19273
  },
18616
19274
  "additionalProperties": false
18617
19275
  },
18618
- "InputInteractionRequest": {
19276
+ "EmailDeliveryPolicy": {
18619
19277
  "type": "object",
18620
- "required": ["type", "prompt", "expires_at"],
19278
+ "description": "Privacy-safe email defaults. Return-Path and Message-ID remain provider-owned, arbitrary headers are unsupported, and every ordinary message carries a visible management route plus safe one-click unsubscribe semantics when custom headers are available. A GET never mutates consent; the matching POST resolves the exact binding.",
19279
+ "required": [
19280
+ "open_tracking",
19281
+ "click_tracking",
19282
+ "tls",
19283
+ "unsubscribe",
19284
+ "subject"
19285
+ ],
18621
19286
  "properties": {
18622
- "type": { "const": "input" },
18623
- "prompt": { "$ref": "#/components/schemas/InteractionPrompt" },
18624
- "expires_at": {
18625
- "$ref": "#/components/schemas/Timestamp-2",
18626
- "description": "Must be 30 seconds through 24 hours after server acceptance."
19287
+ "open_tracking": { "const": false },
19288
+ "click_tracking": { "const": false },
19289
+ "tls": {
19290
+ "enum": ["opportunistic", "enforced"],
19291
+ "default": "opportunistic"
18627
19292
  },
18628
- "correlation_id": {
18629
- "$ref": "#/components/schemas/InteractionCorrelationId"
18630
- }
19293
+ "unsubscribe": { "const": "visible_and_one_click_where_supported" },
19294
+ "subject": { "$ref": "#/components/schemas/EmailSubjectPolicy" }
18631
19295
  },
18632
19296
  "additionalProperties": false
18633
19297
  },
18634
- "HighestProvenProviderState": {
19298
+ "OrganizationCredentialPrefix": {
18635
19299
  "type": "string",
18636
- "enum": ["none", "provider_accepted"],
18637
- "description": "The strongest provider assertion the service can prove; it is not an end-device delivery receipt."
19300
+ "pattern": "^wb_oc1\\.ocred_[A-Za-z0-9_-]{8}$",
19301
+ "description": "A safe display prefix containing the organization-credential format marker and first eight base64url identity characters, never secret bytes."
18638
19302
  },
18639
- "BroadcastMessageProvenance": {
18640
- "type": "object",
18641
- "description": "Provenance carried by an ordinary message created during group expansion. The broadcast, message subscriber_id, and exact historical membership generation form the child uniqueness boundary.",
18642
- "required": ["broadcast_id", "group_id", "membership_generation"],
18643
- "properties": {
18644
- "broadcast_id": { "$ref": "#/components/schemas/OpaqueIdentifier" },
18645
- "group_id": { "$ref": "#/components/schemas/OpaqueIdentifier" },
18646
- "membership_generation": { "type": "integer", "minimum": 1 }
18647
- },
18648
- "additionalProperties": false
19303
+ "OrganizationCredentialScope": {
19304
+ "type": "string",
19305
+ "enum": [
19306
+ "organization:read",
19307
+ "projects:write",
19308
+ "projects:read",
19309
+ "messages:write",
19310
+ "messages:read",
19311
+ "subscriptions:write",
19312
+ "subscriptions:read",
19313
+ "machine-clients:write",
19314
+ "machine-clients:read",
19315
+ "capabilities:read",
19316
+ "connections:write",
19317
+ "connections:read"
19318
+ ],
19319
+ "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."
18649
19320
  },
18650
- "TrimmedInputAnswer": {
19321
+ "OrganizationCredentialScopeSet": {
19322
+ "type": "array",
19323
+ "minItems": 1,
19324
+ "maxItems": 12,
19325
+ "uniqueItems": true,
19326
+ "items": { "$ref": "#/components/schemas/OrganizationCredentialScope" }
19327
+ },
19328
+ "OrganizationCredentialSecret": {
18651
19329
  "type": "string",
18652
- "minLength": 1,
18653
- "maxLength": 4000,
18654
- "pattern": "^\\S(?:[\\s\\S]*\\S)?$",
18655
- "description": "Untrusted input text after leading and trailing whitespace is removed."
19330
+ "pattern": "^wb_oc1\\.ocred_[A-Za-z0-9_-]{21}[AQgw]\\.[A-Za-z0-9_-]{42}[AEIMQUYcgkosw048]$",
19331
+ "description": "The exact one-time runtime bearer syntax. The final segment is canonical unpadded base64url for exactly 32 random secret bytes."
18656
19332
  },
18657
- "OpaqueIdentifier-2": {
19333
+ "RecipientCopyOverrideValue": {
18658
19334
  "type": "string",
18659
19335
  "minLength": 1,
18660
- "maxLength": 128,
18661
- "pattern": "^[^\\u0000-\\u001F\\u007F]+$",
18662
- "description": "An opaque identifier whose internal encoding is not a public promise."
19336
+ "maxLength": 500,
19337
+ "description": "Customer-authored plain text with optional plain template placeholders. The service enforces key-specific length and rejects invisible controls. Rendering escapes text; changing labels does not change recipient control actions."
18663
19338
  },
18664
- "InteractionState": {
19339
+ "ApiCredentialPrefix": {
18665
19340
  "type": "string",
18666
- "enum": ["open", "answered", "expired", "cancelled", "retired"]
19341
+ "pattern": "^wb_pc1\\.pcred_[A-Za-z0-9_-]{8}$",
19342
+ "description": "A safe display prefix containing the credential format marker and first eight base64url identity characters, never secret bytes."
19343
+ },
19344
+ "ApiCredentialScope": {
19345
+ "type": "string",
19346
+ "enum": [
19347
+ "projects:write",
19348
+ "projects:read",
19349
+ "messages:write",
19350
+ "messages:read",
19351
+ "subscriptions:write",
19352
+ "subscriptions:read",
19353
+ "machine-clients:write",
19354
+ "machine-clients:read",
19355
+ "capabilities:read",
19356
+ "connections:write",
19357
+ "connections:read"
19358
+ ],
19359
+ "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."
19360
+ },
19361
+ "ApiCredentialScopeSet": {
19362
+ "type": "array",
19363
+ "minItems": 1,
19364
+ "maxItems": 11,
19365
+ "uniqueItems": true,
19366
+ "items": { "$ref": "#/components/schemas/ApiCredentialScope" }
19367
+ },
19368
+ "ApiCredentialSecret": {
19369
+ "type": "string",
19370
+ "pattern": "^wb_pc1\\.pcred_[A-Za-z0-9_-]{21}[AQgw]\\.[A-Za-z0-9_-]{42}[AEIMQUYcgkosw048]$",
19371
+ "description": "The exact one-time runtime bearer syntax. The final segment is canonical unpadded base64url for exactly 32 random secret bytes."
19372
+ },
19373
+ "MessageTarget": {
19374
+ "type": "object",
19375
+ "required": ["subscriber_id"],
19376
+ "properties": {
19377
+ "subscriber_id": { "$ref": "#/components/schemas/OpaqueIdentifier" }
19378
+ },
19379
+ "additionalProperties": false
18667
19380
  },
18668
19381
  "OpaqueIdentifier-3": {
18669
19382
  "type": "string",
@@ -19472,13 +20185,6 @@
19472
20185
  "pattern": "^https?://[^/?#*\\s]+(?::[0-9]{1,5})?/?$",
19473
20186
  "description": "One exact scheme, host, and optional port, in any spelling that means that same origin. A trailing slash, an uppercase host, and an explicitly written default port are accepted and stored in canonical form. Paths, queries, fragments, credentials, and wildcards are forbidden; HTTP is accepted only for localhost or synthetic .test configuration."
19474
20187
  },
19475
- "DeveloperKey": {
19476
- "type": "string",
19477
- "minLength": 1,
19478
- "maxLength": 63,
19479
- "pattern": "^[a-z0-9]+(?:-[a-z0-9]+)*$",
19480
- "description": "A stable, lowercase developer key used by registered return destinations and subscriber groups."
19481
- },
19482
20188
  "RegisteredReturnUrl": {
19483
20189
  "type": "string",
19484
20190
  "format": "uri",
@@ -19496,97 +20202,6 @@
19496
20202
  },
19497
20203
  "additionalProperties": false
19498
20204
  },
19499
- "EmailBindingConsent": {
19500
- "description": "How consent for this exact subscriber, notifier and connection was established. A customer assertion never becomes recipient-observed verification. Complaint, unsubscribe and suppression override either arm.",
19501
- "oneOf": [
19502
- {
19503
- "type": "object",
19504
- "required": ["kind", "observed_at"],
19505
- "properties": {
19506
- "kind": { "const": "recipient_observed" },
19507
- "observed_at": { "$ref": "#/components/schemas/Timestamp" }
19508
- },
19509
- "additionalProperties": false
19510
- },
19511
- {
19512
- "type": "object",
19513
- "required": ["kind", "consented_at", "source"],
19514
- "properties": {
19515
- "kind": { "const": "asserted_prior_consent" },
19516
- "consented_at": { "$ref": "#/components/schemas/Timestamp" },
19517
- "source": {
19518
- "type": "string",
19519
- "minLength": 1,
19520
- "maxLength": 100,
19521
- "pattern": "^[^\u0000-\u001f-Ÿ

]+$",
19522
- "description": "A bounded customer description of where consent was collected. The authenticated asserting actor is recorded by the service, never accepted from this object."
19523
- },
19524
- "asserted_at": { "$ref": "#/components/schemas/Timestamp" },
19525
- "asserted_by": {
19526
- "type": "object",
19527
- "required": ["kind", "id"],
19528
- "properties": {
19529
- "kind": { "enum": ["credential", "grant", "session"] },
19530
- "id": { "$ref": "#/components/schemas/OpaqueIdentifier" }
19531
- },
19532
- "additionalProperties": false
19533
- },
19534
- "domain_id": { "$ref": "#/components/schemas/OpaqueIdentifier" }
19535
- },
19536
- "additionalProperties": false
19537
- }
19538
- ]
19539
- },
19540
- "RecipientChannelSummary": {
19541
- "type": "object",
19542
- "description": "Safe state for one configured channel. Provider identities, credentials, callback state, and other subscribers are absent.",
19543
- "required": ["channel", "state", "preferred"],
19544
- "properties": {
19545
- "channel": { "$ref": "#/components/schemas/Channel" },
19546
- "state": {
19547
- "type": "string",
19548
- "enum": ["available", "connected", "paused"]
19549
- },
19550
- "binding_id": {
19551
- "$ref": "#/components/schemas/OpaqueIdentifier",
19552
- "description": "The exact binding owned by this session's subscriber. Present only for connected or paused state."
19553
- },
19554
- "preferred": { "type": "boolean" },
19555
- "connection_id": { "$ref": "#/components/schemas/OpaqueIdentifier" },
19556
- "masked_address": {
19557
- "type": "string",
19558
- "minLength": 3,
19559
- "maxLength": 254,
19560
- "description": "Masked recipient address. Never the full mailbox."
19561
- },
19562
- "consent": { "$ref": "#/components/schemas/EmailBindingConsent" }
19563
- },
19564
- "allOf": [
19565
- {
19566
- "if": {
19567
- "properties": { "state": { "enum": ["connected", "paused"] } },
19568
- "required": ["state"]
19569
- },
19570
- "then": {
19571
- "required": ["binding_id"],
19572
- "properties": { "binding_id": {} }
19573
- }
19574
- },
19575
- {
19576
- "if": {
19577
- "properties": { "state": { "const": "available" } },
19578
- "required": ["state"]
19579
- },
19580
- "then": {
19581
- "properties": {
19582
- "binding_id": false,
19583
- "preferred": { "const": false }
19584
- }
19585
- }
19586
- }
19587
- ],
19588
- "additionalProperties": false
19589
- },
19590
20205
  "RecipientSessionCapabilityPrefix": {
19591
20206
  "type": "string",
19592
20207
  "minLength": 33,
@@ -19645,33 +20260,6 @@
19645
20260
  "pattern": "^(?:UTC|[A-Za-z][A-Za-z0-9._+-]*(?:/[A-Za-z][A-Za-z0-9._+-]*)+)$",
19646
20261
  "description": "A named IANA time-zone identifier. Numeric offsets, abbreviations and display labels are invalid; recognized aliases normalize to the runtime's canonical identifier before persistence. Providers may leave it absent."
19647
20262
  },
19648
- "BroadcastAggregate": {
19649
- "type": "object",
19650
- "description": "A projection over expansion and ordinary child-message states, not an independent delivery state machine.",
19651
- "required": [
19652
- "audience",
19653
- "expanded",
19654
- "pending",
19655
- "provider_accepted",
19656
- "delivered",
19657
- "skipped",
19658
- "failed",
19659
- "cancelled",
19660
- "expired"
19661
- ],
19662
- "properties": {
19663
- "audience": { "type": "integer", "minimum": 0 },
19664
- "expanded": { "type": "integer", "minimum": 0 },
19665
- "pending": { "type": "integer", "minimum": 0 },
19666
- "provider_accepted": { "type": "integer", "minimum": 0 },
19667
- "delivered": { "type": "integer", "minimum": 0 },
19668
- "skipped": { "type": "integer", "minimum": 0 },
19669
- "failed": { "type": "integer", "minimum": 0 },
19670
- "cancelled": { "type": "integer", "minimum": 0 },
19671
- "expired": { "type": "integer", "minimum": 0 }
19672
- },
19673
- "additionalProperties": false
19674
- },
19675
20263
  "SafeExplanation": { "type": "string", "minLength": 1, "maxLength": 500 },
19676
20264
  "InteractionCapability": {
19677
20265
  "type": "object",
@@ -19752,7 +20340,7 @@
19752
20340
  "provider-capabilities-v1.schema": {
19753
20341
  "$schema": "https://json-schema.org/draft/2020-12/schema",
19754
20342
  "$id": "https://whooshbang.flowxo.com/schemas/provider-capabilities-v1.schema.json",
19755
- "x-contract-version": "2.0.0",
20343
+ "x-contract-version": "2.2.0",
19756
20344
  "title": "ProviderCapabilitiesV1",
19757
20345
  "description": "Provider-neutral, renderer-facing delivery and interaction capabilities with explicit degradation.",
19758
20346
  "type": "object",
@@ -20472,5 +21060,5 @@
20472
21060
  }
20473
21061
  }
20474
21062
  },
20475
- "x-contract-version": "2.0.0"
21063
+ "x-contract-version": "2.2.0"
20476
21064
  }