@whooshbang/contracts 1.0.0-rc.13

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 (229) hide show
  1. package/CHANGELOG.md +198 -0
  2. package/LICENSE +21 -0
  3. package/README.md +120 -0
  4. package/contract-manifest.json +390 -0
  5. package/dist/base64url.d.ts +2 -0
  6. package/dist/base64url.js +51 -0
  7. package/dist/base64url.js.map +1 -0
  8. package/dist/generated/artifact-index.d.ts +205 -0
  9. package/dist/generated/artifact-index.js +396 -0
  10. package/dist/generated/artifact-index.js.map +1 -0
  11. package/dist/generated/contract-manifest.d.ts +66 -0
  12. package/dist/generated/contract-manifest.js +6 -0
  13. package/dist/generated/contract-manifest.js.map +1 -0
  14. package/dist/generated/core-scenario.d.ts +18 -0
  15. package/dist/generated/core-scenario.js +6 -0
  16. package/dist/generated/core-scenario.js.map +1 -0
  17. package/dist/generated/fixture-index.d.ts +33 -0
  18. package/dist/generated/fixture-index.js +6 -0
  19. package/dist/generated/fixture-index.js.map +1 -0
  20. package/dist/generated/interaction-machine-scenario.d.ts +263 -0
  21. package/dist/generated/interaction-machine-scenario.js +6 -0
  22. package/dist/generated/interaction-machine-scenario.js.map +1 -0
  23. package/dist/generated/interaction-v1.d.ts +152 -0
  24. package/dist/generated/interaction-v1.js +6 -0
  25. package/dist/generated/interaction-v1.js.map +1 -0
  26. package/dist/generated/machine-v1.d.ts +169 -0
  27. package/dist/generated/machine-v1.js +6 -0
  28. package/dist/generated/machine-v1.js.map +1 -0
  29. package/dist/generated/provider-capabilities-v1.d.ts +78 -0
  30. package/dist/generated/provider-capabilities-v1.js +6 -0
  31. package/dist/generated/provider-capabilities-v1.js.map +1 -0
  32. package/dist/generated/validators-standalone.d.ts +659 -0
  33. package/dist/generated/validators-standalone.js +47800 -0
  34. package/dist/generated/validators-standalone.js.map +1 -0
  35. package/dist/generated/validators.d.ts +74 -0
  36. package/dist/generated/validators.js +493 -0
  37. package/dist/generated/validators.js.map +1 -0
  38. package/dist/generated/webhook-delivery-case-v1.d.ts +91 -0
  39. package/dist/generated/webhook-delivery-case-v1.js +6 -0
  40. package/dist/generated/webhook-delivery-case-v1.js.map +1 -0
  41. package/dist/generated/webhook-signing-vector-v1.d.ts +88 -0
  42. package/dist/generated/webhook-signing-vector-v1.js +6 -0
  43. package/dist/generated/webhook-signing-vector-v1.js.map +1 -0
  44. package/dist/generated/whooshbang-event-v1.d.ts +191 -0
  45. package/dist/generated/whooshbang-event-v1.js +6 -0
  46. package/dist/generated/whooshbang-event-v1.js.map +1 -0
  47. package/dist/generated/whooshbang-v1.d.ts +1162 -0
  48. package/dist/generated/whooshbang-v1.js +6 -0
  49. package/dist/generated/whooshbang-v1.js.map +1 -0
  50. package/dist/index.d.ts +20 -0
  51. package/dist/index.js +7 -0
  52. package/dist/index.js.map +1 -0
  53. package/dist/machine-credential.d.ts +31 -0
  54. package/dist/machine-credential.js +78 -0
  55. package/dist/machine-credential.js.map +1 -0
  56. package/dist/webhook-consumer.d.ts +50 -0
  57. package/dist/webhook-consumer.js +71 -0
  58. package/dist/webhook-consumer.js.map +1 -0
  59. package/dist/webhook-delivery.d.ts +46 -0
  60. package/dist/webhook-delivery.js +126 -0
  61. package/dist/webhook-delivery.js.map +1 -0
  62. package/dist/webhook-signature.d.ts +36 -0
  63. package/dist/webhook-signature.js +255 -0
  64. package/dist/webhook-signature.js.map +1 -0
  65. package/examples/python/verify_webhook.py +283 -0
  66. package/fixtures/.gitkeep +1 -0
  67. package/fixtures/core-v1/fixture-index.json +963 -0
  68. package/fixtures/core-v1/problems/auth-invalid.json +9 -0
  69. package/fixtures/core-v1/problems/authentication-required.json +9 -0
  70. package/fixtures/core-v1/problems/cancellation-too-late.json +9 -0
  71. package/fixtures/core-v1/problems/endpoint-rotation-conflict.json +10 -0
  72. package/fixtures/core-v1/problems/environment-mismatch.json +9 -0
  73. package/fixtures/core-v1/problems/idempotency-conflict.json +9 -0
  74. package/fixtures/core-v1/problems/idempotency-key-required.json +9 -0
  75. package/fixtures/core-v1/problems/project-slug-conflict.json +16 -0
  76. package/fixtures/core-v1/problems/provider-outcome-unknown.json +9 -0
  77. package/fixtures/core-v1/problems/provider-retryable.json +10 -0
  78. package/fixtures/core-v1/problems/provider-terminal.json +9 -0
  79. package/fixtures/core-v1/problems/rate-limited.json +10 -0
  80. package/fixtures/core-v1/problems/replay-unavailable-not-terminal.json +16 -0
  81. package/fixtures/core-v1/problems/replay-unavailable-payload.json +16 -0
  82. package/fixtures/core-v1/problems/replay-unavailable-rollout.json +16 -0
  83. package/fixtures/core-v1/problems/request-invalid.json +16 -0
  84. package/fixtures/core-v1/problems/resource-expired.json +9 -0
  85. package/fixtures/core-v1/problems/resource-not-found.json +9 -0
  86. package/fixtures/core-v1/problems/scope-forbidden.json +9 -0
  87. package/fixtures/core-v1/problems/subscriber-unbound.json +9 -0
  88. package/fixtures/core-v1/requests/create-api-credential.json +9 -0
  89. package/fixtures/core-v1/requests/create-customer-endpoint.json +5 -0
  90. package/fixtures/core-v1/requests/create-message.json +15 -0
  91. package/fixtures/core-v1/requests/create-project.json +4 -0
  92. package/fixtures/core-v1/requests/create-subscription-link.json +5 -0
  93. package/fixtures/core-v1/requests/rotate-api-credential.json +3 -0
  94. package/fixtures/core-v1/requests/rotate-customer-endpoint.json +3 -0
  95. package/fixtures/core-v1/scenarios/api-credential-invalid-indistinguishable.json +14 -0
  96. package/fixtures/core-v1/scenarios/api-credential-one-time-reveal.json +21 -0
  97. package/fixtures/core-v1/scenarios/api-credential-revocation-replay.json +17 -0
  98. package/fixtures/core-v1/scenarios/api-credential-rotation-overlap.json +18 -0
  99. package/fixtures/core-v1/scenarios/cancellation-too-late.json +15 -0
  100. package/fixtures/core-v1/scenarios/cross-account-project.json +14 -0
  101. package/fixtures/core-v1/scenarios/cross-project-resource.json +17 -0
  102. package/fixtures/core-v1/scenarios/environment-mismatch.json +17 -0
  103. package/fixtures/core-v1/scenarios/idempotency-conflict.json +35 -0
  104. package/fixtures/core-v1/scenarios/idempotency-equivalent-replay.json +27 -0
  105. package/fixtures/core-v1/scenarios/malformed-bearer.json +14 -0
  106. package/fixtures/core-v1/scenarios/metadata-overrun.json +14 -0
  107. package/fixtures/core-v1/scenarios/missing-scope.json +15 -0
  108. package/fixtures/core-v1/scenarios/project-create-equivalent-replay.json +26 -0
  109. package/fixtures/core-v1/scenarios/project-slug-conflict.json +21 -0
  110. package/fixtures/core-v1/scenarios/provider-outcome-unknown.json +15 -0
  111. package/fixtures/core-v1/scenarios/subscription-activation-replay.json +18 -0
  112. package/fixtures/core-v1/scenarios/subscription-expired-replay.json +16 -0
  113. package/fixtures/core-v1/success/account-projection.json +11 -0
  114. package/fixtures/core-v1/success/api-credential-collection.json +22 -0
  115. package/fixtures/core-v1/success/api-credential-created.json +20 -0
  116. package/fixtures/core-v1/success/api-credential-revoked.json +19 -0
  117. package/fixtures/core-v1/success/api-credential-rotated.json +41 -0
  118. package/fixtures/core-v1/success/customer-endpoint-attempts.json +15 -0
  119. package/fixtures/core-v1/success/customer-endpoint-collection.json +26 -0
  120. package/fixtures/core-v1/success/customer-endpoint-create-replayed.json +16 -0
  121. package/fixtures/core-v1/success/customer-endpoint-created.json +17 -0
  122. package/fixtures/core-v1/success/customer-endpoint-rotated.json +21 -0
  123. package/fixtures/core-v1/success/message-accepted.json +30 -0
  124. package/fixtures/core-v1/success/message-cancellation-too-late.json +27 -0
  125. package/fixtures/core-v1/success/message-cancelled.json +27 -0
  126. package/fixtures/core-v1/success/message-outcome-unknown.json +33 -0
  127. package/fixtures/core-v1/success/message-provider-accepted.json +30 -0
  128. package/fixtures/core-v1/success/project-collection.json +42 -0
  129. package/fixtures/core-v1/success/project-created.json +38 -0
  130. package/fixtures/core-v1/success/subscription-link-activated.json +22 -0
  131. package/fixtures/core-v1/success/subscription-link-cancelled.json +14 -0
  132. package/fixtures/core-v1/success/subscription-link-paused.json +22 -0
  133. package/fixtures/core-v1/success/subscription-link-pending.json +14 -0
  134. package/fixtures/core-v1/success/subscription-link-revoked.json +22 -0
  135. package/fixtures/interaction-machine-v1/capabilities/providers.json +57 -0
  136. package/fixtures/interaction-machine-v1/capabilities/telegram.json +52 -0
  137. package/fixtures/interaction-machine-v1/events/confirm-answered.json +22 -0
  138. package/fixtures/interaction-machine-v1/events/input-answered.json +22 -0
  139. package/fixtures/interaction-machine-v1/events/input-redacted.json +18 -0
  140. package/fixtures/interaction-machine-v1/events/select-answered.json +22 -0
  141. package/fixtures/interaction-machine-v1/requests/ack-processed.json +5 -0
  142. package/fixtures/interaction-machine-v1/requests/ack-quarantined.json +5 -0
  143. package/fixtures/interaction-machine-v1/requests/create-machine-client.json +6 -0
  144. package/fixtures/interaction-machine-v1/requests/create-message-confirm.json +18 -0
  145. package/fixtures/interaction-machine-v1/requests/create-message-input.json +16 -0
  146. package/fixtures/interaction-machine-v1/requests/create-message-select.json +26 -0
  147. package/fixtures/interaction-machine-v1/requests/register-machine-credential.json +4 -0
  148. package/fixtures/interaction-machine-v1/scenarios/ack-conflict.json +14 -0
  149. package/fixtures/interaction-machine-v1/scenarios/ack-cross-machine.json +15 -0
  150. package/fixtures/interaction-machine-v1/scenarios/ack-out-of-order.json +15 -0
  151. package/fixtures/interaction-machine-v1/scenarios/ack-replay.json +17 -0
  152. package/fixtures/interaction-machine-v1/scenarios/ambiguous-free-text.json +15 -0
  153. package/fixtures/interaction-machine-v1/scenarios/callback-token-opacity.json +16 -0
  154. package/fixtures/interaction-machine-v1/scenarios/credential-registration-conflict.json +16 -0
  155. package/fixtures/interaction-machine-v1/scenarios/credential-registration-replay.json +15 -0
  156. package/fixtures/interaction-machine-v1/scenarios/credential-revocation-replay.json +14 -0
  157. package/fixtures/interaction-machine-v1/scenarios/credential-rotation.json +16 -0
  158. package/fixtures/interaction-machine-v1/scenarios/first-credential-activates.json +15 -0
  159. package/fixtures/interaction-machine-v1/scenarios/interaction-duplicate.json +16 -0
  160. package/fixtures/interaction-machine-v1/scenarios/interaction-expired.json +17 -0
  161. package/fixtures/interaction-machine-v1/scenarios/interaction-mismatch.json +16 -0
  162. package/fixtures/interaction-machine-v1/scenarios/interaction-retired.json +16 -0
  163. package/fixtures/interaction-machine-v1/scenarios/interaction-stale.json +16 -0
  164. package/fixtures/interaction-machine-v1/scenarios/machine-admin-denied.json +14 -0
  165. package/fixtures/interaction-machine-v1/scenarios/machine-bound-message-create.json +22 -0
  166. package/fixtures/interaction-machine-v1/scenarios/machine-cancel-denied.json +14 -0
  167. package/fixtures/interaction-machine-v1/scenarios/machine-crash-before-ack.json +16 -0
  168. package/fixtures/interaction-machine-v1/scenarios/machine-notifier-mismatch.json +14 -0
  169. package/fixtures/interaction-machine-v1/scenarios/machine-other-message-read.json +14 -0
  170. package/fixtures/interaction-machine-v1/scenarios/machine-owned-message-read.json +22 -0
  171. package/fixtures/interaction-machine-v1/scenarios/machine-subscriber-mismatch.json +14 -0
  172. package/fixtures/interaction-machine-v1/scenarios/poll-uncommitted-cursor-rejected.json +17 -0
  173. package/fixtures/interaction-machine-v1/scenarios/project-credential-machine-endpoint-denied.json +14 -0
  174. package/fixtures/interaction-machine-v1/scenarios/quarantine-integrity-rejected.json +15 -0
  175. package/fixtures/interaction-machine-v1/scenarios/quarantine-signature-rejected.json +16 -0
  176. package/fixtures/interaction-machine-v1/scenarios/quarantine-valid.json +15 -0
  177. package/fixtures/interaction-machine-v1/scenarios/revoked-machine-credential.json +15 -0
  178. package/fixtures/interaction-machine-v1/success/ack-out-of-order.json +8 -0
  179. package/fixtures/interaction-machine-v1/success/ack-processed.json +8 -0
  180. package/fixtures/interaction-machine-v1/success/ack-quarantined.json +9 -0
  181. package/fixtures/interaction-machine-v1/success/interaction-answered-confirm.json +10 -0
  182. package/fixtures/interaction-machine-v1/success/interaction-answered-input.json +10 -0
  183. package/fixtures/interaction-machine-v1/success/interaction-answered-redacted.json +8 -0
  184. package/fixtures/interaction-machine-v1/success/interaction-answered-select.json +10 -0
  185. package/fixtures/interaction-machine-v1/success/interaction-open.json +7 -0
  186. package/fixtures/interaction-machine-v1/success/machine-client-active.json +20 -0
  187. package/fixtures/interaction-machine-v1/success/machine-client-provisioning.json +19 -0
  188. package/fixtures/interaction-machine-v1/success/machine-client-revoked.json +21 -0
  189. package/fixtures/interaction-machine-v1/success/machine-credential-active.json +13 -0
  190. package/fixtures/interaction-machine-v1/success/machine-credential-revoked.json +14 -0
  191. package/fixtures/interaction-machine-v1/success/message-interaction-open.json +33 -0
  192. package/fixtures/interaction-machine-v1/success/poll-empty.json +6 -0
  193. package/fixtures/interaction-machine-v1/success/poll-events.json +51 -0
  194. package/fixtures/interaction-machine-v1/tokens/callback-token.json +1 -0
  195. package/fixtures/whooshbang-events-v1/delivery/cases.json +418 -0
  196. package/fixtures/whooshbang-events-v1/events/agent-binding-degraded.json +17 -0
  197. package/fixtures/whooshbang-events-v1/events/interaction-expired.json +16 -0
  198. package/fixtures/whooshbang-events-v1/events/interaction-received.json +23 -0
  199. package/fixtures/whooshbang-events-v1/events/message-delivered.json +16 -0
  200. package/fixtures/whooshbang-events-v1/events/message-expired.json +15 -0
  201. package/fixtures/whooshbang-events-v1/events/message-failed.json +17 -0
  202. package/fixtures/whooshbang-events-v1/events/message-provider-accepted.json +15 -0
  203. package/fixtures/whooshbang-events-v1/events/subscription-activated.json +18 -0
  204. package/fixtures/whooshbang-events-v1/events/subscription-revoked.json +18 -0
  205. package/fixtures/whooshbang-events-v1/signing/vectors.json +852 -0
  206. package/openapi/.gitkeep +1 -0
  207. package/openapi/generated/whooshbang-v1.bundle.json +5913 -0
  208. package/openapi/whooshbang-v1.yaml +2334 -0
  209. package/package.json +63 -0
  210. package/schemas/contract-manifest.schema.json +140 -0
  211. package/schemas/core-scenario.schema.json +38 -0
  212. package/schemas/events/agent-binding-degraded-v1.schema.json +8 -0
  213. package/schemas/events/interaction-expired-v1.schema.json +8 -0
  214. package/schemas/events/interaction-received-v1.schema.json +8 -0
  215. package/schemas/events/message-delivered-v1.schema.json +8 -0
  216. package/schemas/events/message-expired-v1.schema.json +8 -0
  217. package/schemas/events/message-failed-v1.schema.json +8 -0
  218. package/schemas/events/message-provider-accepted-v1.schema.json +8 -0
  219. package/schemas/events/subscription-activated-v1.schema.json +8 -0
  220. package/schemas/events/subscription-revoked-v1.schema.json +8 -0
  221. package/schemas/events/whooshbang-event-v1.schema.json +689 -0
  222. package/schemas/fixture-index.schema.json +67 -0
  223. package/schemas/interaction-machine-scenario.schema.json +700 -0
  224. package/schemas/interaction-v1.schema.json +483 -0
  225. package/schemas/machine-v1.schema.json +430 -0
  226. package/schemas/provider-capabilities-v1.schema.json +263 -0
  227. package/schemas/webhooks/webhook-delivery-case-v1.schema.json +167 -0
  228. package/schemas/webhooks/webhook-signing-vector-v1.schema.json +197 -0
  229. package/schemas/whooshbang-v1.schema.json +2173 -0
@@ -0,0 +1,2173 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://whooshbang.flowxo.com/schemas/whooshbang-v1.schema.json",
4
+ "x-contract-version": "1.0.0-rc.13",
5
+ "title": "WhooshBangV1ContractDocument",
6
+ "description": "Public request, resource, summary, and Problem Details models for the WhooshBang V1 REST API.",
7
+ "oneOf": [
8
+ {
9
+ "$ref": "#/$defs/AccountProjection"
10
+ },
11
+ {
12
+ "$ref": "#/$defs/CreateProjectRequest"
13
+ },
14
+ {
15
+ "$ref": "#/$defs/Project"
16
+ },
17
+ {
18
+ "$ref": "#/$defs/ProjectCollection"
19
+ },
20
+ {
21
+ "$ref": "#/$defs/CreateApiCredentialRequest"
22
+ },
23
+ {
24
+ "$ref": "#/$defs/RotateApiCredentialRequest"
25
+ },
26
+ {
27
+ "$ref": "#/$defs/ApiCredential"
28
+ },
29
+ {
30
+ "$ref": "#/$defs/ApiCredentialCollection"
31
+ },
32
+ {
33
+ "$ref": "#/$defs/ApiCredentialSecretReveal"
34
+ },
35
+ {
36
+ "$ref": "#/$defs/ApiCredentialRotation"
37
+ },
38
+ {
39
+ "$ref": "#/$defs/CreateCustomerEndpointRequest"
40
+ },
41
+ {
42
+ "$ref": "#/$defs/CustomerEndpoint"
43
+ },
44
+ {
45
+ "$ref": "#/$defs/CustomerEndpointCollection"
46
+ },
47
+ {
48
+ "$ref": "#/$defs/CustomerEndpointMutationResult"
49
+ },
50
+ {
51
+ "$ref": "#/$defs/CustomerEndpointAttemptCollection"
52
+ },
53
+ {
54
+ "$ref": "#/$defs/CustomerEndpointVerificationChallenge"
55
+ },
56
+ {
57
+ "$ref": "#/$defs/CreateSubscriptionLinkRequest"
58
+ },
59
+ {
60
+ "$ref": "#/$defs/SubscriptionLink"
61
+ },
62
+ {
63
+ "$ref": "#/$defs/CreateMessageRequest"
64
+ },
65
+ {
66
+ "$ref": "#/$defs/Message"
67
+ },
68
+ {
69
+ "$ref": "#/$defs/Problem"
70
+ },
71
+ {
72
+ "$ref": "#/$defs/IdempotencyKey"
73
+ },
74
+ {
75
+ "$ref": "#/$defs/ProviderCapabilitiesCollection"
76
+ }
77
+ ],
78
+ "$defs": {
79
+ "Environment": {
80
+ "type": "string",
81
+ "enum": ["test", "live"],
82
+ "description": "The single environment selected by the authenticated credential."
83
+ },
84
+ "Channel": {
85
+ "type": "string",
86
+ "enum": ["telegram"],
87
+ "description": "The only channel accepted during C0. The vocabulary can grow additively."
88
+ },
89
+ "OpaqueIdentifier": {
90
+ "type": "string",
91
+ "minLength": 1,
92
+ "maxLength": 128,
93
+ "pattern": "^[^\\u0000-\\u001F\\u007F]+$",
94
+ "description": "An opaque identifier. Its shape, ordering, and embedded content are not public promises."
95
+ },
96
+ "DiagnosticId": {
97
+ "type": "string",
98
+ "minLength": 8,
99
+ "maxLength": 128,
100
+ "pattern": "^[A-Za-z0-9_-]+$",
101
+ "description": "A safe public identifier used to correlate protected service diagnostics."
102
+ },
103
+ "Timestamp": {
104
+ "type": "string",
105
+ "format": "date-time",
106
+ "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])$",
107
+ "description": "An RFC 3339 timestamp with a UTC offset. The server is authoritative."
108
+ },
109
+ "IdempotencyKey": {
110
+ "type": "string",
111
+ "minLength": 8,
112
+ "maxLength": 255,
113
+ "pattern": "^[!-~]+$",
114
+ "description": "Visible ASCII, scoped by authenticated environment, operation ID, and key, and retained for seven days from first acceptance."
115
+ },
116
+ "MetadataValue": {
117
+ "oneOf": [
118
+ {
119
+ "type": "string"
120
+ },
121
+ {
122
+ "type": "number"
123
+ },
124
+ {
125
+ "type": "boolean"
126
+ },
127
+ {
128
+ "type": "null"
129
+ }
130
+ ]
131
+ },
132
+ "Metadata": {
133
+ "type": "object",
134
+ "description": "Bounded customer correlation data. Do not include secrets or personal data.",
135
+ "maxProperties": 20,
136
+ "propertyNames": {
137
+ "minLength": 1,
138
+ "maxLength": 64
139
+ },
140
+ "additionalProperties": {
141
+ "$ref": "#/$defs/MetadataValue"
142
+ },
143
+ "x-max-json-bytes": 4096
144
+ },
145
+ "AccountRole": {
146
+ "type": "string",
147
+ "enum": ["admin", "member"],
148
+ "description": "The stable WhooshBang authorization role projected from the current Clerk organization membership."
149
+ },
150
+ "AccountMembershipSummary": {
151
+ "type": "object",
152
+ "required": ["role"],
153
+ "properties": {
154
+ "role": {
155
+ "$ref": "#/$defs/AccountRole"
156
+ }
157
+ },
158
+ "additionalProperties": false
159
+ },
160
+ "AccountProjection": {
161
+ "type": "object",
162
+ "description": "The current Clerk organization projected as a WhooshBang tenant. Clerk organization and user identifiers are not public fields.",
163
+ "required": [
164
+ "id",
165
+ "name",
166
+ "status",
167
+ "membership",
168
+ "created_at",
169
+ "updated_at",
170
+ "diagnostic_id"
171
+ ],
172
+ "properties": {
173
+ "id": {
174
+ "$ref": "#/$defs/OpaqueIdentifier"
175
+ },
176
+ "name": {
177
+ "type": "string",
178
+ "minLength": 1,
179
+ "maxLength": 100
180
+ },
181
+ "status": {
182
+ "type": "string",
183
+ "enum": ["active", "suspended"]
184
+ },
185
+ "membership": {
186
+ "$ref": "#/$defs/AccountMembershipSummary"
187
+ },
188
+ "created_at": {
189
+ "$ref": "#/$defs/Timestamp"
190
+ },
191
+ "updated_at": {
192
+ "$ref": "#/$defs/Timestamp"
193
+ },
194
+ "diagnostic_id": {
195
+ "$ref": "#/$defs/DiagnosticId"
196
+ }
197
+ },
198
+ "additionalProperties": false
199
+ },
200
+ "CreateProjectRequest": {
201
+ "type": "object",
202
+ "description": "Creates one project inside the account selected by the authenticated human session.",
203
+ "required": ["name", "slug"],
204
+ "properties": {
205
+ "name": {
206
+ "type": "string",
207
+ "minLength": 1,
208
+ "maxLength": 100
209
+ },
210
+ "slug": {
211
+ "type": "string",
212
+ "minLength": 1,
213
+ "maxLength": 63,
214
+ "pattern": "^[a-z0-9]+(?:-[a-z0-9]+)*$"
215
+ }
216
+ },
217
+ "additionalProperties": false
218
+ },
219
+ "DefaultNotifierSummary": {
220
+ "type": "object",
221
+ "description": "The default notifier created transactionally with one project environment.",
222
+ "required": [
223
+ "id",
224
+ "environment_id",
225
+ "display_name",
226
+ "status",
227
+ "is_default"
228
+ ],
229
+ "properties": {
230
+ "id": {
231
+ "$ref": "#/$defs/OpaqueIdentifier"
232
+ },
233
+ "environment_id": {
234
+ "$ref": "#/$defs/OpaqueIdentifier"
235
+ },
236
+ "display_name": {
237
+ "const": "Default"
238
+ },
239
+ "status": {
240
+ "type": "string",
241
+ "enum": ["active", "disabled"]
242
+ },
243
+ "is_default": {
244
+ "const": true
245
+ }
246
+ },
247
+ "additionalProperties": false
248
+ },
249
+ "ProjectEnvironment": {
250
+ "type": "object",
251
+ "description": "A test or live project environment with its transactionally created default notifier.",
252
+ "required": ["id", "project_id", "kind", "status", "default_notifier"],
253
+ "properties": {
254
+ "id": {
255
+ "$ref": "#/$defs/OpaqueIdentifier"
256
+ },
257
+ "project_id": {
258
+ "$ref": "#/$defs/OpaqueIdentifier"
259
+ },
260
+ "kind": {
261
+ "$ref": "#/$defs/Environment"
262
+ },
263
+ "status": {
264
+ "type": "string",
265
+ "enum": ["active", "disabled"]
266
+ },
267
+ "default_notifier": {
268
+ "$ref": "#/$defs/DefaultNotifierSummary"
269
+ }
270
+ },
271
+ "additionalProperties": false
272
+ },
273
+ "Project": {
274
+ "type": "object",
275
+ "description": "An account-scoped application project with its complete test/live bootstrap.",
276
+ "required": [
277
+ "id",
278
+ "account_id",
279
+ "name",
280
+ "slug",
281
+ "status",
282
+ "environments",
283
+ "created_at",
284
+ "updated_at",
285
+ "diagnostic_id"
286
+ ],
287
+ "properties": {
288
+ "id": {
289
+ "$ref": "#/$defs/OpaqueIdentifier"
290
+ },
291
+ "account_id": {
292
+ "$ref": "#/$defs/OpaqueIdentifier"
293
+ },
294
+ "name": {
295
+ "type": "string",
296
+ "minLength": 1,
297
+ "maxLength": 100
298
+ },
299
+ "slug": {
300
+ "type": "string",
301
+ "minLength": 1,
302
+ "maxLength": 63,
303
+ "pattern": "^[a-z0-9]+(?:-[a-z0-9]+)*$"
304
+ },
305
+ "status": {
306
+ "type": "string",
307
+ "enum": ["active", "disabled"]
308
+ },
309
+ "environments": {
310
+ "type": "array",
311
+ "minItems": 2,
312
+ "maxItems": 2,
313
+ "x-unique-property": "kind",
314
+ "items": {
315
+ "$ref": "#/$defs/ProjectEnvironment"
316
+ }
317
+ },
318
+ "created_at": {
319
+ "$ref": "#/$defs/Timestamp"
320
+ },
321
+ "updated_at": {
322
+ "$ref": "#/$defs/Timestamp"
323
+ },
324
+ "diagnostic_id": {
325
+ "$ref": "#/$defs/DiagnosticId"
326
+ }
327
+ },
328
+ "additionalProperties": false
329
+ },
330
+ "ProjectCollection": {
331
+ "type": "object",
332
+ "description": "A stable page of projects inside the authenticated account.",
333
+ "required": ["items"],
334
+ "properties": {
335
+ "items": {
336
+ "type": "array",
337
+ "maxItems": 100,
338
+ "items": {
339
+ "$ref": "#/$defs/Project"
340
+ }
341
+ },
342
+ "next_cursor": {
343
+ "type": "string",
344
+ "minLength": 1,
345
+ "maxLength": 256,
346
+ "pattern": "^[A-Za-z0-9_-]+$"
347
+ }
348
+ },
349
+ "additionalProperties": false
350
+ },
351
+ "ApiCredentialId": {
352
+ "type": "string",
353
+ "pattern": "^pcred_[A-Za-z0-9_-]{21}[AQgw]$",
354
+ "description": "pcred_ followed by the canonical unpadded base64url encoding of exactly 16 random bytes."
355
+ },
356
+ "ApiCredentialPrefix": {
357
+ "type": "string",
358
+ "pattern": "^wb_pc1\\.pcred_[A-Za-z0-9_-]{8}$",
359
+ "description": "A safe display prefix containing the credential format marker and first eight base64url identity characters, never secret bytes."
360
+ },
361
+ "ApiCredentialSecret": {
362
+ "type": "string",
363
+ "pattern": "^wb_pc1\\.pcred_[A-Za-z0-9_-]{21}[AQgw]\\.[A-Za-z0-9_-]{42}[AEIMQUYcgkosw048]$",
364
+ "description": "The exact one-time runtime bearer syntax. The final segment is canonical unpadded base64url for exactly 32 random secret bytes."
365
+ },
366
+ "ApiCredentialScope": {
367
+ "type": "string",
368
+ "enum": [
369
+ "messages:write",
370
+ "messages:read",
371
+ "subscriptions:write",
372
+ "subscriptions:read",
373
+ "machine-clients:write",
374
+ "machine-clients:read",
375
+ "capabilities:read"
376
+ ],
377
+ "description": "One project-credential capability. Environment authority always comes from the credential record, never a scope or request field."
378
+ },
379
+ "ApiCredentialScopeSet": {
380
+ "type": "array",
381
+ "minItems": 1,
382
+ "maxItems": 7,
383
+ "uniqueItems": true,
384
+ "items": {
385
+ "$ref": "#/$defs/ApiCredentialScope"
386
+ }
387
+ },
388
+ "CreateApiCredentialRequest": {
389
+ "type": "object",
390
+ "description": "Creates one credential inside the project environment selected by the authenticated administration path.",
391
+ "required": ["label", "scopes"],
392
+ "properties": {
393
+ "label": {
394
+ "type": "string",
395
+ "minLength": 1,
396
+ "maxLength": 100
397
+ },
398
+ "scopes": {
399
+ "$ref": "#/$defs/ApiCredentialScopeSet"
400
+ }
401
+ },
402
+ "additionalProperties": false
403
+ },
404
+ "RotateApiCredentialRequest": {
405
+ "type": "object",
406
+ "description": "Creates a replacement with the same label and scopes and schedules terminal revocation of the replaced credential after the declared overlap.",
407
+ "required": ["overlap_seconds"],
408
+ "properties": {
409
+ "overlap_seconds": {
410
+ "type": "integer",
411
+ "minimum": 0,
412
+ "maximum": 86400
413
+ }
414
+ },
415
+ "additionalProperties": false
416
+ },
417
+ "ApiCredential": {
418
+ "type": "object",
419
+ "description": "Safe environment-scoped API credential metadata. It never contains a bearer secret, digest, Clerk identifier, or digest-key reference.",
420
+ "required": [
421
+ "id",
422
+ "project_id",
423
+ "environment_id",
424
+ "environment",
425
+ "prefix",
426
+ "label",
427
+ "scopes",
428
+ "created_by",
429
+ "created_at",
430
+ "status"
431
+ ],
432
+ "properties": {
433
+ "id": {
434
+ "$ref": "#/$defs/ApiCredentialId"
435
+ },
436
+ "project_id": {
437
+ "$ref": "#/$defs/OpaqueIdentifier"
438
+ },
439
+ "environment_id": {
440
+ "$ref": "#/$defs/OpaqueIdentifier"
441
+ },
442
+ "environment": {
443
+ "$ref": "#/$defs/Environment"
444
+ },
445
+ "prefix": {
446
+ "$ref": "#/$defs/ApiCredentialPrefix"
447
+ },
448
+ "label": {
449
+ "type": "string",
450
+ "minLength": 1,
451
+ "maxLength": 100
452
+ },
453
+ "scopes": {
454
+ "$ref": "#/$defs/ApiCredentialScopeSet"
455
+ },
456
+ "created_by": {
457
+ "$ref": "#/$defs/OpaqueIdentifier",
458
+ "description": "An opaque WhooshBang member identifier. It is never a Clerk user identifier."
459
+ },
460
+ "created_at": {
461
+ "$ref": "#/$defs/Timestamp"
462
+ },
463
+ "last_used_at": {
464
+ "$ref": "#/$defs/Timestamp"
465
+ },
466
+ "scheduled_revocation_at": {
467
+ "$ref": "#/$defs/Timestamp",
468
+ "description": "When an active credential in a rotation overlap becomes terminally revoked."
469
+ },
470
+ "status": {
471
+ "type": "string",
472
+ "enum": ["active", "revoked"]
473
+ },
474
+ "revoked_at": {
475
+ "$ref": "#/$defs/Timestamp"
476
+ }
477
+ },
478
+ "allOf": [
479
+ {
480
+ "if": {
481
+ "properties": {
482
+ "status": {
483
+ "const": "revoked"
484
+ }
485
+ },
486
+ "required": ["status"]
487
+ },
488
+ "then": {
489
+ "required": ["revoked_at"],
490
+ "properties": {
491
+ "revoked_at": {},
492
+ "scheduled_revocation_at": false
493
+ }
494
+ },
495
+ "else": {
496
+ "properties": {
497
+ "revoked_at": false
498
+ }
499
+ }
500
+ }
501
+ ],
502
+ "additionalProperties": false
503
+ },
504
+ "ApiCredentialCollection": {
505
+ "type": "object",
506
+ "description": "A stable page of safe credential metadata in one project environment.",
507
+ "required": ["items"],
508
+ "properties": {
509
+ "items": {
510
+ "type": "array",
511
+ "maxItems": 100,
512
+ "items": {
513
+ "$ref": "#/$defs/ApiCredential"
514
+ }
515
+ },
516
+ "next_cursor": {
517
+ "type": "string",
518
+ "minLength": 1,
519
+ "maxLength": 256,
520
+ "pattern": "^[A-Za-z0-9_-]+$"
521
+ }
522
+ },
523
+ "additionalProperties": false
524
+ },
525
+ "ApiCredentialSecretReveal": {
526
+ "type": "object",
527
+ "description": "The creation-only response. The secret is emitted in this response exactly once and is never available from list, rotate-target, revoke, or later read responses.",
528
+ "required": ["credential", "secret"],
529
+ "properties": {
530
+ "credential": {
531
+ "$ref": "#/$defs/ApiCredential"
532
+ },
533
+ "secret": {
534
+ "$ref": "#/$defs/ApiCredentialSecret"
535
+ }
536
+ },
537
+ "additionalProperties": false
538
+ },
539
+ "ApiCredentialRotation": {
540
+ "type": "object",
541
+ "description": "A creation-only replacement secret plus safe metadata for the credential it replaces.",
542
+ "required": ["credential", "replaced_credential"],
543
+ "properties": {
544
+ "credential": {
545
+ "$ref": "#/$defs/ApiCredentialSecretReveal"
546
+ },
547
+ "replaced_credential": {
548
+ "$ref": "#/$defs/ApiCredential"
549
+ }
550
+ },
551
+ "additionalProperties": false
552
+ },
553
+ "BindingSummary": {
554
+ "type": "object",
555
+ "description": "A safe channel binding identity. Raw Telegram chat and user identifiers are never exposed.",
556
+ "required": [
557
+ "id",
558
+ "project_id",
559
+ "environment",
560
+ "subscriber_id",
561
+ "notifier_id",
562
+ "channel",
563
+ "status"
564
+ ],
565
+ "properties": {
566
+ "id": {
567
+ "$ref": "#/$defs/OpaqueIdentifier"
568
+ },
569
+ "project_id": {
570
+ "$ref": "#/$defs/OpaqueIdentifier"
571
+ },
572
+ "environment": {
573
+ "$ref": "#/$defs/Environment"
574
+ },
575
+ "subscriber_id": {
576
+ "$ref": "#/$defs/OpaqueIdentifier"
577
+ },
578
+ "notifier_id": {
579
+ "$ref": "#/$defs/OpaqueIdentifier"
580
+ },
581
+ "channel": {
582
+ "$ref": "#/$defs/Channel"
583
+ },
584
+ "status": {
585
+ "type": "string",
586
+ "enum": ["active", "paused", "revoked"]
587
+ }
588
+ },
589
+ "additionalProperties": false
590
+ },
591
+ "CreateSubscriptionLinkRequest": {
592
+ "type": "object",
593
+ "description": "Creates one project- and environment-scoped, single-use authorization link.",
594
+ "required": ["subscriber_id", "channels"],
595
+ "properties": {
596
+ "subscriber_id": {
597
+ "$ref": "#/$defs/OpaqueIdentifier"
598
+ },
599
+ "notifier_id": {
600
+ "allOf": [
601
+ {
602
+ "$ref": "#/$defs/OpaqueIdentifier"
603
+ }
604
+ ],
605
+ "default": "default"
606
+ },
607
+ "channels": {
608
+ "type": "array",
609
+ "minItems": 1,
610
+ "maxItems": 1,
611
+ "uniqueItems": true,
612
+ "items": {
613
+ "$ref": "#/$defs/Channel"
614
+ }
615
+ },
616
+ "return_url": {
617
+ "type": "string",
618
+ "format": "uri",
619
+ "pattern": "^https?://[^\\s]+$",
620
+ "anyOf": [
621
+ {
622
+ "type": "string",
623
+ "pattern": "^https://"
624
+ },
625
+ {
626
+ "type": "string",
627
+ "pattern": "^http://[^/?#\\s]*\\.test(?::[0-9]{1,5})?(?:[/?#]|$)"
628
+ }
629
+ ],
630
+ "maxLength": 2048,
631
+ "description": "Untrusted redirect input. HTTPS is required outside synthetic .test fixtures; the service never fetches this URL."
632
+ },
633
+ "expires_in_seconds": {
634
+ "type": "integer",
635
+ "minimum": 60,
636
+ "maximum": 86400,
637
+ "default": 900
638
+ }
639
+ },
640
+ "additionalProperties": false
641
+ },
642
+ "ClassifiedError": {
643
+ "type": "object",
644
+ "description": "A stable, safe error classification with no provider response or credential material.",
645
+ "required": ["code", "retryable"],
646
+ "properties": {
647
+ "code": {
648
+ "$ref": "#/$defs/ProblemCode"
649
+ },
650
+ "retryable": {
651
+ "type": "boolean"
652
+ },
653
+ "detail": {
654
+ "type": "string",
655
+ "minLength": 1,
656
+ "maxLength": 1024
657
+ },
658
+ "diagnostic_id": {
659
+ "$ref": "#/$defs/DiagnosticId"
660
+ }
661
+ },
662
+ "additionalProperties": false
663
+ },
664
+ "SubscriptionLink": {
665
+ "type": "object",
666
+ "description": "A public subscription-link resource scoped to the authenticated project and environment.",
667
+ "required": [
668
+ "id",
669
+ "project_id",
670
+ "environment",
671
+ "subscriber_id",
672
+ "notifier_id",
673
+ "channels",
674
+ "status",
675
+ "created_at",
676
+ "updated_at",
677
+ "expires_at",
678
+ "diagnostic_id"
679
+ ],
680
+ "properties": {
681
+ "id": {
682
+ "$ref": "#/$defs/OpaqueIdentifier"
683
+ },
684
+ "project_id": {
685
+ "$ref": "#/$defs/OpaqueIdentifier"
686
+ },
687
+ "environment": {
688
+ "$ref": "#/$defs/Environment"
689
+ },
690
+ "subscriber_id": {
691
+ "$ref": "#/$defs/OpaqueIdentifier"
692
+ },
693
+ "notifier_id": {
694
+ "$ref": "#/$defs/OpaqueIdentifier"
695
+ },
696
+ "channels": {
697
+ "type": "array",
698
+ "minItems": 1,
699
+ "maxItems": 1,
700
+ "uniqueItems": true,
701
+ "items": {
702
+ "$ref": "#/$defs/Channel"
703
+ }
704
+ },
705
+ "status": {
706
+ "type": "string",
707
+ "enum": [
708
+ "pending",
709
+ "activated",
710
+ "expired",
711
+ "cancelled",
712
+ "failed",
713
+ "revoked"
714
+ ]
715
+ },
716
+ "authorization_url": {
717
+ "type": "string",
718
+ "format": "uri",
719
+ "pattern": "^https://[^\\s]+$",
720
+ "maxLength": 2048,
721
+ "description": "An opaque authorization URL. It contains no project or provider secret."
722
+ },
723
+ "binding": {
724
+ "$ref": "#/$defs/BindingSummary"
725
+ },
726
+ "created_at": {
727
+ "$ref": "#/$defs/Timestamp"
728
+ },
729
+ "updated_at": {
730
+ "$ref": "#/$defs/Timestamp"
731
+ },
732
+ "expires_at": {
733
+ "$ref": "#/$defs/Timestamp"
734
+ },
735
+ "diagnostic_id": {
736
+ "$ref": "#/$defs/DiagnosticId"
737
+ },
738
+ "classified_error": {
739
+ "$ref": "#/$defs/ClassifiedError"
740
+ },
741
+ "cancellation_effective": {
742
+ "type": "boolean",
743
+ "description": "Present on cancellation responses; false never claims provider-side deletion."
744
+ }
745
+ },
746
+ "allOf": [
747
+ {
748
+ "if": {
749
+ "type": "object",
750
+ "properties": {
751
+ "status": {
752
+ "const": "pending"
753
+ }
754
+ },
755
+ "required": ["status"]
756
+ },
757
+ "then": {
758
+ "type": "object",
759
+ "required": ["authorization_url"],
760
+ "properties": {
761
+ "authorization_url": {}
762
+ }
763
+ }
764
+ },
765
+ {
766
+ "if": {
767
+ "type": "object",
768
+ "properties": {
769
+ "status": {
770
+ "enum": ["activated", "revoked"]
771
+ }
772
+ },
773
+ "required": ["status"]
774
+ },
775
+ "then": {
776
+ "type": "object",
777
+ "required": ["binding"],
778
+ "properties": {
779
+ "binding": {}
780
+ }
781
+ }
782
+ },
783
+ {
784
+ "if": {
785
+ "type": "object",
786
+ "properties": {
787
+ "status": {
788
+ "const": "failed"
789
+ }
790
+ },
791
+ "required": ["status"]
792
+ },
793
+ "then": {
794
+ "type": "object",
795
+ "required": ["classified_error"],
796
+ "properties": {
797
+ "classified_error": {}
798
+ }
799
+ }
800
+ }
801
+ ],
802
+ "additionalProperties": false
803
+ },
804
+ "MessageTarget": {
805
+ "type": "object",
806
+ "required": ["subscriber_id"],
807
+ "properties": {
808
+ "subscriber_id": {
809
+ "$ref": "#/$defs/OpaqueIdentifier"
810
+ }
811
+ },
812
+ "additionalProperties": false
813
+ },
814
+ "TextContent": {
815
+ "type": "object",
816
+ "required": ["type", "text"],
817
+ "properties": {
818
+ "type": {
819
+ "const": "text"
820
+ },
821
+ "text": {
822
+ "type": "string",
823
+ "minLength": 1,
824
+ "maxLength": 8192,
825
+ "x-max-utf8-bytes": 8192
826
+ }
827
+ },
828
+ "additionalProperties": false
829
+ },
830
+ "ProviderCapabilitiesCollection": {
831
+ "type": "object",
832
+ "required": ["schema", "providers"],
833
+ "properties": {
834
+ "schema": {
835
+ "const": "whooshbang.provider-capabilities-collection.v1"
836
+ },
837
+ "providers": {
838
+ "type": "array",
839
+ "minItems": 1,
840
+ "items": {
841
+ "$ref": "provider-capabilities-v1.schema.json"
842
+ },
843
+ "x-unique-property": "provider"
844
+ }
845
+ },
846
+ "additionalProperties": false
847
+ },
848
+ "CustomerEventType": {
849
+ "type": "string",
850
+ "enum": [
851
+ "subscription.activated",
852
+ "subscription.revoked",
853
+ "message.provider_accepted",
854
+ "message.delivered",
855
+ "message.failed",
856
+ "message.expired",
857
+ "interaction.received",
858
+ "interaction.expired",
859
+ "agent_binding.degraded"
860
+ ]
861
+ },
862
+ "CustomerEndpointStatus": {
863
+ "type": "string",
864
+ "enum": ["unverified", "active", "paused", "disabled"]
865
+ },
866
+ "CreateCustomerEndpointRequest": {
867
+ "type": "object",
868
+ "description": "Creates one project/environment endpoint. The service validates the final normalized destination and never returns it from health or diagnostic reads.",
869
+ "required": ["url", "event_types"],
870
+ "properties": {
871
+ "url": {
872
+ "type": "string",
873
+ "format": "uri",
874
+ "minLength": 8,
875
+ "maxLength": 2048,
876
+ "pattern": "^https://[^\\s]+$"
877
+ },
878
+ "event_types": {
879
+ "type": "array",
880
+ "minItems": 1,
881
+ "maxItems": 9,
882
+ "uniqueItems": true,
883
+ "items": {
884
+ "$ref": "#/$defs/CustomerEventType"
885
+ }
886
+ },
887
+ "local_test": {
888
+ "type": "boolean",
889
+ "default": false,
890
+ "description": "Explicit local-tooling marker. Production deployments reject true even though local test tooling accepts loopback HTTP before persistence."
891
+ }
892
+ },
893
+ "additionalProperties": false
894
+ },
895
+ "RotateCustomerEndpointRequest": {
896
+ "type": "object",
897
+ "required": ["overlap_seconds"],
898
+ "properties": {
899
+ "overlap_seconds": {
900
+ "type": "integer",
901
+ "minimum": 60,
902
+ "maximum": 86400
903
+ }
904
+ },
905
+ "additionalProperties": false
906
+ },
907
+ "CustomerEndpointFailure": {
908
+ "type": "object",
909
+ "required": ["at", "category"],
910
+ "properties": {
911
+ "at": {
912
+ "$ref": "#/$defs/Timestamp"
913
+ },
914
+ "category": {
915
+ "type": "string",
916
+ "minLength": 1,
917
+ "maxLength": 64,
918
+ "pattern": "^[a-z0-9_]+$"
919
+ }
920
+ },
921
+ "additionalProperties": false
922
+ },
923
+ "CustomerEndpoint": {
924
+ "type": "object",
925
+ "description": "Safe endpoint lifecycle and health metadata. URL, signing material, event bodies, answers, and response bodies are structurally absent.",
926
+ "required": [
927
+ "id",
928
+ "project_id",
929
+ "environment_id",
930
+ "environment",
931
+ "status",
932
+ "verification_status",
933
+ "event_types",
934
+ "secret_version",
935
+ "health",
936
+ "created_at",
937
+ "updated_at",
938
+ "diagnostic_id"
939
+ ],
940
+ "properties": {
941
+ "id": {
942
+ "$ref": "#/$defs/OpaqueIdentifier"
943
+ },
944
+ "project_id": {
945
+ "$ref": "#/$defs/OpaqueIdentifier"
946
+ },
947
+ "environment_id": {
948
+ "$ref": "#/$defs/OpaqueIdentifier"
949
+ },
950
+ "environment": {
951
+ "$ref": "#/$defs/Environment"
952
+ },
953
+ "status": {
954
+ "$ref": "#/$defs/CustomerEndpointStatus"
955
+ },
956
+ "verification_status": {
957
+ "type": "string",
958
+ "enum": ["pending", "failed", "verified"]
959
+ },
960
+ "event_types": {
961
+ "type": "array",
962
+ "minItems": 1,
963
+ "maxItems": 9,
964
+ "uniqueItems": true,
965
+ "items": {
966
+ "$ref": "#/$defs/CustomerEventType"
967
+ }
968
+ },
969
+ "secret_version": {
970
+ "type": "integer",
971
+ "minimum": 1
972
+ },
973
+ "health": {
974
+ "type": "string",
975
+ "enum": ["never_delivered", "healthy", "failing"]
976
+ },
977
+ "created_at": {
978
+ "$ref": "#/$defs/Timestamp"
979
+ },
980
+ "updated_at": {
981
+ "$ref": "#/$defs/Timestamp"
982
+ },
983
+ "verification_attempted_at": {
984
+ "$ref": "#/$defs/Timestamp"
985
+ },
986
+ "verified_at": {
987
+ "$ref": "#/$defs/Timestamp"
988
+ },
989
+ "rotated_at": {
990
+ "$ref": "#/$defs/Timestamp"
991
+ },
992
+ "paused_at": {
993
+ "$ref": "#/$defs/Timestamp"
994
+ },
995
+ "disabled_at": {
996
+ "$ref": "#/$defs/Timestamp"
997
+ },
998
+ "last_success_at": {
999
+ "$ref": "#/$defs/Timestamp"
1000
+ },
1001
+ "last_failure": {
1002
+ "$ref": "#/$defs/CustomerEndpointFailure"
1003
+ },
1004
+ "diagnostic_id": {
1005
+ "$ref": "#/$defs/DiagnosticId"
1006
+ }
1007
+ },
1008
+ "additionalProperties": false
1009
+ },
1010
+ "CustomerEndpointCollection": {
1011
+ "type": "object",
1012
+ "required": ["items"],
1013
+ "properties": {
1014
+ "items": {
1015
+ "type": "array",
1016
+ "maxItems": 100,
1017
+ "items": {
1018
+ "$ref": "#/$defs/CustomerEndpoint"
1019
+ }
1020
+ }
1021
+ },
1022
+ "additionalProperties": false
1023
+ },
1024
+ "CustomerEndpointSecret": {
1025
+ "type": "string",
1026
+ "pattern": "^whsec_[A-Za-z0-9+/]{43}=$",
1027
+ "description": "Exactly 32 random bytes in the Standard Webhooks secret format. Returned only by the first successful create or rotate response."
1028
+ },
1029
+ "CustomerEndpointMutationResult": {
1030
+ "type": "object",
1031
+ "required": ["endpoint"],
1032
+ "properties": {
1033
+ "endpoint": {
1034
+ "$ref": "#/$defs/CustomerEndpoint"
1035
+ },
1036
+ "secret": {
1037
+ "$ref": "#/$defs/CustomerEndpointSecret"
1038
+ }
1039
+ },
1040
+ "additionalProperties": false,
1041
+ "description": "The initial create/rotate result includes secret. An equivalent idempotent replay returns the same logical endpoint without revealing the secret again."
1042
+ },
1043
+ "CustomerEndpointAttemptDiagnostic": {
1044
+ "type": "object",
1045
+ "description": "Content-free per-attempt timing and failure classification.",
1046
+ "required": ["id", "event_id", "attempt", "status", "created_at"],
1047
+ "properties": {
1048
+ "id": {
1049
+ "$ref": "#/$defs/OpaqueIdentifier"
1050
+ },
1051
+ "event_id": {
1052
+ "$ref": "#/$defs/OpaqueIdentifier"
1053
+ },
1054
+ "attempt": {
1055
+ "type": "integer",
1056
+ "minimum": 1,
1057
+ "maximum": 8
1058
+ },
1059
+ "status": {
1060
+ "type": "string",
1061
+ "enum": [
1062
+ "pending",
1063
+ "attempting",
1064
+ "delivered",
1065
+ "retry_wait",
1066
+ "terminal_failed",
1067
+ "paused"
1068
+ ]
1069
+ },
1070
+ "diagnostic_code": {
1071
+ "type": "string",
1072
+ "minLength": 1,
1073
+ "maxLength": 64,
1074
+ "pattern": "^[a-z0-9_]+$"
1075
+ },
1076
+ "http_status": {
1077
+ "type": "integer",
1078
+ "minimum": 100,
1079
+ "maximum": 599
1080
+ },
1081
+ "started_at": {
1082
+ "$ref": "#/$defs/Timestamp"
1083
+ },
1084
+ "completed_at": {
1085
+ "$ref": "#/$defs/Timestamp"
1086
+ },
1087
+ "created_at": {
1088
+ "$ref": "#/$defs/Timestamp"
1089
+ }
1090
+ },
1091
+ "additionalProperties": false
1092
+ },
1093
+ "CustomerEndpointAttemptCollection": {
1094
+ "type": "object",
1095
+ "required": ["endpoint_id", "attempts"],
1096
+ "properties": {
1097
+ "endpoint_id": {
1098
+ "$ref": "#/$defs/OpaqueIdentifier"
1099
+ },
1100
+ "attempts": {
1101
+ "type": "array",
1102
+ "maxItems": 100,
1103
+ "items": {
1104
+ "$ref": "#/$defs/CustomerEndpointAttemptDiagnostic"
1105
+ }
1106
+ }
1107
+ },
1108
+ "additionalProperties": false
1109
+ },
1110
+ "CustomerEndpointVerificationChallenge": {
1111
+ "type": "object",
1112
+ "required": ["schema", "endpoint_id", "challenge"],
1113
+ "properties": {
1114
+ "schema": {
1115
+ "const": "whooshbang.endpoint-verification.v1"
1116
+ },
1117
+ "endpoint_id": {
1118
+ "$ref": "#/$defs/OpaqueIdentifier"
1119
+ },
1120
+ "challenge": {
1121
+ "type": "string",
1122
+ "pattern": "^[A-Za-z0-9+/]{43}=$"
1123
+ }
1124
+ },
1125
+ "additionalProperties": false,
1126
+ "description": "Exact signed request bytes. A verifier echoes these exact bytes with valid webhook headers to prove it possesses the endpoint secret."
1127
+ },
1128
+ "CreateMessageRequest": {
1129
+ "type": "object",
1130
+ "description": "Creates one logical message for one subscriber and one resolved channel binding.",
1131
+ "required": ["to", "content"],
1132
+ "properties": {
1133
+ "to": {
1134
+ "$ref": "#/$defs/MessageTarget"
1135
+ },
1136
+ "notifier_id": {
1137
+ "allOf": [
1138
+ {
1139
+ "$ref": "#/$defs/OpaqueIdentifier"
1140
+ }
1141
+ ],
1142
+ "default": "default"
1143
+ },
1144
+ "content": {
1145
+ "$ref": "#/$defs/TextContent"
1146
+ },
1147
+ "expires_at": {
1148
+ "$ref": "#/$defs/Timestamp",
1149
+ "description": "Optional server-authoritative expiry no more than 24 hours after acceptance."
1150
+ },
1151
+ "correlation_id": {
1152
+ "type": "string",
1153
+ "minLength": 1,
1154
+ "maxLength": 128
1155
+ },
1156
+ "metadata": {
1157
+ "$ref": "#/$defs/Metadata"
1158
+ },
1159
+ "interaction": {
1160
+ "$ref": "interaction-v1.schema.json#/$defs/InteractionRequest"
1161
+ }
1162
+ },
1163
+ "additionalProperties": false
1164
+ },
1165
+ "HighestProvenProviderState": {
1166
+ "type": "string",
1167
+ "enum": ["none", "provider_accepted"],
1168
+ "description": "The strongest provider assertion the service can prove; it is not an end-device delivery receipt."
1169
+ },
1170
+ "DeliveryAttemptSummary": {
1171
+ "type": "object",
1172
+ "description": "A safe aggregate of delivery attempts. Protected provider responses are excluded.",
1173
+ "required": [
1174
+ "attempt_count",
1175
+ "highest_proven_provider_state",
1176
+ "retry_scheduled"
1177
+ ],
1178
+ "properties": {
1179
+ "attempt_count": {
1180
+ "type": "integer",
1181
+ "minimum": 0
1182
+ },
1183
+ "highest_proven_provider_state": {
1184
+ "$ref": "#/$defs/HighestProvenProviderState"
1185
+ },
1186
+ "retry_scheduled": {
1187
+ "type": "boolean"
1188
+ },
1189
+ "retry_at": {
1190
+ "$ref": "#/$defs/Timestamp"
1191
+ },
1192
+ "classified_error": {
1193
+ "$ref": "#/$defs/ClassifiedError"
1194
+ }
1195
+ },
1196
+ "allOf": [
1197
+ {
1198
+ "if": {
1199
+ "type": "object",
1200
+ "properties": {
1201
+ "retry_scheduled": {
1202
+ "const": true
1203
+ }
1204
+ },
1205
+ "required": ["retry_scheduled"]
1206
+ },
1207
+ "then": {
1208
+ "type": "object",
1209
+ "required": ["retry_at"],
1210
+ "properties": {
1211
+ "retry_at": {}
1212
+ }
1213
+ },
1214
+ "else": {
1215
+ "type": "object",
1216
+ "properties": {
1217
+ "retry_at": false
1218
+ }
1219
+ }
1220
+ }
1221
+ ],
1222
+ "additionalProperties": false
1223
+ },
1224
+ "MessageState": {
1225
+ "type": "string",
1226
+ "enum": [
1227
+ "accepted",
1228
+ "dispatch_pending",
1229
+ "queued",
1230
+ "sending",
1231
+ "provider_accepted",
1232
+ "retry_wait",
1233
+ "terminal_failed",
1234
+ "outcome_unknown",
1235
+ "cancelled",
1236
+ "expired"
1237
+ ]
1238
+ },
1239
+ "Message": {
1240
+ "type": "object",
1241
+ "description": "A project- and environment-scoped logical message and its highest proven delivery outcome.",
1242
+ "required": [
1243
+ "id",
1244
+ "project_id",
1245
+ "environment",
1246
+ "subscriber_id",
1247
+ "notifier_id",
1248
+ "binding",
1249
+ "state",
1250
+ "accepted_at",
1251
+ "updated_at",
1252
+ "expires_at",
1253
+ "delivery",
1254
+ "diagnostic_id"
1255
+ ],
1256
+ "properties": {
1257
+ "id": {
1258
+ "$ref": "#/$defs/OpaqueIdentifier"
1259
+ },
1260
+ "project_id": {
1261
+ "$ref": "#/$defs/OpaqueIdentifier"
1262
+ },
1263
+ "environment": {
1264
+ "$ref": "#/$defs/Environment"
1265
+ },
1266
+ "subscriber_id": {
1267
+ "$ref": "#/$defs/OpaqueIdentifier"
1268
+ },
1269
+ "notifier_id": {
1270
+ "$ref": "#/$defs/OpaqueIdentifier"
1271
+ },
1272
+ "binding": {
1273
+ "$ref": "#/$defs/BindingSummary"
1274
+ },
1275
+ "state": {
1276
+ "$ref": "#/$defs/MessageState"
1277
+ },
1278
+ "accepted_at": {
1279
+ "$ref": "#/$defs/Timestamp"
1280
+ },
1281
+ "updated_at": {
1282
+ "$ref": "#/$defs/Timestamp"
1283
+ },
1284
+ "expires_at": {
1285
+ "$ref": "#/$defs/Timestamp"
1286
+ },
1287
+ "delivery": {
1288
+ "$ref": "#/$defs/DeliveryAttemptSummary"
1289
+ },
1290
+ "diagnostic_id": {
1291
+ "$ref": "#/$defs/DiagnosticId"
1292
+ },
1293
+ "correlation_id": {
1294
+ "type": "string",
1295
+ "minLength": 1,
1296
+ "maxLength": 128
1297
+ },
1298
+ "metadata": {
1299
+ "$ref": "#/$defs/Metadata"
1300
+ },
1301
+ "interaction": {
1302
+ "$ref": "interaction-v1.schema.json#/$defs/InteractionSummary"
1303
+ },
1304
+ "cancellation_effective": {
1305
+ "type": "boolean",
1306
+ "description": "Present on cancellation responses. False means the provider call was already owned and no deletion is claimed."
1307
+ },
1308
+ "outcome_guidance": {
1309
+ "type": "string",
1310
+ "const": "Delivery may have been accepted by the provider. No automatic retry is scheduled; create a new message explicitly if another attempt is appropriate."
1311
+ }
1312
+ },
1313
+ "allOf": [
1314
+ {
1315
+ "if": {
1316
+ "type": "object",
1317
+ "properties": {
1318
+ "state": {
1319
+ "const": "outcome_unknown"
1320
+ }
1321
+ },
1322
+ "required": ["state"]
1323
+ },
1324
+ "then": {
1325
+ "type": "object",
1326
+ "required": ["outcome_guidance"],
1327
+ "properties": {
1328
+ "outcome_guidance": {},
1329
+ "delivery": {
1330
+ "allOf": [
1331
+ {
1332
+ "$ref": "#/$defs/DeliveryAttemptSummary"
1333
+ },
1334
+ {
1335
+ "type": "object",
1336
+ "required": ["classified_error"],
1337
+ "properties": {
1338
+ "attempt_count": {
1339
+ "type": "integer",
1340
+ "minimum": 1
1341
+ },
1342
+ "highest_proven_provider_state": {
1343
+ "const": "none"
1344
+ },
1345
+ "retry_scheduled": {
1346
+ "const": false
1347
+ },
1348
+ "classified_error": {
1349
+ "allOf": [
1350
+ {
1351
+ "$ref": "#/$defs/ClassifiedError"
1352
+ },
1353
+ {
1354
+ "type": "object",
1355
+ "properties": {
1356
+ "code": {
1357
+ "const": "provider_outcome_unknown"
1358
+ },
1359
+ "retryable": {
1360
+ "const": false
1361
+ }
1362
+ }
1363
+ }
1364
+ ]
1365
+ }
1366
+ }
1367
+ }
1368
+ ]
1369
+ }
1370
+ }
1371
+ }
1372
+ },
1373
+ {
1374
+ "if": {
1375
+ "type": "object",
1376
+ "properties": {
1377
+ "state": {
1378
+ "enum": [
1379
+ "accepted",
1380
+ "dispatch_pending",
1381
+ "queued",
1382
+ "sending",
1383
+ "provider_accepted",
1384
+ "cancelled",
1385
+ "expired"
1386
+ ]
1387
+ }
1388
+ },
1389
+ "required": ["state"]
1390
+ },
1391
+ "then": {
1392
+ "type": "object",
1393
+ "properties": {
1394
+ "delivery": {
1395
+ "allOf": [
1396
+ {
1397
+ "$ref": "#/$defs/DeliveryAttemptSummary"
1398
+ },
1399
+ {
1400
+ "type": "object",
1401
+ "properties": {
1402
+ "classified_error": false
1403
+ }
1404
+ }
1405
+ ]
1406
+ }
1407
+ }
1408
+ }
1409
+ },
1410
+ {
1411
+ "if": {
1412
+ "type": "object",
1413
+ "properties": {
1414
+ "state": {
1415
+ "not": {
1416
+ "const": "provider_accepted"
1417
+ }
1418
+ }
1419
+ },
1420
+ "required": ["state"]
1421
+ },
1422
+ "then": {
1423
+ "type": "object",
1424
+ "properties": {
1425
+ "delivery": {
1426
+ "allOf": [
1427
+ {
1428
+ "$ref": "#/$defs/DeliveryAttemptSummary"
1429
+ },
1430
+ {
1431
+ "type": "object",
1432
+ "properties": {
1433
+ "highest_proven_provider_state": {
1434
+ "const": "none"
1435
+ }
1436
+ }
1437
+ }
1438
+ ]
1439
+ }
1440
+ }
1441
+ }
1442
+ },
1443
+ {
1444
+ "if": {
1445
+ "type": "object",
1446
+ "properties": {
1447
+ "state": {
1448
+ "not": {
1449
+ "const": "retry_wait"
1450
+ }
1451
+ }
1452
+ },
1453
+ "required": ["state"]
1454
+ },
1455
+ "then": {
1456
+ "type": "object",
1457
+ "properties": {
1458
+ "delivery": {
1459
+ "allOf": [
1460
+ {
1461
+ "$ref": "#/$defs/DeliveryAttemptSummary"
1462
+ },
1463
+ {
1464
+ "type": "object",
1465
+ "properties": {
1466
+ "retry_scheduled": {
1467
+ "const": false
1468
+ }
1469
+ }
1470
+ }
1471
+ ]
1472
+ }
1473
+ }
1474
+ }
1475
+ },
1476
+ {
1477
+ "if": {
1478
+ "type": "object",
1479
+ "properties": {
1480
+ "state": {
1481
+ "const": "provider_accepted"
1482
+ }
1483
+ },
1484
+ "required": ["state"]
1485
+ },
1486
+ "then": {
1487
+ "type": "object",
1488
+ "properties": {
1489
+ "delivery": {
1490
+ "allOf": [
1491
+ {
1492
+ "$ref": "#/$defs/DeliveryAttemptSummary"
1493
+ },
1494
+ {
1495
+ "type": "object",
1496
+ "properties": {
1497
+ "attempt_count": {
1498
+ "type": "integer",
1499
+ "minimum": 1
1500
+ },
1501
+ "highest_proven_provider_state": {
1502
+ "const": "provider_accepted"
1503
+ }
1504
+ }
1505
+ }
1506
+ ]
1507
+ }
1508
+ }
1509
+ }
1510
+ },
1511
+ {
1512
+ "if": {
1513
+ "type": "object",
1514
+ "properties": {
1515
+ "state": {
1516
+ "const": "retry_wait"
1517
+ }
1518
+ },
1519
+ "required": ["state"]
1520
+ },
1521
+ "then": {
1522
+ "type": "object",
1523
+ "properties": {
1524
+ "delivery": {
1525
+ "allOf": [
1526
+ {
1527
+ "$ref": "#/$defs/DeliveryAttemptSummary"
1528
+ },
1529
+ {
1530
+ "type": "object",
1531
+ "required": ["classified_error"],
1532
+ "properties": {
1533
+ "attempt_count": {
1534
+ "type": "integer",
1535
+ "minimum": 1
1536
+ },
1537
+ "retry_scheduled": {
1538
+ "const": true
1539
+ },
1540
+ "classified_error": {
1541
+ "allOf": [
1542
+ {
1543
+ "$ref": "#/$defs/ClassifiedError"
1544
+ },
1545
+ {
1546
+ "type": "object",
1547
+ "properties": {
1548
+ "code": {
1549
+ "const": "provider_retryable"
1550
+ },
1551
+ "retryable": {
1552
+ "const": true
1553
+ }
1554
+ }
1555
+ }
1556
+ ]
1557
+ }
1558
+ }
1559
+ }
1560
+ ]
1561
+ }
1562
+ }
1563
+ }
1564
+ },
1565
+ {
1566
+ "if": {
1567
+ "type": "object",
1568
+ "properties": {
1569
+ "state": {
1570
+ "const": "terminal_failed"
1571
+ }
1572
+ },
1573
+ "required": ["state"]
1574
+ },
1575
+ "then": {
1576
+ "type": "object",
1577
+ "properties": {
1578
+ "delivery": {
1579
+ "allOf": [
1580
+ {
1581
+ "$ref": "#/$defs/DeliveryAttemptSummary"
1582
+ },
1583
+ {
1584
+ "type": "object",
1585
+ "required": ["classified_error"],
1586
+ "properties": {
1587
+ "attempt_count": {
1588
+ "type": "integer",
1589
+ "minimum": 1
1590
+ },
1591
+ "classified_error": {
1592
+ "allOf": [
1593
+ {
1594
+ "$ref": "#/$defs/ClassifiedError"
1595
+ },
1596
+ {
1597
+ "type": "object",
1598
+ "properties": {
1599
+ "code": {
1600
+ "const": "provider_terminal"
1601
+ },
1602
+ "retryable": {
1603
+ "const": false
1604
+ }
1605
+ }
1606
+ }
1607
+ ]
1608
+ }
1609
+ }
1610
+ }
1611
+ ]
1612
+ }
1613
+ }
1614
+ }
1615
+ },
1616
+ {
1617
+ "if": {
1618
+ "type": "object",
1619
+ "properties": {
1620
+ "state": {
1621
+ "const": "sending"
1622
+ }
1623
+ },
1624
+ "required": ["state"]
1625
+ },
1626
+ "then": {
1627
+ "type": "object",
1628
+ "properties": {
1629
+ "delivery": {
1630
+ "allOf": [
1631
+ {
1632
+ "$ref": "#/$defs/DeliveryAttemptSummary"
1633
+ },
1634
+ {
1635
+ "type": "object",
1636
+ "properties": {
1637
+ "attempt_count": {
1638
+ "type": "integer",
1639
+ "minimum": 1
1640
+ }
1641
+ }
1642
+ }
1643
+ ]
1644
+ }
1645
+ }
1646
+ }
1647
+ },
1648
+ {
1649
+ "if": {
1650
+ "type": "object",
1651
+ "properties": {
1652
+ "state": {
1653
+ "enum": ["sending", "provider_accepted", "outcome_unknown"]
1654
+ },
1655
+ "cancellation_effective": {
1656
+ "type": "boolean"
1657
+ }
1658
+ },
1659
+ "required": ["state", "cancellation_effective"]
1660
+ },
1661
+ "then": {
1662
+ "type": "object",
1663
+ "properties": {
1664
+ "cancellation_effective": {
1665
+ "const": false
1666
+ }
1667
+ }
1668
+ }
1669
+ }
1670
+ ],
1671
+ "additionalProperties": false
1672
+ },
1673
+ "ProblemCode": {
1674
+ "type": "string",
1675
+ "enum": [
1676
+ "authentication_required",
1677
+ "credential_invalid",
1678
+ "scope_forbidden",
1679
+ "environment_mismatch",
1680
+ "idempotency_key_required",
1681
+ "idempotency_conflict",
1682
+ "project_slug_conflict",
1683
+ "request_invalid",
1684
+ "replay_unavailable",
1685
+ "subscriber_unbound",
1686
+ "resource_not_found",
1687
+ "resource_expired",
1688
+ "cancellation_too_late",
1689
+ "endpoint_rotation_conflict",
1690
+ "rate_limited",
1691
+ "provider_retryable",
1692
+ "provider_terminal",
1693
+ "provider_outcome_unknown"
1694
+ ]
1695
+ },
1696
+ "ProblemFieldError": {
1697
+ "type": "object",
1698
+ "required": ["field", "code", "detail"],
1699
+ "properties": {
1700
+ "field": {
1701
+ "type": "string",
1702
+ "minLength": 1,
1703
+ "maxLength": 256,
1704
+ "description": "A bounded JSON Pointer or request field identifier."
1705
+ },
1706
+ "code": {
1707
+ "type": "string",
1708
+ "minLength": 1,
1709
+ "maxLength": 64,
1710
+ "pattern": "^[a-z][a-z0-9_]*$"
1711
+ },
1712
+ "detail": {
1713
+ "type": "string",
1714
+ "minLength": 1,
1715
+ "maxLength": 512
1716
+ }
1717
+ },
1718
+ "additionalProperties": false
1719
+ },
1720
+ "Problem": {
1721
+ "type": "object",
1722
+ "description": "Safe RFC 9457-style Problem Details for the WhooshBang API.",
1723
+ "required": [
1724
+ "type",
1725
+ "title",
1726
+ "status",
1727
+ "detail",
1728
+ "code",
1729
+ "diagnostic_id",
1730
+ "retryable"
1731
+ ],
1732
+ "properties": {
1733
+ "type": {
1734
+ "type": "string",
1735
+ "format": "uri",
1736
+ "pattern": "^https://whooshbang\\.flowxo\\.com/problems/[a-z][a-z0-9_]*$"
1737
+ },
1738
+ "title": {
1739
+ "type": "string",
1740
+ "minLength": 1,
1741
+ "maxLength": 128
1742
+ },
1743
+ "status": {
1744
+ "type": "integer",
1745
+ "minimum": 400,
1746
+ "maximum": 599
1747
+ },
1748
+ "detail": {
1749
+ "type": "string",
1750
+ "minLength": 1,
1751
+ "maxLength": 1024
1752
+ },
1753
+ "code": {
1754
+ "$ref": "#/$defs/ProblemCode"
1755
+ },
1756
+ "diagnostic_id": {
1757
+ "$ref": "#/$defs/DiagnosticId"
1758
+ },
1759
+ "retryable": {
1760
+ "type": "boolean"
1761
+ },
1762
+ "retry_at": {
1763
+ "$ref": "#/$defs/Timestamp"
1764
+ },
1765
+ "field_errors": {
1766
+ "type": "array",
1767
+ "maxItems": 20,
1768
+ "items": {
1769
+ "$ref": "#/$defs/ProblemFieldError"
1770
+ }
1771
+ }
1772
+ },
1773
+ "allOf": [
1774
+ {
1775
+ "if": {
1776
+ "type": "object",
1777
+ "properties": {
1778
+ "retryable": {
1779
+ "const": false
1780
+ }
1781
+ },
1782
+ "required": ["retryable"]
1783
+ },
1784
+ "then": {
1785
+ "type": "object",
1786
+ "properties": {
1787
+ "retry_at": false
1788
+ }
1789
+ }
1790
+ }
1791
+ ],
1792
+ "oneOf": [
1793
+ {
1794
+ "type": "object",
1795
+ "properties": {
1796
+ "type": {
1797
+ "const": "https://whooshbang.flowxo.com/problems/authentication_required"
1798
+ },
1799
+ "title": {
1800
+ "const": "Authentication required"
1801
+ },
1802
+ "status": {
1803
+ "const": 401
1804
+ },
1805
+ "code": {
1806
+ "const": "authentication_required"
1807
+ },
1808
+ "retryable": {
1809
+ "const": false
1810
+ }
1811
+ }
1812
+ },
1813
+ {
1814
+ "type": "object",
1815
+ "properties": {
1816
+ "type": {
1817
+ "const": "https://whooshbang.flowxo.com/problems/replay_unavailable"
1818
+ },
1819
+ "title": {
1820
+ "const": "Replay unavailable"
1821
+ },
1822
+ "status": {
1823
+ "const": 409
1824
+ },
1825
+ "code": {
1826
+ "const": "replay_unavailable"
1827
+ },
1828
+ "retryable": {
1829
+ "const": false
1830
+ }
1831
+ }
1832
+ },
1833
+ {
1834
+ "type": "object",
1835
+ "properties": {
1836
+ "type": {
1837
+ "const": "https://whooshbang.flowxo.com/problems/endpoint_rotation_conflict"
1838
+ },
1839
+ "title": {
1840
+ "const": "Endpoint rotation conflict"
1841
+ },
1842
+ "status": {
1843
+ "const": 409
1844
+ },
1845
+ "code": {
1846
+ "const": "endpoint_rotation_conflict"
1847
+ },
1848
+ "retryable": {
1849
+ "const": true
1850
+ }
1851
+ }
1852
+ },
1853
+ {
1854
+ "type": "object",
1855
+ "properties": {
1856
+ "type": {
1857
+ "const": "https://whooshbang.flowxo.com/problems/project_slug_conflict"
1858
+ },
1859
+ "title": {
1860
+ "const": "Project slug conflict"
1861
+ },
1862
+ "status": {
1863
+ "const": 409
1864
+ },
1865
+ "code": {
1866
+ "const": "project_slug_conflict"
1867
+ },
1868
+ "retryable": {
1869
+ "const": false
1870
+ }
1871
+ }
1872
+ },
1873
+ {
1874
+ "type": "object",
1875
+ "properties": {
1876
+ "type": {
1877
+ "const": "https://whooshbang.flowxo.com/problems/credential_invalid"
1878
+ },
1879
+ "title": {
1880
+ "const": "Credential invalid"
1881
+ },
1882
+ "status": {
1883
+ "const": 401
1884
+ },
1885
+ "code": {
1886
+ "const": "credential_invalid"
1887
+ },
1888
+ "retryable": {
1889
+ "const": false
1890
+ }
1891
+ }
1892
+ },
1893
+ {
1894
+ "type": "object",
1895
+ "properties": {
1896
+ "type": {
1897
+ "const": "https://whooshbang.flowxo.com/problems/scope_forbidden"
1898
+ },
1899
+ "title": {
1900
+ "const": "Scope forbidden"
1901
+ },
1902
+ "status": {
1903
+ "const": 403
1904
+ },
1905
+ "code": {
1906
+ "const": "scope_forbidden"
1907
+ },
1908
+ "retryable": {
1909
+ "const": false
1910
+ }
1911
+ }
1912
+ },
1913
+ {
1914
+ "type": "object",
1915
+ "properties": {
1916
+ "type": {
1917
+ "const": "https://whooshbang.flowxo.com/problems/environment_mismatch"
1918
+ },
1919
+ "title": {
1920
+ "const": "Environment mismatch"
1921
+ },
1922
+ "status": {
1923
+ "const": 403
1924
+ },
1925
+ "code": {
1926
+ "const": "environment_mismatch"
1927
+ },
1928
+ "retryable": {
1929
+ "const": false
1930
+ }
1931
+ }
1932
+ },
1933
+ {
1934
+ "type": "object",
1935
+ "properties": {
1936
+ "type": {
1937
+ "const": "https://whooshbang.flowxo.com/problems/idempotency_key_required"
1938
+ },
1939
+ "title": {
1940
+ "const": "Idempotency key required"
1941
+ },
1942
+ "status": {
1943
+ "const": 400
1944
+ },
1945
+ "code": {
1946
+ "const": "idempotency_key_required"
1947
+ },
1948
+ "retryable": {
1949
+ "const": false
1950
+ }
1951
+ }
1952
+ },
1953
+ {
1954
+ "type": "object",
1955
+ "properties": {
1956
+ "type": {
1957
+ "const": "https://whooshbang.flowxo.com/problems/idempotency_conflict"
1958
+ },
1959
+ "title": {
1960
+ "const": "Idempotency conflict"
1961
+ },
1962
+ "status": {
1963
+ "const": 409
1964
+ },
1965
+ "code": {
1966
+ "const": "idempotency_conflict"
1967
+ },
1968
+ "retryable": {
1969
+ "const": false
1970
+ }
1971
+ }
1972
+ },
1973
+ {
1974
+ "type": "object",
1975
+ "properties": {
1976
+ "type": {
1977
+ "const": "https://whooshbang.flowxo.com/problems/request_invalid"
1978
+ },
1979
+ "title": {
1980
+ "const": "Request invalid"
1981
+ },
1982
+ "status": {
1983
+ "const": 400
1984
+ },
1985
+ "code": {
1986
+ "const": "request_invalid"
1987
+ },
1988
+ "retryable": {
1989
+ "const": false
1990
+ }
1991
+ }
1992
+ },
1993
+ {
1994
+ "type": "object",
1995
+ "properties": {
1996
+ "type": {
1997
+ "const": "https://whooshbang.flowxo.com/problems/subscriber_unbound"
1998
+ },
1999
+ "title": {
2000
+ "const": "Subscriber unbound"
2001
+ },
2002
+ "status": {
2003
+ "const": 422
2004
+ },
2005
+ "code": {
2006
+ "const": "subscriber_unbound"
2007
+ },
2008
+ "retryable": {
2009
+ "const": false
2010
+ }
2011
+ }
2012
+ },
2013
+ {
2014
+ "type": "object",
2015
+ "properties": {
2016
+ "type": {
2017
+ "const": "https://whooshbang.flowxo.com/problems/resource_not_found"
2018
+ },
2019
+ "title": {
2020
+ "const": "Resource not found"
2021
+ },
2022
+ "status": {
2023
+ "const": 404
2024
+ },
2025
+ "code": {
2026
+ "const": "resource_not_found"
2027
+ },
2028
+ "retryable": {
2029
+ "const": false
2030
+ }
2031
+ }
2032
+ },
2033
+ {
2034
+ "type": "object",
2035
+ "properties": {
2036
+ "type": {
2037
+ "const": "https://whooshbang.flowxo.com/problems/resource_expired"
2038
+ },
2039
+ "title": {
2040
+ "const": "Resource expired"
2041
+ },
2042
+ "status": {
2043
+ "const": 404
2044
+ },
2045
+ "code": {
2046
+ "const": "resource_expired"
2047
+ },
2048
+ "retryable": {
2049
+ "const": false
2050
+ }
2051
+ }
2052
+ },
2053
+ {
2054
+ "type": "object",
2055
+ "properties": {
2056
+ "type": {
2057
+ "const": "https://whooshbang.flowxo.com/problems/cancellation_too_late"
2058
+ },
2059
+ "title": {
2060
+ "const": "Cancellation too late"
2061
+ },
2062
+ "status": {
2063
+ "const": 409
2064
+ },
2065
+ "code": {
2066
+ "const": "cancellation_too_late"
2067
+ },
2068
+ "retryable": {
2069
+ "const": false
2070
+ }
2071
+ }
2072
+ },
2073
+ {
2074
+ "type": "object",
2075
+ "properties": {
2076
+ "type": {
2077
+ "const": "https://whooshbang.flowxo.com/problems/rate_limited"
2078
+ },
2079
+ "title": {
2080
+ "const": "Rate limited"
2081
+ },
2082
+ "status": {
2083
+ "const": 429
2084
+ },
2085
+ "code": {
2086
+ "const": "rate_limited"
2087
+ },
2088
+ "retryable": {
2089
+ "const": true
2090
+ }
2091
+ }
2092
+ },
2093
+ {
2094
+ "type": "object",
2095
+ "properties": {
2096
+ "type": {
2097
+ "const": "https://whooshbang.flowxo.com/problems/provider_retryable"
2098
+ },
2099
+ "title": {
2100
+ "const": "Provider retryable failure"
2101
+ },
2102
+ "status": {
2103
+ "const": 503
2104
+ },
2105
+ "code": {
2106
+ "const": "provider_retryable"
2107
+ },
2108
+ "retryable": {
2109
+ "const": true
2110
+ }
2111
+ }
2112
+ },
2113
+ {
2114
+ "type": "object",
2115
+ "properties": {
2116
+ "type": {
2117
+ "const": "https://whooshbang.flowxo.com/problems/provider_terminal"
2118
+ },
2119
+ "title": {
2120
+ "const": "Provider terminal failure"
2121
+ },
2122
+ "status": {
2123
+ "const": 502
2124
+ },
2125
+ "code": {
2126
+ "const": "provider_terminal"
2127
+ },
2128
+ "retryable": {
2129
+ "const": false
2130
+ }
2131
+ }
2132
+ },
2133
+ {
2134
+ "type": "object",
2135
+ "properties": {
2136
+ "type": {
2137
+ "const": "https://whooshbang.flowxo.com/problems/provider_outcome_unknown"
2138
+ },
2139
+ "title": {
2140
+ "const": "Provider outcome unknown"
2141
+ },
2142
+ "status": {
2143
+ "const": 502
2144
+ },
2145
+ "code": {
2146
+ "const": "provider_outcome_unknown"
2147
+ },
2148
+ "retryable": {
2149
+ "const": false
2150
+ }
2151
+ }
2152
+ }
2153
+ ],
2154
+ "additionalProperties": false
2155
+ },
2156
+ "AccountScope": {
2157
+ "type": "string",
2158
+ "enum": ["projects:write", "projects:read"],
2159
+ "description": "One account-level capability. Only a human session or an OAuth grant can hold one: a project credential lives inside a project and can never create or enumerate its siblings."
2160
+ },
2161
+ "OAuthScope": {
2162
+ "description": "A scope an OAuth grant may carry. Project-level scopes are the credential vocabulary unchanged; account-level scopes exist only here and on a human session.",
2163
+ "oneOf": [
2164
+ {
2165
+ "$ref": "#/$defs/AccountScope"
2166
+ },
2167
+ {
2168
+ "$ref": "#/$defs/ApiCredentialScope"
2169
+ }
2170
+ ]
2171
+ }
2172
+ }
2173
+ }