@whooshbang/contracts 1.0.0-rc.20 → 1.0.0-rc.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +17 -0
- package/contract-manifest.json +46 -32
- package/dist/generated/artifact-index.d.ts +34 -34
- package/dist/generated/artifact-index.js +46 -32
- package/dist/generated/artifact-index.js.map +1 -1
- package/dist/generated/validators-standalone.d.ts +384 -254
- package/dist/generated/validators-standalone.js +25492 -15741
- package/dist/generated/validators-standalone.js.map +1 -1
- package/dist/generated/validators.d.ts +11 -1
- package/dist/generated/validators.js +62 -17
- package/dist/generated/validators.js.map +1 -1
- package/dist/generated/whooshbang-v1.d.ts +809 -5
- package/dist/index.d.ts +6 -2
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/fixtures/core-v1/fixture-index.json +110 -1
- package/fixtures/core-v1/problems/credential-account-mismatch.json +9 -0
- package/fixtures/core-v1/requests/create-account-credential.json +4 -0
- package/fixtures/core-v1/requests/create-connection-test.json +3 -0
- package/fixtures/core-v1/requests/rotate-account-credential.json +3 -0
- package/fixtures/core-v1/scenarios/api-credential-invalid-indistinguishable.json +1 -1
- package/fixtures/core-v1/scenarios/api-credential-one-time-reveal.json +1 -1
- package/fixtures/core-v1/scenarios/api-credential-revocation-replay.json +1 -1
- package/fixtures/core-v1/scenarios/api-credential-rotation-overlap.json +1 -1
- package/fixtures/core-v1/scenarios/cancellation-too-late.json +1 -1
- package/fixtures/core-v1/scenarios/cross-account-project.json +1 -1
- package/fixtures/core-v1/scenarios/cross-project-resource.json +1 -1
- package/fixtures/core-v1/scenarios/environment-mismatch.json +1 -1
- package/fixtures/core-v1/scenarios/idempotency-conflict.json +1 -1
- package/fixtures/core-v1/scenarios/idempotency-equivalent-replay.json +1 -1
- package/fixtures/core-v1/scenarios/malformed-bearer.json +1 -1
- package/fixtures/core-v1/scenarios/metadata-overrun.json +1 -1
- package/fixtures/core-v1/scenarios/missing-scope.json +1 -1
- package/fixtures/core-v1/scenarios/project-create-equivalent-replay.json +1 -1
- package/fixtures/core-v1/scenarios/project-slug-conflict.json +1 -1
- package/fixtures/core-v1/scenarios/provider-outcome-unknown.json +1 -1
- package/fixtures/core-v1/scenarios/subscription-activation-replay.json +1 -1
- package/fixtures/core-v1/scenarios/subscription-authorization-capability-opacity.json +1 -1
- package/fixtures/core-v1/scenarios/subscription-cross-connection-callback-rejected.json +1 -1
- package/fixtures/core-v1/scenarios/subscription-expired-replay.json +1 -1
- package/fixtures/core-v1/scenarios/subscription-selected-connection-replay.json +1 -1
- package/fixtures/core-v1/success/account-credential-collection.json +17 -0
- package/fixtures/core-v1/success/account-credential-created.json +12 -0
- package/fixtures/core-v1/success/account-credential-revoked.json +11 -0
- package/fixtures/core-v1/success/account-credential-rotated.json +25 -0
- package/fixtures/core-v1/success/channel-connection-slack-customer-active.json +31 -0
- package/fixtures/core-v1/success/channel-connection-slack-customer-pending.json +36 -0
- package/fixtures/core-v1/success/connection-test-collection.json +39 -0
- package/fixtures/core-v1/success/connection-test-pending.json +16 -0
- package/fixtures/core-v1/success/connection-test-succeeded.json +20 -0
- package/fixtures/core-v1/success/customer-owned-slack-app-manifest.json +53 -0
- package/fixtures/interaction-machine-v1/scenarios/ack-conflict.json +1 -1
- package/fixtures/interaction-machine-v1/scenarios/ack-cross-machine.json +1 -1
- package/fixtures/interaction-machine-v1/scenarios/ack-out-of-order.json +1 -1
- package/fixtures/interaction-machine-v1/scenarios/ack-replay.json +1 -1
- package/fixtures/interaction-machine-v1/scenarios/ambiguous-free-text.json +1 -1
- package/fixtures/interaction-machine-v1/scenarios/callback-token-opacity.json +1 -1
- package/fixtures/interaction-machine-v1/scenarios/credential-registration-conflict.json +1 -1
- package/fixtures/interaction-machine-v1/scenarios/credential-registration-replay.json +1 -1
- package/fixtures/interaction-machine-v1/scenarios/credential-revocation-replay.json +1 -1
- package/fixtures/interaction-machine-v1/scenarios/credential-rotation.json +1 -1
- package/fixtures/interaction-machine-v1/scenarios/first-credential-activates.json +1 -1
- package/fixtures/interaction-machine-v1/scenarios/interaction-duplicate.json +1 -1
- package/fixtures/interaction-machine-v1/scenarios/interaction-expired.json +1 -1
- package/fixtures/interaction-machine-v1/scenarios/interaction-mismatch.json +1 -1
- package/fixtures/interaction-machine-v1/scenarios/interaction-retired.json +1 -1
- package/fixtures/interaction-machine-v1/scenarios/interaction-stale.json +1 -1
- package/fixtures/interaction-machine-v1/scenarios/machine-admin-denied.json +1 -1
- package/fixtures/interaction-machine-v1/scenarios/machine-bound-message-create.json +1 -1
- package/fixtures/interaction-machine-v1/scenarios/machine-cancel-denied.json +1 -1
- package/fixtures/interaction-machine-v1/scenarios/machine-crash-before-ack.json +1 -1
- package/fixtures/interaction-machine-v1/scenarios/machine-notifier-mismatch.json +1 -1
- package/fixtures/interaction-machine-v1/scenarios/machine-other-message-read.json +1 -1
- package/fixtures/interaction-machine-v1/scenarios/machine-owned-message-read.json +1 -1
- package/fixtures/interaction-machine-v1/scenarios/machine-subscriber-mismatch.json +1 -1
- package/fixtures/interaction-machine-v1/scenarios/poll-uncommitted-cursor-rejected.json +1 -1
- package/fixtures/interaction-machine-v1/scenarios/project-credential-machine-endpoint-denied.json +1 -1
- package/fixtures/interaction-machine-v1/scenarios/quarantine-integrity-rejected.json +1 -1
- package/fixtures/interaction-machine-v1/scenarios/quarantine-signature-rejected.json +1 -1
- package/fixtures/interaction-machine-v1/scenarios/quarantine-valid.json +1 -1
- package/fixtures/interaction-machine-v1/scenarios/revoked-machine-credential.json +1 -1
- package/fixtures/recipient-experience-v1/scenarios/membership-broadcast-snapshot.json +1 -1
- package/fixtures/recipient-experience-v1/scenarios/preferred-binding-resolution.json +1 -1
- package/fixtures/recipient-experience-v1/scenarios/recipient-session-security.json +1 -1
- package/fixtures/recipient-experience-v1/scenarios/subscriber-initial-groups-transaction.json +1 -1
- package/openapi/generated/whooshbang-v1.bundle.json +1581 -93
- package/openapi/whooshbang-v1.yaml +667 -10
- package/package.json +1 -1
- package/schemas/core-scenario.schema.json +1 -1
- package/schemas/events/agent-binding-degraded-v1.schema.json +1 -1
- package/schemas/events/interaction-expired-v1.schema.json +1 -1
- package/schemas/events/interaction-received-v1.schema.json +1 -1
- package/schemas/events/message-delivered-v1.schema.json +1 -1
- package/schemas/events/message-expired-v1.schema.json +1 -1
- package/schemas/events/message-failed-v1.schema.json +1 -1
- package/schemas/events/message-provider-accepted-v1.schema.json +1 -1
- package/schemas/events/subscription-activated-v1.schema.json +1 -1
- package/schemas/events/subscription-revoked-v1.schema.json +1 -1
- package/schemas/events/whooshbang-event-v1.schema.json +1 -1
- package/schemas/fixture-index.schema.json +1 -1
- package/schemas/interaction-machine-scenario.schema.json +1 -1
- package/schemas/interaction-v1.schema.json +1 -1
- package/schemas/machine-v1.schema.json +1 -1
- package/schemas/provider-capabilities-v1.schema.json +1 -1
- package/schemas/webhooks/webhook-delivery-case-v1.schema.json +1 -1
- package/schemas/webhooks/webhook-signing-vector-v1.schema.json +1 -1
- package/schemas/whooshbang-v1.schema.json +811 -23
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"openapi": "3.1.1",
|
|
3
3
|
"info": {
|
|
4
4
|
"title": "WhooshBang API",
|
|
5
|
-
"version": "1.0.0-rc.
|
|
5
|
+
"version": "1.0.0-rc.21",
|
|
6
6
|
"description": "The V1 contract for human account/project administration, project-scoped\nsubscription links, and asynchronous text messages. A Clerk session selects\none account for administration. A project credential selects one account,\nproject, and `test` or `live` environment for product operations; request\nbodies cannot override either authenticated scope.\n\nA person's OAuth grant selects one account 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": {
|
|
@@ -32,6 +32,10 @@
|
|
|
32
32
|
"name": "API credentials",
|
|
33
33
|
"description": "Human-administered, environment-scoped bearer credentials with one-time secret reveal."
|
|
34
34
|
},
|
|
35
|
+
{
|
|
36
|
+
"name": "Account credentials",
|
|
37
|
+
"description": "Human-administered, account-scoped bearer credentials that name their project and environment per call."
|
|
38
|
+
},
|
|
35
39
|
{
|
|
36
40
|
"name": "Subscription links",
|
|
37
41
|
"description": "Single-use, expiring Telegram authorization links."
|
|
@@ -119,13 +123,299 @@
|
|
|
119
123
|
}
|
|
120
124
|
}
|
|
121
125
|
},
|
|
126
|
+
"/v1/account/credentials": {
|
|
127
|
+
"get": {
|
|
128
|
+
"operationId": "listAccountCredentials",
|
|
129
|
+
"summary": "List safe account credential metadata",
|
|
130
|
+
"description": "Returns only credentials owned by the account selected by the verified\nClerk organization session. Secrets and digest material are never\nreturned.\n",
|
|
131
|
+
"tags": ["Account credentials"],
|
|
132
|
+
"security": [{ "clerkSession": [] }],
|
|
133
|
+
"x-account-membership": "required",
|
|
134
|
+
"parameters": [
|
|
135
|
+
{ "$ref": "#/components/parameters/ApiCredentialAfterCursor" },
|
|
136
|
+
{ "$ref": "#/components/parameters/ApiCredentialPageLimit" },
|
|
137
|
+
{ "$ref": "#/components/parameters/PageNumber" }
|
|
138
|
+
],
|
|
139
|
+
"responses": {
|
|
140
|
+
"200": {
|
|
141
|
+
"description": "A stable page of safe account credential metadata.",
|
|
142
|
+
"headers": {
|
|
143
|
+
"WhooshBang-Diagnostic-Id": {
|
|
144
|
+
"$ref": "#/components/headers/DiagnosticId"
|
|
145
|
+
}
|
|
146
|
+
},
|
|
147
|
+
"content": {
|
|
148
|
+
"application/json": {
|
|
149
|
+
"schema": {
|
|
150
|
+
"$ref": "#/components/schemas/AccountCredentialCollection"
|
|
151
|
+
},
|
|
152
|
+
"examples": {
|
|
153
|
+
"accountCredentials": {
|
|
154
|
+
"value": {
|
|
155
|
+
"items": [
|
|
156
|
+
{
|
|
157
|
+
"id": "acred_AAAAAAAAAAAAAAAAAAAAAA",
|
|
158
|
+
"prefix": "wb_ac1.acred_AAAAAAAA",
|
|
159
|
+
"label": "Synthetic release automation",
|
|
160
|
+
"scopes": [
|
|
161
|
+
"account:read",
|
|
162
|
+
"messages:write",
|
|
163
|
+
"messages:read"
|
|
164
|
+
],
|
|
165
|
+
"created_by": "member_synthetic_001",
|
|
166
|
+
"created_at": "2026-07-25T17:45:00Z",
|
|
167
|
+
"last_used_at": "2026-07-25T17:50:00Z",
|
|
168
|
+
"status": "active"
|
|
169
|
+
}
|
|
170
|
+
],
|
|
171
|
+
"page": 1,
|
|
172
|
+
"page_size": 50,
|
|
173
|
+
"total": 1
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
},
|
|
180
|
+
"400": { "$ref": "#/components/responses/BadRequest" },
|
|
181
|
+
"401": { "$ref": "#/components/responses/Unauthorized" },
|
|
182
|
+
"403": { "$ref": "#/components/responses/Forbidden" },
|
|
183
|
+
"404": { "$ref": "#/components/responses/NotFound" },
|
|
184
|
+
"429": { "$ref": "#/components/responses/RateLimited" }
|
|
185
|
+
}
|
|
186
|
+
},
|
|
187
|
+
"post": {
|
|
188
|
+
"operationId": "createAccountCredential",
|
|
189
|
+
"summary": "Create an account-scoped API credential",
|
|
190
|
+
"description": "Creates one credential against the whole account. It names no project\nand no environment: a request names them in its path, and the\ncredential's authority on that path is the intersection of its scopes\nwith the project-credential vocabulary, so it is never wider than a\nproject credential would have been for the same project environment.\nThe complete bearer secret appears only in this creation response. It\ncannot be read later and is never replayed from an idempotency cache.\n",
|
|
191
|
+
"tags": ["Account credentials"],
|
|
192
|
+
"security": [{ "clerkSession": [] }],
|
|
193
|
+
"x-account-membership": "required",
|
|
194
|
+
"x-secret-reveal": {
|
|
195
|
+
"responses": 1,
|
|
196
|
+
"replayable": false,
|
|
197
|
+
"later_reads": "metadata_only"
|
|
198
|
+
},
|
|
199
|
+
"requestBody": {
|
|
200
|
+
"required": true,
|
|
201
|
+
"content": {
|
|
202
|
+
"application/json": {
|
|
203
|
+
"schema": {
|
|
204
|
+
"$ref": "#/components/schemas/CreateAccountCredentialRequest"
|
|
205
|
+
},
|
|
206
|
+
"examples": {
|
|
207
|
+
"createAccountCredential": {
|
|
208
|
+
"value": {
|
|
209
|
+
"label": "Release automation",
|
|
210
|
+
"scopes": [
|
|
211
|
+
"account:read",
|
|
212
|
+
"messages:write",
|
|
213
|
+
"messages:read"
|
|
214
|
+
]
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
},
|
|
221
|
+
"responses": {
|
|
222
|
+
"201": {
|
|
223
|
+
"description": "Creation-only secret reveal and safe credential metadata.",
|
|
224
|
+
"headers": {
|
|
225
|
+
"WhooshBang-Diagnostic-Id": {
|
|
226
|
+
"$ref": "#/components/headers/DiagnosticId"
|
|
227
|
+
}
|
|
228
|
+
},
|
|
229
|
+
"content": {
|
|
230
|
+
"application/json": {
|
|
231
|
+
"schema": {
|
|
232
|
+
"$ref": "#/components/schemas/AccountCredentialSecretReveal"
|
|
233
|
+
},
|
|
234
|
+
"examples": {
|
|
235
|
+
"createdAccountCredential": {
|
|
236
|
+
"value": {
|
|
237
|
+
"credential": {
|
|
238
|
+
"id": "acred_AAAAAAAAAAAAAAAAAAAAAA",
|
|
239
|
+
"prefix": "wb_ac1.acred_AAAAAAAA",
|
|
240
|
+
"label": "Synthetic release automation",
|
|
241
|
+
"scopes": [
|
|
242
|
+
"account:read",
|
|
243
|
+
"messages:write",
|
|
244
|
+
"messages:read"
|
|
245
|
+
],
|
|
246
|
+
"created_by": "member_synthetic_001",
|
|
247
|
+
"created_at": "2026-07-25T17:45:00Z",
|
|
248
|
+
"status": "active"
|
|
249
|
+
},
|
|
250
|
+
"secret": "wb_ac1.acred_AAAAAAAAAAAAAAAAAAAAAA.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
},
|
|
257
|
+
"400": { "$ref": "#/components/responses/BadRequest" },
|
|
258
|
+
"401": { "$ref": "#/components/responses/Unauthorized" },
|
|
259
|
+
"403": { "$ref": "#/components/responses/Forbidden" },
|
|
260
|
+
"404": { "$ref": "#/components/responses/NotFound" },
|
|
261
|
+
"429": { "$ref": "#/components/responses/RateLimited" }
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
},
|
|
265
|
+
"/v1/account/credentials/{account_credential_id}/rotate": {
|
|
266
|
+
"parameters": [{ "$ref": "#/components/parameters/AccountCredentialId" }],
|
|
267
|
+
"post": {
|
|
268
|
+
"operationId": "rotateAccountCredential",
|
|
269
|
+
"summary": "Rotate an account-scoped API credential",
|
|
270
|
+
"description": "Creates one replacement with the same label and scopes. The complete new\nbearer secret appears only in this response. The replaced credential\nremains active for the explicitly declared overlap, from zero through\n86400 seconds, then becomes terminally revoked. A revoked credential\ncannot be rotated. Missing and out-of-account credentials use the same\nnon-enumerating `404`.\n",
|
|
271
|
+
"tags": ["Account credentials"],
|
|
272
|
+
"security": [{ "clerkSession": [] }],
|
|
273
|
+
"x-account-membership": "required",
|
|
274
|
+
"x-secret-reveal": {
|
|
275
|
+
"responses": 1,
|
|
276
|
+
"replayable": false,
|
|
277
|
+
"later_reads": "metadata_only"
|
|
278
|
+
},
|
|
279
|
+
"requestBody": {
|
|
280
|
+
"required": true,
|
|
281
|
+
"content": {
|
|
282
|
+
"application/json": {
|
|
283
|
+
"schema": {
|
|
284
|
+
"$ref": "#/components/schemas/RotateAccountCredentialRequest"
|
|
285
|
+
},
|
|
286
|
+
"examples": {
|
|
287
|
+
"rotateAccountCredential": {
|
|
288
|
+
"value": { "overlap_seconds": 3600 }
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
},
|
|
294
|
+
"responses": {
|
|
295
|
+
"201": {
|
|
296
|
+
"description": "Creation-only replacement secret and safe metadata for both credentials.",
|
|
297
|
+
"headers": {
|
|
298
|
+
"WhooshBang-Diagnostic-Id": {
|
|
299
|
+
"$ref": "#/components/headers/DiagnosticId"
|
|
300
|
+
}
|
|
301
|
+
},
|
|
302
|
+
"content": {
|
|
303
|
+
"application/json": {
|
|
304
|
+
"schema": {
|
|
305
|
+
"$ref": "#/components/schemas/AccountCredentialRotation"
|
|
306
|
+
},
|
|
307
|
+
"examples": {
|
|
308
|
+
"rotatedAccountCredential": {
|
|
309
|
+
"value": {
|
|
310
|
+
"credential": {
|
|
311
|
+
"credential": {
|
|
312
|
+
"id": "acred_BBBBBBBBBBBBBBBBBBBBBA",
|
|
313
|
+
"prefix": "wb_ac1.acred_BBBBBBBB",
|
|
314
|
+
"label": "Synthetic release automation",
|
|
315
|
+
"scopes": [
|
|
316
|
+
"account:read",
|
|
317
|
+
"messages:write",
|
|
318
|
+
"messages:read"
|
|
319
|
+
],
|
|
320
|
+
"created_by": "member_synthetic_001",
|
|
321
|
+
"created_at": "2026-07-25T17:45:00Z",
|
|
322
|
+
"status": "active"
|
|
323
|
+
},
|
|
324
|
+
"secret": "wb_ac1.acred_BBBBBBBBBBBBBBBBBBBBBA.BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBA"
|
|
325
|
+
},
|
|
326
|
+
"replaced_credential": {
|
|
327
|
+
"id": "acred_AAAAAAAAAAAAAAAAAAAAAA",
|
|
328
|
+
"prefix": "wb_ac1.acred_AAAAAAAA",
|
|
329
|
+
"label": "Synthetic release automation",
|
|
330
|
+
"scopes": [
|
|
331
|
+
"account:read",
|
|
332
|
+
"messages:write",
|
|
333
|
+
"messages:read"
|
|
334
|
+
],
|
|
335
|
+
"created_by": "member_synthetic_001",
|
|
336
|
+
"created_at": "2026-07-25T17:45:00Z",
|
|
337
|
+
"last_used_at": "2026-07-25T17:50:00Z",
|
|
338
|
+
"scheduled_revocation_at": "2026-07-25T18:45:00Z",
|
|
339
|
+
"status": "active"
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
},
|
|
347
|
+
"400": { "$ref": "#/components/responses/BadRequest" },
|
|
348
|
+
"401": { "$ref": "#/components/responses/Unauthorized" },
|
|
349
|
+
"403": { "$ref": "#/components/responses/Forbidden" },
|
|
350
|
+
"404": { "$ref": "#/components/responses/NotFound" },
|
|
351
|
+
"429": { "$ref": "#/components/responses/RateLimited" }
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
},
|
|
355
|
+
"/v1/account/credentials/{account_credential_id}/revoke": {
|
|
356
|
+
"parameters": [{ "$ref": "#/components/parameters/AccountCredentialId" }],
|
|
357
|
+
"post": {
|
|
358
|
+
"operationId": "revokeAccountCredential",
|
|
359
|
+
"summary": "Revoke an account-scoped API credential",
|
|
360
|
+
"description": "Immediately and terminally revokes the selected credential. Repeating\nthe request for the same visible credential is safe and returns the\nsame revoked metadata without revealing secret or digest material.\nMissing and out-of-account credentials use the same non-enumerating\n`404`.\n",
|
|
361
|
+
"tags": ["Account credentials"],
|
|
362
|
+
"security": [{ "clerkSession": [] }],
|
|
363
|
+
"x-account-membership": "required",
|
|
364
|
+
"x-repeat-semantics": {
|
|
365
|
+
"same_target": "idempotent",
|
|
366
|
+
"secret_returned": false
|
|
367
|
+
},
|
|
368
|
+
"responses": {
|
|
369
|
+
"200": {
|
|
370
|
+
"description": "Safe terminal credential metadata.",
|
|
371
|
+
"headers": {
|
|
372
|
+
"WhooshBang-Diagnostic-Id": {
|
|
373
|
+
"$ref": "#/components/headers/DiagnosticId"
|
|
374
|
+
}
|
|
375
|
+
},
|
|
376
|
+
"content": {
|
|
377
|
+
"application/json": {
|
|
378
|
+
"schema": { "$ref": "#/components/schemas/AccountCredential" },
|
|
379
|
+
"examples": {
|
|
380
|
+
"revokedAccountCredential": {
|
|
381
|
+
"value": {
|
|
382
|
+
"id": "acred_AAAAAAAAAAAAAAAAAAAAAA",
|
|
383
|
+
"prefix": "wb_ac1.acred_AAAAAAAA",
|
|
384
|
+
"label": "Synthetic release automation",
|
|
385
|
+
"scopes": [
|
|
386
|
+
"account:read",
|
|
387
|
+
"messages:write",
|
|
388
|
+
"messages:read"
|
|
389
|
+
],
|
|
390
|
+
"created_by": "member_synthetic_001",
|
|
391
|
+
"created_at": "2026-07-25T17:45:00Z",
|
|
392
|
+
"last_used_at": "2026-07-25T17:50:00Z",
|
|
393
|
+
"status": "revoked",
|
|
394
|
+
"revoked_at": "2026-07-25T17:55:00Z"
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
},
|
|
401
|
+
"401": { "$ref": "#/components/responses/Unauthorized" },
|
|
402
|
+
"403": { "$ref": "#/components/responses/Forbidden" },
|
|
403
|
+
"404": { "$ref": "#/components/responses/NotFound" },
|
|
404
|
+
"429": { "$ref": "#/components/responses/RateLimited" }
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
},
|
|
122
408
|
"/v1/projects": {
|
|
123
409
|
"get": {
|
|
124
410
|
"operationId": "listProjects",
|
|
125
411
|
"summary": "List projects in the current account",
|
|
126
412
|
"description": "Returns only projects owned by the account selected by the verified\nClerk organization session. Every project includes its complete test/live\nenvironment and default-notifier bootstrap.\n",
|
|
127
413
|
"tags": ["Projects"],
|
|
128
|
-
"security": [
|
|
414
|
+
"security": [
|
|
415
|
+
{ "clerkSession": [] },
|
|
416
|
+
{ "bearerGrant": [] },
|
|
417
|
+
{ "bearerAccountCredential": [] }
|
|
418
|
+
],
|
|
129
419
|
"x-required-scopes": ["projects:read"],
|
|
130
420
|
"x-account-membership": "required",
|
|
131
421
|
"parameters": [
|
|
@@ -308,7 +598,11 @@
|
|
|
308
598
|
"summary": "Read one project in the current account",
|
|
309
599
|
"description": "Returns the complete project bootstrap. An absent project and a project\noutside the authenticated account use the same non-enumerating `404`.\n",
|
|
310
600
|
"tags": ["Projects"],
|
|
311
|
-
"security": [
|
|
601
|
+
"security": [
|
|
602
|
+
{ "clerkSession": [] },
|
|
603
|
+
{ "bearerGrant": [] },
|
|
604
|
+
{ "bearerAccountCredential": [] }
|
|
605
|
+
],
|
|
312
606
|
"x-required-scopes": ["projects:read"],
|
|
313
607
|
"x-account-membership": "required",
|
|
314
608
|
"responses": {
|
|
@@ -1059,7 +1353,11 @@
|
|
|
1059
1353
|
"summary": "Normalize and retain one environment-owned notification image",
|
|
1060
1354
|
"description": "Accepts one bounded raw PNG, validates and re-encodes it as canonical\nRGBA PNG bytes, and retains the immutable asset for a 30-day admission\nwindow. The service never fetches a customer URL. An equivalent replay\nreturns the original asset with `200`; a new asset returns `201`.\n",
|
|
1061
1355
|
"tags": ["Messages"],
|
|
1062
|
-
"security": [
|
|
1356
|
+
"security": [
|
|
1357
|
+
{ "bearerCredential": [] },
|
|
1358
|
+
{ "bearerGrant": [] },
|
|
1359
|
+
{ "bearerAccountCredential": [] }
|
|
1360
|
+
],
|
|
1063
1361
|
"x-required-scopes": ["messages:write"],
|
|
1064
1362
|
"x-idempotency": {
|
|
1065
1363
|
"required": true,
|
|
@@ -1146,7 +1444,11 @@
|
|
|
1146
1444
|
"summary": "Delete one environment-owned notification image",
|
|
1147
1445
|
"description": "Immediately prevents new notification references. Repeating deletion\nof an asset already deleted in this exact scope remains successful;\nunknown and foreign identifiers return the same non-enumerating `404`.\nAccepted notifications retain their existing delivery lease.\n",
|
|
1148
1446
|
"tags": ["Messages"],
|
|
1149
|
-
"security": [
|
|
1447
|
+
"security": [
|
|
1448
|
+
{ "bearerCredential": [] },
|
|
1449
|
+
{ "bearerGrant": [] },
|
|
1450
|
+
{ "bearerAccountCredential": [] }
|
|
1451
|
+
],
|
|
1150
1452
|
"x-required-scopes": ["messages:write"],
|
|
1151
1453
|
"x-repeat-semantics": {
|
|
1152
1454
|
"same_target": "idempotent",
|
|
@@ -2282,7 +2584,8 @@
|
|
|
2282
2584
|
"security": [
|
|
2283
2585
|
{ "clerkSession": [] },
|
|
2284
2586
|
{ "bearerCredential": [] },
|
|
2285
|
-
{ "bearerGrant": [] }
|
|
2587
|
+
{ "bearerGrant": [] },
|
|
2588
|
+
{ "bearerAccountCredential": [] }
|
|
2286
2589
|
],
|
|
2287
2590
|
"x-required-scopes": ["messages:read"],
|
|
2288
2591
|
"parameters": [
|
|
@@ -2355,7 +2658,8 @@
|
|
|
2355
2658
|
"security": [
|
|
2356
2659
|
{ "clerkSession": [] },
|
|
2357
2660
|
{ "bearerCredential": [] },
|
|
2358
|
-
{ "bearerGrant": [] }
|
|
2661
|
+
{ "bearerGrant": [] },
|
|
2662
|
+
{ "bearerAccountCredential": [] }
|
|
2359
2663
|
],
|
|
2360
2664
|
"x-required-scopes": ["messages:write"],
|
|
2361
2665
|
"x-idempotency": {
|
|
@@ -2488,7 +2792,8 @@
|
|
|
2488
2792
|
"security": [
|
|
2489
2793
|
{ "clerkSession": [] },
|
|
2490
2794
|
{ "bearerCredential": [] },
|
|
2491
|
-
{ "bearerGrant": [] }
|
|
2795
|
+
{ "bearerGrant": [] },
|
|
2796
|
+
{ "bearerAccountCredential": [] }
|
|
2492
2797
|
],
|
|
2493
2798
|
"x-required-scopes": ["messages:read"],
|
|
2494
2799
|
"responses": {
|
|
@@ -2519,7 +2824,8 @@
|
|
|
2519
2824
|
"security": [
|
|
2520
2825
|
{ "clerkSession": [] },
|
|
2521
2826
|
{ "bearerCredential": [] },
|
|
2522
|
-
{ "bearerGrant": [] }
|
|
2827
|
+
{ "bearerGrant": [] },
|
|
2828
|
+
{ "bearerAccountCredential": [] }
|
|
2523
2829
|
],
|
|
2524
2830
|
"x-required-scopes": ["messages:write"],
|
|
2525
2831
|
"responses": {
|
|
@@ -2552,7 +2858,8 @@
|
|
|
2552
2858
|
"security": [
|
|
2553
2859
|
{ "clerkSession": [] },
|
|
2554
2860
|
{ "bearerCredential": [] },
|
|
2555
|
-
{ "bearerGrant": [] }
|
|
2861
|
+
{ "bearerGrant": [] },
|
|
2862
|
+
{ "bearerAccountCredential": [] }
|
|
2556
2863
|
],
|
|
2557
2864
|
"x-required-scopes": ["messages:write"],
|
|
2558
2865
|
"x-idempotency": {
|
|
@@ -2655,7 +2962,8 @@
|
|
|
2655
2962
|
"security": [
|
|
2656
2963
|
{ "clerkSession": [] },
|
|
2657
2964
|
{ "bearerCredential": [] },
|
|
2658
|
-
{ "bearerGrant": [] }
|
|
2965
|
+
{ "bearerGrant": [] },
|
|
2966
|
+
{ "bearerAccountCredential": [] }
|
|
2659
2967
|
],
|
|
2660
2968
|
"x-required-scopes": ["messages:write"],
|
|
2661
2969
|
"x-repeat-semantics": {
|
|
@@ -2691,7 +2999,8 @@
|
|
|
2691
2999
|
"security": [
|
|
2692
3000
|
{ "clerkSession": [] },
|
|
2693
3001
|
{ "bearerCredential": [] },
|
|
2694
|
-
{ "bearerGrant": [] }
|
|
3002
|
+
{ "bearerGrant": [] },
|
|
3003
|
+
{ "bearerAccountCredential": [] }
|
|
2695
3004
|
],
|
|
2696
3005
|
"x-required-scopes": ["messages:read"],
|
|
2697
3006
|
"parameters": [
|
|
@@ -2751,7 +3060,8 @@
|
|
|
2751
3060
|
"security": [
|
|
2752
3061
|
{ "clerkSession": [] },
|
|
2753
3062
|
{ "bearerCredential": [] },
|
|
2754
|
-
{ "bearerGrant": [] }
|
|
3063
|
+
{ "bearerGrant": [] },
|
|
3064
|
+
{ "bearerAccountCredential": [] }
|
|
2755
3065
|
],
|
|
2756
3066
|
"x-required-scopes": ["connections:read"],
|
|
2757
3067
|
"responses": {
|
|
@@ -2825,7 +3135,8 @@
|
|
|
2825
3135
|
"security": [
|
|
2826
3136
|
{ "clerkSession": [] },
|
|
2827
3137
|
{ "bearerCredential": [] },
|
|
2828
|
-
{ "bearerGrant": [] }
|
|
3138
|
+
{ "bearerGrant": [] },
|
|
3139
|
+
{ "bearerAccountCredential": [] }
|
|
2829
3140
|
],
|
|
2830
3141
|
"x-required-scopes": ["connections:read"],
|
|
2831
3142
|
"parameters": [
|
|
@@ -2895,7 +3206,8 @@
|
|
|
2895
3206
|
"security": [
|
|
2896
3207
|
{ "clerkSession": [] },
|
|
2897
3208
|
{ "bearerCredential": [] },
|
|
2898
|
-
{ "bearerGrant": [] }
|
|
3209
|
+
{ "bearerGrant": [] },
|
|
3210
|
+
{ "bearerAccountCredential": [] }
|
|
2899
3211
|
],
|
|
2900
3212
|
"x-required-scopes": ["connections:write"],
|
|
2901
3213
|
"x-idempotency": {
|
|
@@ -3084,7 +3396,8 @@
|
|
|
3084
3396
|
"security": [
|
|
3085
3397
|
{ "clerkSession": [] },
|
|
3086
3398
|
{ "bearerCredential": [] },
|
|
3087
|
-
{ "bearerGrant": [] }
|
|
3399
|
+
{ "bearerGrant": [] },
|
|
3400
|
+
{ "bearerAccountCredential": [] }
|
|
3088
3401
|
],
|
|
3089
3402
|
"x-required-scopes": ["connections:read"],
|
|
3090
3403
|
"responses": {
|
|
@@ -3162,7 +3475,8 @@
|
|
|
3162
3475
|
"security": [
|
|
3163
3476
|
{ "clerkSession": [] },
|
|
3164
3477
|
{ "bearerCredential": [] },
|
|
3165
|
-
{ "bearerGrant": [] }
|
|
3478
|
+
{ "bearerGrant": [] },
|
|
3479
|
+
{ "bearerAccountCredential": [] }
|
|
3166
3480
|
],
|
|
3167
3481
|
"x-required-scopes": ["connections:write"],
|
|
3168
3482
|
"requestBody": {
|
|
@@ -3214,7 +3528,8 @@
|
|
|
3214
3528
|
"security": [
|
|
3215
3529
|
{ "clerkSession": [] },
|
|
3216
3530
|
{ "bearerCredential": [] },
|
|
3217
|
-
{ "bearerGrant": [] }
|
|
3531
|
+
{ "bearerGrant": [] },
|
|
3532
|
+
{ "bearerAccountCredential": [] }
|
|
3218
3533
|
],
|
|
3219
3534
|
"x-required-scopes": ["connections:write"],
|
|
3220
3535
|
"responses": {
|
|
@@ -3274,7 +3589,8 @@
|
|
|
3274
3589
|
"security": [
|
|
3275
3590
|
{ "clerkSession": [] },
|
|
3276
3591
|
{ "bearerCredential": [] },
|
|
3277
|
-
{ "bearerGrant": [] }
|
|
3592
|
+
{ "bearerGrant": [] },
|
|
3593
|
+
{ "bearerAccountCredential": [] }
|
|
3278
3594
|
],
|
|
3279
3595
|
"x-required-scopes": ["connections:write"],
|
|
3280
3596
|
"responses": {
|
|
@@ -3342,7 +3658,8 @@
|
|
|
3342
3658
|
"security": [
|
|
3343
3659
|
{ "clerkSession": [] },
|
|
3344
3660
|
{ "bearerCredential": [] },
|
|
3345
|
-
{ "bearerGrant": [] }
|
|
3661
|
+
{ "bearerGrant": [] },
|
|
3662
|
+
{ "bearerAccountCredential": [] }
|
|
3346
3663
|
],
|
|
3347
3664
|
"x-required-scopes": ["connections:write"],
|
|
3348
3665
|
"x-idempotency": {
|
|
@@ -3385,13 +3702,14 @@
|
|
|
3385
3702
|
],
|
|
3386
3703
|
"post": {
|
|
3387
3704
|
"operationId": "restartChannelConnectionSetup",
|
|
3388
|
-
"summary": "Start a fresh
|
|
3389
|
-
"description": "Issues a new
|
|
3705
|
+
"summary": "Start a fresh setup on a connection that never finished one",
|
|
3706
|
+
"description": "Issues a new guided setup for a `customer_managed` connection whose\nsetup expired, failed, or was simply abandoned. Nothing was ever\nprovisioned under it, no identity was claimed, and no subscriber ever\nconsented to anything through it, so nothing is lost.\n\n**Telegram reuses the connection and answers `200`.** A fresh `setup`\nappears on the same resource.\n\n**Customer-owned Slack replaces the connection and answers `201`.** The\nabandoned connection is archived and a new one is returned, carrying a\nnew `app_setup` and a new creation link. The connection cannot be\nreused, because Slack bakes this deployment's request URLs into the app\nat the moment it is created and they are not ours to change afterwards:\na second setup on one connection would mean two Slack apps answering to\none address. **Read `id` on the response** — the connection you were\npolling is now archived. A Slack app the previous attempt already\ncreated is not withdrawn and cannot be; it is the customer's, and they\ndelete it in Slack when they are ready.\n\nEntering the three app-specific values again is not a restart, and does\nnot need one. `rotate-credential` replaces them on the connection that\nalready exists.\n\nRestart takes no body. The suggested identity recorded when the\nconnection was created is what the new attempt proposes again, so a\ncaller resuming a half-finished setup cannot accidentally rename a bot\nan administrator was already looking at. A different suggestion means\na different connection.\n\nRepeating the call is safe. A setup that is still live is returned as\nit stands rather than being replaced, so two dashboard tabs converge\non one session instead of racing to strand each other's link, and only\na session that can no longer be used is exchanged for a new one.\n\nA bot the previous attempt already created is not withdrawn and cannot\nbe: Telegram exposes no way to delete a bot. The administrator keeps it\nin their own account until they retire it in BotFather, and a restart\nwalks them through creating another rather than pretending the first\nnever happened.\n\nRefusals reuse the closed Problem vocabulary and differ by\n`field_errors[].code`:\n\n- the connection did not come from guided setup, so there is no setup\n to restart — `idempotency_conflict` 409, `setup_not_managed` at `/`;\n- the connection already finished setup, or is archived, so restarting\n would offer a second identity for something subscribers may already\n be bound to — `idempotency_conflict` 409,\n `invalid_connection_transition` at `/`;\n- the provider could not be reached, so whether a new session exists is\n genuinely unknown — `provider_outcome_unknown` 502; read the\n connection before calling again;\n- the provider rate-limited the call — `provider_retryable` 503 with\n `retry_at`.\n",
|
|
3390
3707
|
"tags": ["Channel connections"],
|
|
3391
3708
|
"security": [
|
|
3392
3709
|
{ "clerkSession": [] },
|
|
3393
3710
|
{ "bearerCredential": [] },
|
|
3394
|
-
{ "bearerGrant": [] }
|
|
3711
|
+
{ "bearerGrant": [] },
|
|
3712
|
+
{ "bearerAccountCredential": [] }
|
|
3395
3713
|
],
|
|
3396
3714
|
"x-required-scopes": ["connections:write"],
|
|
3397
3715
|
"x-repeat-semantics": {
|
|
@@ -3439,9 +3757,62 @@
|
|
|
3439
3757
|
}
|
|
3440
3758
|
}
|
|
3441
3759
|
},
|
|
3442
|
-
"
|
|
3443
|
-
|
|
3444
|
-
|
|
3760
|
+
"201": {
|
|
3761
|
+
"description": "A replacement connection carrying a fresh setup, for a\ncustomer-owned Slack app whose request URLs cannot be re-pointed.\nThe connection named in the request is now archived.\n",
|
|
3762
|
+
"headers": {
|
|
3763
|
+
"WhooshBang-Diagnostic-Id": {
|
|
3764
|
+
"$ref": "#/components/headers/DiagnosticId"
|
|
3765
|
+
}
|
|
3766
|
+
},
|
|
3767
|
+
"content": {
|
|
3768
|
+
"application/json": {
|
|
3769
|
+
"schema": { "$ref": "#/components/schemas/ChannelConnection" },
|
|
3770
|
+
"examples": {
|
|
3771
|
+
"setupReplaced": {
|
|
3772
|
+
"value": {
|
|
3773
|
+
"id": "chcon_77777777777777777777777777777777",
|
|
3774
|
+
"project_id": "project_synthetic_alpha",
|
|
3775
|
+
"environment_id": "environment_test_synthetic_alpha",
|
|
3776
|
+
"environment": "test",
|
|
3777
|
+
"provider": "slack",
|
|
3778
|
+
"mode": "customer_managed",
|
|
3779
|
+
"display_name": "Acme Slack",
|
|
3780
|
+
"status": "pending",
|
|
3781
|
+
"health": "unknown",
|
|
3782
|
+
"installation": {
|
|
3783
|
+
"display_name": "Slack app not yet created",
|
|
3784
|
+
"health": "unknown",
|
|
3785
|
+
"status": "pending"
|
|
3786
|
+
},
|
|
3787
|
+
"capabilities": [],
|
|
3788
|
+
"recovery_action": "complete_authorization",
|
|
3789
|
+
"app_setup": {
|
|
3790
|
+
"status": "awaiting_authorities",
|
|
3791
|
+
"creation_url": "https://api.slack.com/apps?new_app=1&manifest_json=%7B%22_metadata%22%3A%7B%22major_version%22%3A1%7D%7D",
|
|
3792
|
+
"expires_at": "2026-09-01T12:30:00.000Z",
|
|
3793
|
+
"app_name": "Acme Notifications",
|
|
3794
|
+
"bot_display_name": "Acme Notify",
|
|
3795
|
+
"outstanding_authorities": [
|
|
3796
|
+
"client_identity",
|
|
3797
|
+
"client_secret",
|
|
3798
|
+
"signing_secret"
|
|
3799
|
+
]
|
|
3800
|
+
},
|
|
3801
|
+
"row_version": 1,
|
|
3802
|
+
"created_at": "2026-09-01T12:00:00.000Z",
|
|
3803
|
+
"updated_at": "2026-09-01T12:00:00.000Z",
|
|
3804
|
+
"last_transition_at": "2026-09-01T12:00:00.000Z",
|
|
3805
|
+
"last_transition_reason": "slack_setup_awaiting_authorities",
|
|
3806
|
+
"diagnostic_id": "diag_slack_customer_pending_001"
|
|
3807
|
+
}
|
|
3808
|
+
}
|
|
3809
|
+
}
|
|
3810
|
+
}
|
|
3811
|
+
}
|
|
3812
|
+
},
|
|
3813
|
+
"401": { "$ref": "#/components/responses/Unauthorized" },
|
|
3814
|
+
"403": { "$ref": "#/components/responses/Forbidden" },
|
|
3815
|
+
"404": { "$ref": "#/components/responses/NotFound" },
|
|
3445
3816
|
"409": { "$ref": "#/components/responses/Conflict" },
|
|
3446
3817
|
"502": { "$ref": "#/components/responses/ProviderOutcomeUnknown" },
|
|
3447
3818
|
"503": { "$ref": "#/components/responses/ProviderRetryable" }
|
|
@@ -3462,7 +3833,8 @@
|
|
|
3462
3833
|
"security": [
|
|
3463
3834
|
{ "clerkSession": [] },
|
|
3464
3835
|
{ "bearerCredential": [] },
|
|
3465
|
-
{ "bearerGrant": [] }
|
|
3836
|
+
{ "bearerGrant": [] },
|
|
3837
|
+
{ "bearerAccountCredential": [] }
|
|
3466
3838
|
],
|
|
3467
3839
|
"x-required-scopes": ["connections:write"],
|
|
3468
3840
|
"requestBody": {
|
|
@@ -3573,7 +3945,8 @@
|
|
|
3573
3945
|
"security": [
|
|
3574
3946
|
{ "clerkSession": [] },
|
|
3575
3947
|
{ "bearerCredential": [] },
|
|
3576
|
-
{ "bearerGrant": [] }
|
|
3948
|
+
{ "bearerGrant": [] },
|
|
3949
|
+
{ "bearerAccountCredential": [] }
|
|
3577
3950
|
],
|
|
3578
3951
|
"x-required-scopes": ["connections:write"],
|
|
3579
3952
|
"responses": {
|
|
@@ -3639,7 +4012,8 @@
|
|
|
3639
4012
|
"security": [
|
|
3640
4013
|
{ "clerkSession": [] },
|
|
3641
4014
|
{ "bearerCredential": [] },
|
|
3642
|
-
{ "bearerGrant": [] }
|
|
4015
|
+
{ "bearerGrant": [] },
|
|
4016
|
+
{ "bearerAccountCredential": [] }
|
|
3643
4017
|
],
|
|
3644
4018
|
"x-required-scopes": ["connections:write"],
|
|
3645
4019
|
"responses": {
|
|
@@ -3691,6 +4065,352 @@
|
|
|
3691
4065
|
}
|
|
3692
4066
|
}
|
|
3693
4067
|
},
|
|
4068
|
+
"/v1/projects/{project_id}/environments/{environment_id}/channel-connections/{connection_id}/app-manifest": {
|
|
4069
|
+
"parameters": [
|
|
4070
|
+
{ "$ref": "#/components/parameters/ProjectId" },
|
|
4071
|
+
{ "$ref": "#/components/parameters/EnvironmentId" },
|
|
4072
|
+
{ "$ref": "#/components/parameters/ChannelConnectionId" }
|
|
4073
|
+
],
|
|
4074
|
+
"get": {
|
|
4075
|
+
"operationId": "getChannelConnectionAppManifest",
|
|
4076
|
+
"summary": "Read the generated app configuration a customer-owned setup will create",
|
|
4077
|
+
"description": "Returns the exact configuration WhooshBang generated for this\ncustomer-owned Slack setup — the scopes it will ask for, the request\nURLs it will point at, the literal manifest document, and the same\nprefilled creation link the setup already carries.\n\nIt exists so the owner can read what they are about to approve, and a\nreviewer can check it, without opening Slack or decoding a URL. It\ncarries configuration only: no account, project, or environment, no\ncredential, and no provider-native identifier.\n\nOnly a `customer_managed` Slack connection has one. Every other\nprovider and mode answers `404`, because a manifest they do not have is\nnot a manifest that is empty.\n",
|
|
4078
|
+
"tags": ["Channel connections"],
|
|
4079
|
+
"security": [
|
|
4080
|
+
{ "clerkSession": [] },
|
|
4081
|
+
{ "bearerCredential": [] },
|
|
4082
|
+
{ "bearerGrant": [] },
|
|
4083
|
+
{ "bearerAccountCredential": [] }
|
|
4084
|
+
],
|
|
4085
|
+
"x-required-scopes": ["connections:read"],
|
|
4086
|
+
"responses": {
|
|
4087
|
+
"200": {
|
|
4088
|
+
"description": "The generated app configuration for exactly this setup.",
|
|
4089
|
+
"headers": {
|
|
4090
|
+
"WhooshBang-Diagnostic-Id": {
|
|
4091
|
+
"$ref": "#/components/headers/DiagnosticId"
|
|
4092
|
+
}
|
|
4093
|
+
},
|
|
4094
|
+
"content": {
|
|
4095
|
+
"application/json": {
|
|
4096
|
+
"schema": {
|
|
4097
|
+
"$ref": "#/components/schemas/CustomerOwnedSlackAppManifest"
|
|
4098
|
+
},
|
|
4099
|
+
"examples": {
|
|
4100
|
+
"manifest": {
|
|
4101
|
+
"value": {
|
|
4102
|
+
"creation_url": "https://api.slack.com/apps?new_app=1&manifest_json=%7B%22_metadata%22%3A%7B%22major_version%22%3A1%7D%7D",
|
|
4103
|
+
"app_name": "Acme Notifications",
|
|
4104
|
+
"bot_display_name": "Acme Notify",
|
|
4105
|
+
"description": "Delivery notifications from Acme.",
|
|
4106
|
+
"bot_scopes": ["chat:write"],
|
|
4107
|
+
"user_scopes": ["openid"],
|
|
4108
|
+
"request_urls": {
|
|
4109
|
+
"events": "https://api.whooshbang.com/internal/webhooks/slack/apps/CTBQNURSpqxDgLcqvbEDcA/events",
|
|
4110
|
+
"interactions": "https://api.whooshbang.com/internal/webhooks/slack/apps/CTBQNURSpqxDgLcqvbEDcA/interactions",
|
|
4111
|
+
"installation_redirect": "https://api.whooshbang.com/v1/provider-authorizations/slack/apps/callback",
|
|
4112
|
+
"recipient_redirect": "https://api.whooshbang.com/v1/provider-authorizations/slack/apps/recipient/callback"
|
|
4113
|
+
},
|
|
4114
|
+
"manifest": {
|
|
4115
|
+
"_metadata": { "major_version": 1, "minor_version": 1 },
|
|
4116
|
+
"display_information": {
|
|
4117
|
+
"name": "Acme Notifications",
|
|
4118
|
+
"description": "Delivery notifications from Acme."
|
|
4119
|
+
},
|
|
4120
|
+
"features": {
|
|
4121
|
+
"app_home": {
|
|
4122
|
+
"home_tab_enabled": true,
|
|
4123
|
+
"messages_tab_enabled": true,
|
|
4124
|
+
"messages_tab_read_only_enabled": true
|
|
4125
|
+
},
|
|
4126
|
+
"bot_user": {
|
|
4127
|
+
"display_name": "Acme Notify",
|
|
4128
|
+
"always_online": false
|
|
4129
|
+
}
|
|
4130
|
+
},
|
|
4131
|
+
"oauth_config": {
|
|
4132
|
+
"pkce_enabled": false,
|
|
4133
|
+
"redirect_urls": [
|
|
4134
|
+
"https://api.whooshbang.com/v1/provider-authorizations/slack/apps/callback",
|
|
4135
|
+
"https://api.whooshbang.com/v1/provider-authorizations/slack/apps/recipient/callback"
|
|
4136
|
+
],
|
|
4137
|
+
"scopes": {
|
|
4138
|
+
"bot": ["chat:write"],
|
|
4139
|
+
"user": ["openid"]
|
|
4140
|
+
}
|
|
4141
|
+
},
|
|
4142
|
+
"settings": {
|
|
4143
|
+
"event_subscriptions": {
|
|
4144
|
+
"request_url": "https://api.whooshbang.com/internal/webhooks/slack/apps/CTBQNURSpqxDgLcqvbEDcA/events",
|
|
4145
|
+
"bot_events": [
|
|
4146
|
+
"app_home_opened",
|
|
4147
|
+
"app_uninstalled",
|
|
4148
|
+
"tokens_revoked"
|
|
4149
|
+
]
|
|
4150
|
+
},
|
|
4151
|
+
"interactivity": {
|
|
4152
|
+
"is_enabled": true,
|
|
4153
|
+
"request_url": "https://api.whooshbang.com/internal/webhooks/slack/apps/CTBQNURSpqxDgLcqvbEDcA/interactions"
|
|
4154
|
+
},
|
|
4155
|
+
"is_hosted": false,
|
|
4156
|
+
"is_mcp_enabled": false,
|
|
4157
|
+
"org_deploy_enabled": false,
|
|
4158
|
+
"socket_mode_enabled": false,
|
|
4159
|
+
"token_rotation_enabled": true
|
|
4160
|
+
}
|
|
4161
|
+
},
|
|
4162
|
+
"diagnostic_id": "diag_slack_app_manifest_001"
|
|
4163
|
+
}
|
|
4164
|
+
}
|
|
4165
|
+
}
|
|
4166
|
+
}
|
|
4167
|
+
}
|
|
4168
|
+
},
|
|
4169
|
+
"401": { "$ref": "#/components/responses/Unauthorized" },
|
|
4170
|
+
"403": { "$ref": "#/components/responses/Forbidden" },
|
|
4171
|
+
"404": { "$ref": "#/components/responses/NotFound" }
|
|
4172
|
+
}
|
|
4173
|
+
}
|
|
4174
|
+
},
|
|
4175
|
+
"/v1/projects/{project_id}/environments/{environment_id}/channel-connections/{connection_id}/tests": {
|
|
4176
|
+
"parameters": [
|
|
4177
|
+
{ "$ref": "#/components/parameters/ProjectId" },
|
|
4178
|
+
{ "$ref": "#/components/parameters/EnvironmentId" },
|
|
4179
|
+
{ "$ref": "#/components/parameters/ChannelConnectionId" }
|
|
4180
|
+
],
|
|
4181
|
+
"get": {
|
|
4182
|
+
"operationId": "listConnectionTests",
|
|
4183
|
+
"summary": "List this connection's recent owner-triggered tests",
|
|
4184
|
+
"description": "Newest first. This is the audit-safe failure history for one\nconnection: what was tried, when, what came back, and what to do about\nit. Every entry is safe to render, log, and paste into an issue.\n",
|
|
4185
|
+
"tags": ["Channel connections"],
|
|
4186
|
+
"security": [
|
|
4187
|
+
{ "clerkSession": [] },
|
|
4188
|
+
{ "bearerCredential": [] },
|
|
4189
|
+
{ "bearerGrant": [] },
|
|
4190
|
+
{ "bearerAccountCredential": [] }
|
|
4191
|
+
],
|
|
4192
|
+
"x-required-scopes": ["connections:read"],
|
|
4193
|
+
"parameters": [
|
|
4194
|
+
{ "$ref": "#/components/parameters/ListAfterCursor" },
|
|
4195
|
+
{ "$ref": "#/components/parameters/ListPageLimit" },
|
|
4196
|
+
{ "$ref": "#/components/parameters/PageNumber" }
|
|
4197
|
+
],
|
|
4198
|
+
"responses": {
|
|
4199
|
+
"200": {
|
|
4200
|
+
"description": "Tests placed against exactly this connection.",
|
|
4201
|
+
"headers": {
|
|
4202
|
+
"WhooshBang-Diagnostic-Id": {
|
|
4203
|
+
"$ref": "#/components/headers/DiagnosticId"
|
|
4204
|
+
}
|
|
4205
|
+
},
|
|
4206
|
+
"content": {
|
|
4207
|
+
"application/json": {
|
|
4208
|
+
"schema": {
|
|
4209
|
+
"$ref": "#/components/schemas/ConnectionTestCollection"
|
|
4210
|
+
},
|
|
4211
|
+
"examples": {
|
|
4212
|
+
"tests": {
|
|
4213
|
+
"value": {
|
|
4214
|
+
"items": [
|
|
4215
|
+
{
|
|
4216
|
+
"id": "cntest_11111111111111111111111111111111",
|
|
4217
|
+
"connection_id": "chcon_77777777777777777777777777777777",
|
|
4218
|
+
"kind": "signed_interaction",
|
|
4219
|
+
"status": "succeeded",
|
|
4220
|
+
"requested_at": "2026-09-02T09:00:00.000Z",
|
|
4221
|
+
"completed_at": "2026-09-02T09:00:22.000Z",
|
|
4222
|
+
"expires_at": "2026-09-02T09:15:00.000Z",
|
|
4223
|
+
"evidence": {
|
|
4224
|
+
"identity_handle": "Acme Notify",
|
|
4225
|
+
"workspace_display_name": "Acme",
|
|
4226
|
+
"accepted_at": "2026-09-02T09:00:01.000Z",
|
|
4227
|
+
"observed_at": "2026-09-02T09:00:22.000Z",
|
|
4228
|
+
"round_trip_ms": 22000,
|
|
4229
|
+
"signature_verified": true,
|
|
4230
|
+
"ingress_host": "api.whooshbang.com"
|
|
4231
|
+
},
|
|
4232
|
+
"recovery_action": "none",
|
|
4233
|
+
"diagnostic_id": "diag_connection_test_succeeded_001"
|
|
4234
|
+
},
|
|
4235
|
+
{
|
|
4236
|
+
"id": "cntest_22222222222222222222222222222222",
|
|
4237
|
+
"connection_id": "chcon_77777777777777777777777777777777",
|
|
4238
|
+
"kind": "outbound_notification",
|
|
4239
|
+
"status": "failed",
|
|
4240
|
+
"requested_at": "2026-09-02T08:40:00.000Z",
|
|
4241
|
+
"completed_at": "2026-09-02T08:40:02.000Z",
|
|
4242
|
+
"expires_at": "2026-09-02T08:55:00.000Z",
|
|
4243
|
+
"failure_code": "credential_unavailable",
|
|
4244
|
+
"recovery_action": "reinstall",
|
|
4245
|
+
"diagnostic_id": "diag_connection_test_failed_001"
|
|
4246
|
+
}
|
|
4247
|
+
],
|
|
4248
|
+
"page": 1,
|
|
4249
|
+
"page_size": 25,
|
|
4250
|
+
"total": 2
|
|
4251
|
+
}
|
|
4252
|
+
}
|
|
4253
|
+
}
|
|
4254
|
+
}
|
|
4255
|
+
}
|
|
4256
|
+
},
|
|
4257
|
+
"401": { "$ref": "#/components/responses/Unauthorized" },
|
|
4258
|
+
"403": { "$ref": "#/components/responses/Forbidden" },
|
|
4259
|
+
"404": { "$ref": "#/components/responses/NotFound" }
|
|
4260
|
+
}
|
|
4261
|
+
},
|
|
4262
|
+
"post": {
|
|
4263
|
+
"operationId": "createConnectionTest",
|
|
4264
|
+
"summary": "Prove this exact connection still works",
|
|
4265
|
+
"description": "Runs one test against one connection, on demand, and answers with what\nit observed rather than with a claim.\n\nAvailable on any Slack connection, whether the app is one the customer\nowns or the one WhooshBang operates. Every other provider and mode is\nrefused rather than reporting a pass it never ran.\n\n**A test addresses the workspace member who authorized this exact\nconnection, and nobody else.** For an app the customer owns that is the\nperson who installed it, once, for one connection. The\nWhooshBang-operated app's installation is shared across tenants, so\n\"whoever installed the app\" is not necessarily anybody the calling\ntenant named; what is recorded instead is the member who approved this\nenvironment's own connection, during its own authorization, and each\nlater authorization replaces it. One tenant therefore cannot cause a\nmessage to somebody another tenant authorized.\n\n`outbound_notification` sends one visible private message through this\nexact identity to that member, and settles when the provider accepts\nit. Nobody else can be addressed: a test cannot reach a channel, and it\ncannot reach a subscriber who has not authorized anything.\n\n`signed_interaction` sends that person a message with one button and\nthen waits. It settles when the provider calls this deployment's\ninteractivity URL and the signature over the untouched body verifies\nagainst the secret this connection custodies — the only proof that the\nsigning secret, the request URL, and public reachability are all\ncorrect at once. A window that closes with nothing arriving settles as\n`expired`, which says nobody pressed it, and a request that arrives and\ndoes not verify settles as `failed` with `unverified_request_received`.\n\nThe response is the placed test. An `outbound_notification` is usually\nalready settled when it returns; a `signed_interaction` is `pending`\nuntil somebody presses the button, so read it again or list this\nconnection's tests.\n\n**A provider failure is an answer, not a refusal.** If the connection\nhas not finished setup, or its grant is unavailable, or Slack refuses,\nrate-limits, or cannot be reached, the test is **recorded** and answers\n`201` carrying `status: failed`, a `failure_code` and a\n`recovery_action`. There is no `502` or `503` on this operation, and\nthat is deliberate: a diagnostic that leaves no trace is the one thing\nan audit-safe failure history cannot have, so the failure goes in the\nhistory rather than into a problem document nobody keeps.\n\nThree things are refused outright, because none describes a test that\nran: the connection is archived, it is not a Slack connection, or it is\na WhooshBang-operated Slack connection whose authorization has been\nrevoked — `idempotency_conflict` 409, `invalid_connection_transition`\nat `/`. A connection that has never recorded who authorized it is not\nrefused: it is a recorded failure with\n`verification_target_unknown`, because there is nobody a test could\nhonestly address.\n\nA test never carries customer notification content, never counts as a\ndelivery to a subscriber, and never changes what any subscriber\nreceives.\n",
|
|
4266
|
+
"tags": ["Channel connections"],
|
|
4267
|
+
"security": [
|
|
4268
|
+
{ "clerkSession": [] },
|
|
4269
|
+
{ "bearerCredential": [] },
|
|
4270
|
+
{ "bearerGrant": [] },
|
|
4271
|
+
{ "bearerAccountCredential": [] }
|
|
4272
|
+
],
|
|
4273
|
+
"x-required-scopes": ["connections:write"],
|
|
4274
|
+
"x-idempotency": {
|
|
4275
|
+
"required": true,
|
|
4276
|
+
"retention_seconds": 604800,
|
|
4277
|
+
"scope": ["authenticated_environment", "operation_id", "key"],
|
|
4278
|
+
"equivalence": "exact_request_body",
|
|
4279
|
+
"replay_retention_extends": false
|
|
4280
|
+
},
|
|
4281
|
+
"requestBody": {
|
|
4282
|
+
"required": true,
|
|
4283
|
+
"content": {
|
|
4284
|
+
"application/json": {
|
|
4285
|
+
"schema": {
|
|
4286
|
+
"$ref": "#/components/schemas/CreateConnectionTestRequest"
|
|
4287
|
+
},
|
|
4288
|
+
"examples": {
|
|
4289
|
+
"signedInteraction": {
|
|
4290
|
+
"value": { "kind": "signed_interaction" }
|
|
4291
|
+
}
|
|
4292
|
+
}
|
|
4293
|
+
}
|
|
4294
|
+
}
|
|
4295
|
+
},
|
|
4296
|
+
"responses": {
|
|
4297
|
+
"200": {
|
|
4298
|
+
"description": "The equivalent test an idempotent replay already placed.",
|
|
4299
|
+
"headers": {
|
|
4300
|
+
"WhooshBang-Diagnostic-Id": {
|
|
4301
|
+
"$ref": "#/components/headers/DiagnosticId"
|
|
4302
|
+
}
|
|
4303
|
+
},
|
|
4304
|
+
"content": {
|
|
4305
|
+
"application/json": {
|
|
4306
|
+
"schema": { "$ref": "#/components/schemas/ConnectionTest" }
|
|
4307
|
+
}
|
|
4308
|
+
}
|
|
4309
|
+
},
|
|
4310
|
+
"201": {
|
|
4311
|
+
"description": "The placed test, settled if it could settle immediately.",
|
|
4312
|
+
"headers": {
|
|
4313
|
+
"WhooshBang-Diagnostic-Id": {
|
|
4314
|
+
"$ref": "#/components/headers/DiagnosticId"
|
|
4315
|
+
}
|
|
4316
|
+
},
|
|
4317
|
+
"content": {
|
|
4318
|
+
"application/json": {
|
|
4319
|
+
"schema": { "$ref": "#/components/schemas/ConnectionTest" },
|
|
4320
|
+
"examples": {
|
|
4321
|
+
"pending": {
|
|
4322
|
+
"value": {
|
|
4323
|
+
"id": "cntest_11111111111111111111111111111111",
|
|
4324
|
+
"connection_id": "chcon_77777777777777777777777777777777",
|
|
4325
|
+
"kind": "signed_interaction",
|
|
4326
|
+
"status": "pending",
|
|
4327
|
+
"requested_at": "2026-09-02T09:00:00.000Z",
|
|
4328
|
+
"expires_at": "2026-09-02T09:15:00.000Z",
|
|
4329
|
+
"evidence": {
|
|
4330
|
+
"identity_handle": "Acme Notify",
|
|
4331
|
+
"workspace_display_name": "Acme",
|
|
4332
|
+
"accepted_at": "2026-09-02T09:00:01.000Z",
|
|
4333
|
+
"ingress_host": "api.whooshbang.com"
|
|
4334
|
+
},
|
|
4335
|
+
"recovery_action": "none",
|
|
4336
|
+
"diagnostic_id": "diag_connection_test_pending_001"
|
|
4337
|
+
}
|
|
4338
|
+
}
|
|
4339
|
+
}
|
|
4340
|
+
}
|
|
4341
|
+
}
|
|
4342
|
+
},
|
|
4343
|
+
"400": { "$ref": "#/components/responses/BadRequest" },
|
|
4344
|
+
"401": { "$ref": "#/components/responses/Unauthorized" },
|
|
4345
|
+
"403": { "$ref": "#/components/responses/Forbidden" },
|
|
4346
|
+
"404": { "$ref": "#/components/responses/NotFound" },
|
|
4347
|
+
"409": { "$ref": "#/components/responses/Conflict" }
|
|
4348
|
+
}
|
|
4349
|
+
}
|
|
4350
|
+
},
|
|
4351
|
+
"/v1/projects/{project_id}/environments/{environment_id}/channel-connections/{connection_id}/tests/{test_id}": {
|
|
4352
|
+
"parameters": [
|
|
4353
|
+
{ "$ref": "#/components/parameters/ProjectId" },
|
|
4354
|
+
{ "$ref": "#/components/parameters/EnvironmentId" },
|
|
4355
|
+
{ "$ref": "#/components/parameters/ChannelConnectionId" },
|
|
4356
|
+
{ "$ref": "#/components/parameters/ConnectionTestId" }
|
|
4357
|
+
],
|
|
4358
|
+
"get": {
|
|
4359
|
+
"operationId": "getConnectionTest",
|
|
4360
|
+
"summary": "Read one owner-triggered connection test",
|
|
4361
|
+
"description": "The durable answer to one test. A `signed_interaction` is read again\nhere until it settles; a pending test whose window has closed reads as\n`expired` on the way out rather than waiting for a sweep, so the status\nthe owner sees is the one they actually have.\n",
|
|
4362
|
+
"tags": ["Channel connections"],
|
|
4363
|
+
"security": [
|
|
4364
|
+
{ "clerkSession": [] },
|
|
4365
|
+
{ "bearerCredential": [] },
|
|
4366
|
+
{ "bearerGrant": [] },
|
|
4367
|
+
{ "bearerAccountCredential": [] }
|
|
4368
|
+
],
|
|
4369
|
+
"x-required-scopes": ["connections:read"],
|
|
4370
|
+
"responses": {
|
|
4371
|
+
"200": {
|
|
4372
|
+
"description": "The exact test.",
|
|
4373
|
+
"headers": {
|
|
4374
|
+
"WhooshBang-Diagnostic-Id": {
|
|
4375
|
+
"$ref": "#/components/headers/DiagnosticId"
|
|
4376
|
+
}
|
|
4377
|
+
},
|
|
4378
|
+
"content": {
|
|
4379
|
+
"application/json": {
|
|
4380
|
+
"schema": { "$ref": "#/components/schemas/ConnectionTest" },
|
|
4381
|
+
"examples": {
|
|
4382
|
+
"succeeded": {
|
|
4383
|
+
"value": {
|
|
4384
|
+
"id": "cntest_11111111111111111111111111111111",
|
|
4385
|
+
"connection_id": "chcon_77777777777777777777777777777777",
|
|
4386
|
+
"kind": "signed_interaction",
|
|
4387
|
+
"status": "succeeded",
|
|
4388
|
+
"requested_at": "2026-09-02T09:00:00.000Z",
|
|
4389
|
+
"completed_at": "2026-09-02T09:00:22.000Z",
|
|
4390
|
+
"expires_at": "2026-09-02T09:15:00.000Z",
|
|
4391
|
+
"evidence": {
|
|
4392
|
+
"identity_handle": "Acme Notify",
|
|
4393
|
+
"workspace_display_name": "Acme",
|
|
4394
|
+
"accepted_at": "2026-09-02T09:00:01.000Z",
|
|
4395
|
+
"observed_at": "2026-09-02T09:00:22.000Z",
|
|
4396
|
+
"round_trip_ms": 22000,
|
|
4397
|
+
"signature_verified": true,
|
|
4398
|
+
"ingress_host": "api.whooshbang.com"
|
|
4399
|
+
},
|
|
4400
|
+
"recovery_action": "none",
|
|
4401
|
+
"diagnostic_id": "diag_connection_test_succeeded_001"
|
|
4402
|
+
}
|
|
4403
|
+
}
|
|
4404
|
+
}
|
|
4405
|
+
}
|
|
4406
|
+
}
|
|
4407
|
+
},
|
|
4408
|
+
"401": { "$ref": "#/components/responses/Unauthorized" },
|
|
4409
|
+
"403": { "$ref": "#/components/responses/Forbidden" },
|
|
4410
|
+
"404": { "$ref": "#/components/responses/NotFound" }
|
|
4411
|
+
}
|
|
4412
|
+
}
|
|
4413
|
+
},
|
|
3694
4414
|
"/v1/projects/{project_id}/environments/{environment_id}/notifier-connections": {
|
|
3695
4415
|
"parameters": [
|
|
3696
4416
|
{ "$ref": "#/components/parameters/ProjectId" },
|
|
@@ -3704,7 +4424,8 @@
|
|
|
3704
4424
|
"security": [
|
|
3705
4425
|
{ "clerkSession": [] },
|
|
3706
4426
|
{ "bearerCredential": [] },
|
|
3707
|
-
{ "bearerGrant": [] }
|
|
4427
|
+
{ "bearerGrant": [] },
|
|
4428
|
+
{ "bearerAccountCredential": [] }
|
|
3708
4429
|
],
|
|
3709
4430
|
"x-required-scopes": ["connections:read"],
|
|
3710
4431
|
"parameters": [
|
|
@@ -3762,7 +4483,8 @@
|
|
|
3762
4483
|
"security": [
|
|
3763
4484
|
{ "clerkSession": [] },
|
|
3764
4485
|
{ "bearerCredential": [] },
|
|
3765
|
-
{ "bearerGrant": [] }
|
|
4486
|
+
{ "bearerGrant": [] },
|
|
4487
|
+
{ "bearerAccountCredential": [] }
|
|
3766
4488
|
],
|
|
3767
4489
|
"x-required-scopes": ["connections:write"],
|
|
3768
4490
|
"requestBody": {
|
|
@@ -3843,7 +4565,8 @@
|
|
|
3843
4565
|
"security": [
|
|
3844
4566
|
{ "clerkSession": [] },
|
|
3845
4567
|
{ "bearerCredential": [] },
|
|
3846
|
-
{ "bearerGrant": [] }
|
|
4568
|
+
{ "bearerGrant": [] },
|
|
4569
|
+
{ "bearerAccountCredential": [] }
|
|
3847
4570
|
],
|
|
3848
4571
|
"x-required-scopes": ["connections:write"],
|
|
3849
4572
|
"responses": {
|
|
@@ -4055,7 +4778,8 @@
|
|
|
4055
4778
|
"security": [
|
|
4056
4779
|
{ "clerkSession": [] },
|
|
4057
4780
|
{ "bearerGrant": [] },
|
|
4058
|
-
{ "bearerCredential": [] }
|
|
4781
|
+
{ "bearerCredential": [] },
|
|
4782
|
+
{ "bearerAccountCredential": [] }
|
|
4059
4783
|
],
|
|
4060
4784
|
"x-required-scopes": ["projects:read"],
|
|
4061
4785
|
"x-account-membership": "required",
|
|
@@ -4103,7 +4827,8 @@
|
|
|
4103
4827
|
"security": [
|
|
4104
4828
|
{ "clerkSession": [] },
|
|
4105
4829
|
{ "bearerGrant": [] },
|
|
4106
|
-
{ "bearerCredential": [] }
|
|
4830
|
+
{ "bearerCredential": [] },
|
|
4831
|
+
{ "bearerAccountCredential": [] }
|
|
4107
4832
|
],
|
|
4108
4833
|
"x-required-scopes": ["projects:write"],
|
|
4109
4834
|
"x-account-membership": "required",
|
|
@@ -5279,7 +6004,7 @@
|
|
|
5279
6004
|
"summary": "Create a subscription link in a named project environment",
|
|
5280
6005
|
"description": "The environment-scoped form of `createSubscriptionLink`, for a caller\nwhose authority selects an account rather than a project. Behavior,\nrequest, and response are identical; the path names the scope the\ncredential form takes from its bearer, and the service checks it\nagainst the granting person's membership before acting.\n",
|
|
5281
6006
|
"tags": ["Subscription links"],
|
|
5282
|
-
"security": [{ "bearerGrant": [] }],
|
|
6007
|
+
"security": [{ "bearerGrant": [] }, { "bearerAccountCredential": [] }],
|
|
5283
6008
|
"x-required-scopes": ["subscriptions:write"],
|
|
5284
6009
|
"x-idempotency": {
|
|
5285
6010
|
"required": true,
|
|
@@ -5361,7 +6086,7 @@
|
|
|
5361
6086
|
"summary": "Inspect a subscription link in a named project environment",
|
|
5362
6087
|
"description": "The environment-scoped form of `getSubscriptionLink`. A link outside the\nnamed environment is absent, exactly as it is for a credential.\n",
|
|
5363
6088
|
"tags": ["Subscription links"],
|
|
5364
|
-
"security": [{ "bearerGrant": [] }],
|
|
6089
|
+
"security": [{ "bearerGrant": [] }, { "bearerAccountCredential": [] }],
|
|
5365
6090
|
"x-required-scopes": ["subscriptions:read"],
|
|
5366
6091
|
"responses": {
|
|
5367
6092
|
"200": {
|
|
@@ -5431,7 +6156,7 @@
|
|
|
5431
6156
|
"summary": "Create an asynchronous message in a named project environment",
|
|
5432
6157
|
"description": "The environment-scoped form of `createMessage`. The path names the\naccount, project, and environment the credential form takes from its\nbearer; test and live remain a property of the named environment and are\nnever selected by the request body.\n",
|
|
5433
6158
|
"tags": ["Messages"],
|
|
5434
|
-
"security": [{ "bearerGrant": [] }],
|
|
6159
|
+
"security": [{ "bearerGrant": [] }, { "bearerAccountCredential": [] }],
|
|
5435
6160
|
"x-required-scopes": ["messages:write"],
|
|
5436
6161
|
"x-idempotency": {
|
|
5437
6162
|
"required": true,
|
|
@@ -5520,7 +6245,7 @@
|
|
|
5520
6245
|
{ "$ref": "#/components/parameters/ResponseInspectionWait" }
|
|
5521
6246
|
],
|
|
5522
6247
|
"tags": ["Messages"],
|
|
5523
|
-
"security": [{ "bearerGrant": [] }],
|
|
6248
|
+
"security": [{ "bearerGrant": [] }, { "bearerAccountCredential": [] }],
|
|
5524
6249
|
"x-required-scopes": ["messages:read"],
|
|
5525
6250
|
"responses": {
|
|
5526
6251
|
"200": {
|
|
@@ -5591,7 +6316,7 @@
|
|
|
5591
6316
|
{ "$ref": "#/components/parameters/ResponseInspectionWait" }
|
|
5592
6317
|
],
|
|
5593
6318
|
"tags": ["Messages"],
|
|
5594
|
-
"security": [{ "bearerGrant": [] }],
|
|
6319
|
+
"security": [{ "bearerGrant": [] }, { "bearerAccountCredential": [] }],
|
|
5595
6320
|
"x-required-scopes": ["messages:read"],
|
|
5596
6321
|
"responses": {
|
|
5597
6322
|
"200": {
|
|
@@ -5639,7 +6364,7 @@
|
|
|
5639
6364
|
"summary": "Create a destination-bound machine client in a named project environment",
|
|
5640
6365
|
"description": "The environment-scoped form of `createMachineClient`. Scope remains the\nexact bound destination; the path only names where the client is created.\n",
|
|
5641
6366
|
"tags": ["Machine clients"],
|
|
5642
|
-
"security": [{ "bearerGrant": [] }],
|
|
6367
|
+
"security": [{ "bearerGrant": [] }, { "bearerAccountCredential": [] }],
|
|
5643
6368
|
"x-required-scopes": ["machine-clients:write"],
|
|
5644
6369
|
"x-idempotency": {
|
|
5645
6370
|
"required": true,
|
|
@@ -5706,24 +6431,91 @@
|
|
|
5706
6431
|
}
|
|
5707
6432
|
}
|
|
5708
6433
|
},
|
|
5709
|
-
"/v1/projects/{project_id}/environments/{environment_id}/
|
|
6434
|
+
"/v1/projects/{project_id}/environments/{environment_id}/machine-clients/{machine_client_id}/credentials": {
|
|
5710
6435
|
"parameters": [
|
|
5711
6436
|
{ "$ref": "#/components/parameters/ProjectId" },
|
|
5712
|
-
{ "$ref": "#/components/parameters/EnvironmentId" }
|
|
6437
|
+
{ "$ref": "#/components/parameters/EnvironmentId" },
|
|
6438
|
+
{ "$ref": "#/components/parameters/MachineClientId" }
|
|
5713
6439
|
],
|
|
5714
|
-
"
|
|
5715
|
-
"operationId": "
|
|
5716
|
-
"summary": "
|
|
5717
|
-
"description": "The environment-scoped form of `
|
|
5718
|
-
"tags": ["
|
|
5719
|
-
"security": [{ "bearerGrant": [] }],
|
|
5720
|
-
"x-required-scopes": ["
|
|
5721
|
-
"
|
|
5722
|
-
"
|
|
5723
|
-
|
|
5724
|
-
|
|
5725
|
-
|
|
5726
|
-
|
|
6440
|
+
"post": {
|
|
6441
|
+
"operationId": "registerMachineCredentialInEnvironment",
|
|
6442
|
+
"summary": "Register a machine-credential digest in a named project environment",
|
|
6443
|
+
"description": "The environment-scoped form of `registerMachineCredential`. Registers\nonly the credential ID and SHA-256 digest; the path only names where the\nowning machine client lives. Request and response shapes are that\noperation's exactly, including its published examples.\n",
|
|
6444
|
+
"tags": ["Machine clients"],
|
|
6445
|
+
"security": [{ "bearerGrant": [] }, { "bearerAccountCredential": [] }],
|
|
6446
|
+
"x-required-scopes": ["machine-clients:write"],
|
|
6447
|
+
"x-idempotency": {
|
|
6448
|
+
"required": true,
|
|
6449
|
+
"retention_seconds": 604800,
|
|
6450
|
+
"scope": ["authenticated_environment", "operation_id", "key"],
|
|
6451
|
+
"equivalence": "validated_request_after_documented_defaults",
|
|
6452
|
+
"replay_retention_extends": false
|
|
6453
|
+
},
|
|
6454
|
+
"parameters": [{ "$ref": "#/components/parameters/IdempotencyKey" }],
|
|
6455
|
+
"requestBody": {
|
|
6456
|
+
"required": true,
|
|
6457
|
+
"content": {
|
|
6458
|
+
"application/json": {
|
|
6459
|
+
"schema": {
|
|
6460
|
+
"$ref": "#/components/schemas/RegisterMachineCredentialRequest"
|
|
6461
|
+
}
|
|
6462
|
+
}
|
|
6463
|
+
}
|
|
6464
|
+
},
|
|
6465
|
+
"responses": {
|
|
6466
|
+
"200": {
|
|
6467
|
+
"description": "Existing safe credential metadata on an equivalent registration replay; never secret or digest.",
|
|
6468
|
+
"headers": {
|
|
6469
|
+
"WhooshBang-Diagnostic-Id": {
|
|
6470
|
+
"$ref": "#/components/headers/DiagnosticId"
|
|
6471
|
+
}
|
|
6472
|
+
},
|
|
6473
|
+
"content": {
|
|
6474
|
+
"application/json": {
|
|
6475
|
+
"schema": { "$ref": "#/components/schemas/MachineCredential" }
|
|
6476
|
+
}
|
|
6477
|
+
}
|
|
6478
|
+
},
|
|
6479
|
+
"201": {
|
|
6480
|
+
"description": "Newly registered safe credential metadata; never secret or digest.",
|
|
6481
|
+
"headers": {
|
|
6482
|
+
"WhooshBang-Diagnostic-Id": {
|
|
6483
|
+
"$ref": "#/components/headers/DiagnosticId"
|
|
6484
|
+
}
|
|
6485
|
+
},
|
|
6486
|
+
"content": {
|
|
6487
|
+
"application/json": {
|
|
6488
|
+
"schema": { "$ref": "#/components/schemas/MachineCredential" }
|
|
6489
|
+
}
|
|
6490
|
+
}
|
|
6491
|
+
},
|
|
6492
|
+
"400": { "$ref": "#/components/responses/BadRequest" },
|
|
6493
|
+
"401": { "$ref": "#/components/responses/Unauthorized" },
|
|
6494
|
+
"403": { "$ref": "#/components/responses/Forbidden" },
|
|
6495
|
+
"404": { "$ref": "#/components/responses/NotFound" },
|
|
6496
|
+
"409": { "$ref": "#/components/responses/Conflict" },
|
|
6497
|
+
"429": { "$ref": "#/components/responses/RateLimited" }
|
|
6498
|
+
}
|
|
6499
|
+
}
|
|
6500
|
+
},
|
|
6501
|
+
"/v1/projects/{project_id}/environments/{environment_id}/capabilities": {
|
|
6502
|
+
"parameters": [
|
|
6503
|
+
{ "$ref": "#/components/parameters/ProjectId" },
|
|
6504
|
+
{ "$ref": "#/components/parameters/EnvironmentId" }
|
|
6505
|
+
],
|
|
6506
|
+
"get": {
|
|
6507
|
+
"operationId": "getCapabilitiesInEnvironment",
|
|
6508
|
+
"summary": "Inspect provider-neutral capabilities for a named project environment",
|
|
6509
|
+
"description": "The environment-scoped form of `getCapabilities`. The vocabulary is\nprovider-neutral and identical; the path names the environment whose\nactive degradation is reported.\n",
|
|
6510
|
+
"tags": ["Capabilities"],
|
|
6511
|
+
"security": [{ "bearerGrant": [] }, { "bearerAccountCredential": [] }],
|
|
6512
|
+
"x-required-scopes": ["capabilities:read"],
|
|
6513
|
+
"responses": {
|
|
6514
|
+
"200": {
|
|
6515
|
+
"description": "Current stable capability vocabulary and active degradation.",
|
|
6516
|
+
"headers": {
|
|
6517
|
+
"WhooshBang-Diagnostic-Id": {
|
|
6518
|
+
"$ref": "#/components/headers/DiagnosticId"
|
|
5727
6519
|
}
|
|
5728
6520
|
},
|
|
5729
6521
|
"content": {
|
|
@@ -5803,7 +6595,8 @@
|
|
|
5803
6595
|
"security": [
|
|
5804
6596
|
{ "clerkSession": [] },
|
|
5805
6597
|
{ "bearerCredential": [] },
|
|
5806
|
-
{ "bearerGrant": [] }
|
|
6598
|
+
{ "bearerGrant": [] },
|
|
6599
|
+
{ "bearerAccountCredential": [] }
|
|
5807
6600
|
],
|
|
5808
6601
|
"x-required-scopes": ["subscriptions:write"],
|
|
5809
6602
|
"x-idempotency": {
|
|
@@ -5880,7 +6673,8 @@
|
|
|
5880
6673
|
"security": [
|
|
5881
6674
|
{ "clerkSession": [] },
|
|
5882
6675
|
{ "bearerCredential": [] },
|
|
5883
|
-
{ "bearerGrant": [] }
|
|
6676
|
+
{ "bearerGrant": [] },
|
|
6677
|
+
{ "bearerAccountCredential": [] }
|
|
5884
6678
|
],
|
|
5885
6679
|
"x-required-scopes": ["subscriptions:read"],
|
|
5886
6680
|
"responses": {
|
|
@@ -5911,7 +6705,8 @@
|
|
|
5911
6705
|
"security": [
|
|
5912
6706
|
{ "clerkSession": [] },
|
|
5913
6707
|
{ "bearerCredential": [] },
|
|
5914
|
-
{ "bearerGrant": [] }
|
|
6708
|
+
{ "bearerGrant": [] },
|
|
6709
|
+
{ "bearerAccountCredential": [] }
|
|
5915
6710
|
],
|
|
5916
6711
|
"x-required-scopes": ["subscriptions:read"],
|
|
5917
6712
|
"parameters": [
|
|
@@ -5963,7 +6758,8 @@
|
|
|
5963
6758
|
"security": [
|
|
5964
6759
|
{ "clerkSession": [] },
|
|
5965
6760
|
{ "bearerCredential": [] },
|
|
5966
|
-
{ "bearerGrant": [] }
|
|
6761
|
+
{ "bearerGrant": [] },
|
|
6762
|
+
{ "bearerAccountCredential": [] }
|
|
5967
6763
|
],
|
|
5968
6764
|
"x-required-scopes": ["subscriptions:write"],
|
|
5969
6765
|
"x-idempotency": {
|
|
@@ -6051,7 +6847,8 @@
|
|
|
6051
6847
|
"security": [
|
|
6052
6848
|
{ "clerkSession": [] },
|
|
6053
6849
|
{ "bearerCredential": [] },
|
|
6054
|
-
{ "bearerGrant": [] }
|
|
6850
|
+
{ "bearerGrant": [] },
|
|
6851
|
+
{ "bearerAccountCredential": [] }
|
|
6055
6852
|
],
|
|
6056
6853
|
"x-required-scopes": ["subscriptions:read"],
|
|
6057
6854
|
"responses": {
|
|
@@ -6076,7 +6873,8 @@
|
|
|
6076
6873
|
"security": [
|
|
6077
6874
|
{ "clerkSession": [] },
|
|
6078
6875
|
{ "bearerCredential": [] },
|
|
6079
|
-
{ "bearerGrant": [] }
|
|
6876
|
+
{ "bearerGrant": [] },
|
|
6877
|
+
{ "bearerAccountCredential": [] }
|
|
6080
6878
|
],
|
|
6081
6879
|
"x-required-scopes": ["subscriptions:write"],
|
|
6082
6880
|
"requestBody": {
|
|
@@ -6113,7 +6911,8 @@
|
|
|
6113
6911
|
"security": [
|
|
6114
6912
|
{ "clerkSession": [] },
|
|
6115
6913
|
{ "bearerCredential": [] },
|
|
6116
|
-
{ "bearerGrant": [] }
|
|
6914
|
+
{ "bearerGrant": [] },
|
|
6915
|
+
{ "bearerAccountCredential": [] }
|
|
6117
6916
|
],
|
|
6118
6917
|
"x-required-scopes": ["subscriptions:write"],
|
|
6119
6918
|
"responses": {
|
|
@@ -6155,7 +6954,8 @@
|
|
|
6155
6954
|
"security": [
|
|
6156
6955
|
{ "clerkSession": [] },
|
|
6157
6956
|
{ "bearerCredential": [] },
|
|
6158
|
-
{ "bearerGrant": [] }
|
|
6957
|
+
{ "bearerGrant": [] },
|
|
6958
|
+
{ "bearerAccountCredential": [] }
|
|
6159
6959
|
],
|
|
6160
6960
|
"x-required-scopes": ["subscriptions:read"],
|
|
6161
6961
|
"parameters": [
|
|
@@ -6255,7 +7055,8 @@
|
|
|
6255
7055
|
"security": [
|
|
6256
7056
|
{ "clerkSession": [] },
|
|
6257
7057
|
{ "bearerCredential": [] },
|
|
6258
|
-
{ "bearerGrant": [] }
|
|
7058
|
+
{ "bearerGrant": [] },
|
|
7059
|
+
{ "bearerAccountCredential": [] }
|
|
6259
7060
|
],
|
|
6260
7061
|
"x-required-scopes": ["subscriptions:write"],
|
|
6261
7062
|
"responses": {
|
|
@@ -6298,7 +7099,8 @@
|
|
|
6298
7099
|
"security": [
|
|
6299
7100
|
{ "clerkSession": [] },
|
|
6300
7101
|
{ "bearerCredential": [] },
|
|
6301
|
-
{ "bearerGrant": [] }
|
|
7102
|
+
{ "bearerGrant": [] },
|
|
7103
|
+
{ "bearerAccountCredential": [] }
|
|
6302
7104
|
],
|
|
6303
7105
|
"x-required-scopes": ["subscriptions:write"],
|
|
6304
7106
|
"responses": {
|
|
@@ -6336,7 +7138,8 @@
|
|
|
6336
7138
|
"security": [
|
|
6337
7139
|
{ "clerkSession": [] },
|
|
6338
7140
|
{ "bearerCredential": [] },
|
|
6339
|
-
{ "bearerGrant": [] }
|
|
7141
|
+
{ "bearerGrant": [] },
|
|
7142
|
+
{ "bearerAccountCredential": [] }
|
|
6340
7143
|
],
|
|
6341
7144
|
"x-required-scopes": ["messages:write"],
|
|
6342
7145
|
"x-idempotency": {
|
|
@@ -6434,7 +7237,8 @@
|
|
|
6434
7237
|
"security": [
|
|
6435
7238
|
{ "clerkSession": [] },
|
|
6436
7239
|
{ "bearerCredential": [] },
|
|
6437
|
-
{ "bearerGrant": [] }
|
|
7240
|
+
{ "bearerGrant": [] },
|
|
7241
|
+
{ "bearerAccountCredential": [] }
|
|
6438
7242
|
],
|
|
6439
7243
|
"x-required-scopes": ["messages:read"],
|
|
6440
7244
|
"responses": {
|
|
@@ -6493,7 +7297,13 @@
|
|
|
6493
7297
|
"type": "http",
|
|
6494
7298
|
"scheme": "bearer",
|
|
6495
7299
|
"bearerFormat": "OAuth 2.0 access token",
|
|
6496
|
-
"description": "An access token issued by the WhooshBang authorization server for one\nconsenting account member. It selects exactly one account and carries\nthe scopes that member consented to, and never a project: an operation\nnames its project and environment in the path, and the service checks\nboth against the granting person's membership before acting.
|
|
7300
|
+
"description": "An access token issued by the WhooshBang authorization server for one\nconsenting account member. It selects exactly one account and carries\nthe scopes that member consented to, and never a project: an operation\nnames its project and environment in the path, and the service checks\nboth against the granting person's membership before acting. A project\ncredential can name no project but its own; an account credential names\none per call the same way this does.\n"
|
|
7301
|
+
},
|
|
7302
|
+
"bearerAccountCredential": {
|
|
7303
|
+
"type": "http",
|
|
7304
|
+
"scheme": "bearer",
|
|
7305
|
+
"bearerFormat": "wb_ac1.acred_<16-byte-base64url>.<32-byte-base64url-secret>",
|
|
7306
|
+
"description": "Selects exactly one account and nothing below it. It names no project\nand no environment, so it authenticates only operations whose path does,\nand its authority there is the intersection of its scopes with the\nproject-credential vocabulary — never wider than a project credential\nwould have been for the same project environment. A path naming a\nproject outside its account is refused with `credential_account_mismatch`\nrather than a bare scope failure. `account:read` is the one scope it can\nhold that a project credential cannot, and it authorizes only the\naccount's project directory: listing the account's projects and reading\none, which is the account-wide question a project credential cannot ask\nand the reason a `--project <slug>` selector can work at all. It is\nrequired *in addition to* each of those operations' own\n`x-required-scopes`, never instead of them.\nCredentials cannot be read after creation.\n"
|
|
6497
7307
|
},
|
|
6498
7308
|
"bearerCredential": {
|
|
6499
7309
|
"type": "http",
|
|
@@ -6550,6 +7360,13 @@
|
|
|
6550
7360
|
"description": "Canonical opaque project credential identity; never a readable secret.",
|
|
6551
7361
|
"schema": { "$ref": "#/components/schemas/ApiCredentialId" }
|
|
6552
7362
|
},
|
|
7363
|
+
"AccountCredentialId": {
|
|
7364
|
+
"name": "account_credential_id",
|
|
7365
|
+
"in": "path",
|
|
7366
|
+
"required": true,
|
|
7367
|
+
"description": "Canonical opaque account credential identity; never a readable secret.",
|
|
7368
|
+
"schema": { "$ref": "#/components/schemas/AccountCredentialId" }
|
|
7369
|
+
},
|
|
6553
7370
|
"ApiCredentialAfterCursor": {
|
|
6554
7371
|
"name": "after",
|
|
6555
7372
|
"in": "query",
|
|
@@ -6678,6 +7495,13 @@
|
|
|
6678
7495
|
"description": "Opaque connection identity; never authorization.",
|
|
6679
7496
|
"schema": { "$ref": "#/components/schemas/OpaqueIdentifier" }
|
|
6680
7497
|
},
|
|
7498
|
+
"ConnectionTestId": {
|
|
7499
|
+
"name": "test_id",
|
|
7500
|
+
"in": "path",
|
|
7501
|
+
"required": true,
|
|
7502
|
+
"description": "Opaque identity of one owner-triggered connection test; never authorization.",
|
|
7503
|
+
"schema": { "$ref": "#/components/schemas/OpaqueIdentifier" }
|
|
7504
|
+
},
|
|
6681
7505
|
"NotifierId": {
|
|
6682
7506
|
"name": "notifier_id",
|
|
6683
7507
|
"in": "path",
|
|
@@ -6880,7 +7704,10 @@
|
|
|
6880
7704
|
},
|
|
6881
7705
|
"setup_modes": {
|
|
6882
7706
|
"type": "array",
|
|
6883
|
-
"
|
|
7707
|
+
"minItems": 1,
|
|
7708
|
+
"maxItems": 2,
|
|
7709
|
+
"uniqueItems": true,
|
|
7710
|
+
"items": { "enum": ["whooshbang_hosted", "customer_managed"] }
|
|
6884
7711
|
},
|
|
6885
7712
|
"lifecycle_operations": {
|
|
6886
7713
|
"type": "array",
|
|
@@ -6914,7 +7741,8 @@
|
|
|
6914
7741
|
"description": "One environment's provider-discriminated connection. Credentials and provider-native authority identifiers are structurally absent.",
|
|
6915
7742
|
"oneOf": [
|
|
6916
7743
|
{ "$ref": "#/components/schemas/TelegramChannelConnection" },
|
|
6917
|
-
{ "$ref": "#/components/schemas/HostedChannelConnection" }
|
|
7744
|
+
{ "$ref": "#/components/schemas/HostedChannelConnection" },
|
|
7745
|
+
{ "$ref": "#/components/schemas/CustomerOwnedSlackChannelConnection" }
|
|
6918
7746
|
]
|
|
6919
7747
|
},
|
|
6920
7748
|
"ChannelConnectionCollection": {
|
|
@@ -7024,6 +7852,47 @@
|
|
|
7024
7852
|
},
|
|
7025
7853
|
"additionalProperties": false
|
|
7026
7854
|
},
|
|
7855
|
+
{
|
|
7856
|
+
"type": "object",
|
|
7857
|
+
"description": "Starts a guided setup in which the customer creates and owns their own Slack app. The connection comes back `pending` with an `app_setup` carrying Slack's own creation link, already filled in with this app's manifest.\n\nThis arm carries no credential field, because at this point no credential exists: Slack issues the app's secrets when the owner presses Create. They arrive later through the write-only rotation boundary, and the bot grant never arrives from the caller at all — WhooshBang obtains it from Slack's installation exchange. The three names here are what the owner's whole workspace will see, which is why the caller chooses them rather than WhooshBang: a generated name would permanently brand a customer's own app with ours, and that is the one thing customer-owned modes exist to prevent.",
|
|
7858
|
+
"required": [
|
|
7859
|
+
"mode",
|
|
7860
|
+
"provider",
|
|
7861
|
+
"display_name",
|
|
7862
|
+
"app_name",
|
|
7863
|
+
"bot_display_name",
|
|
7864
|
+
"app_description"
|
|
7865
|
+
],
|
|
7866
|
+
"properties": {
|
|
7867
|
+
"mode": { "type": "string", "const": "customer_managed" },
|
|
7868
|
+
"provider": { "type": "string", "const": "slack" },
|
|
7869
|
+
"display_name": {
|
|
7870
|
+
"type": "string",
|
|
7871
|
+
"minLength": 1,
|
|
7872
|
+
"maxLength": 100,
|
|
7873
|
+
"description": "What this connection is called inside WhooshBang. Unrelated to what Slack shows the workspace."
|
|
7874
|
+
},
|
|
7875
|
+
"app_name": {
|
|
7876
|
+
"type": "string",
|
|
7877
|
+
"minLength": 1,
|
|
7878
|
+
"maxLength": 35,
|
|
7879
|
+
"description": "The app name Slack's creation dialog is prefilled with, constrained to Slack's own 35-character limit. The owner can still change it in Slack; this is a suggestion, not a reservation."
|
|
7880
|
+
},
|
|
7881
|
+
"bot_display_name": {
|
|
7882
|
+
"type": "string",
|
|
7883
|
+
"minLength": 1,
|
|
7884
|
+
"maxLength": 80,
|
|
7885
|
+
"description": "The bot display name in the same dialog — what a recipient reads at the top of the conversation. Slack's own 80-character limit."
|
|
7886
|
+
},
|
|
7887
|
+
"app_description": {
|
|
7888
|
+
"type": "string",
|
|
7889
|
+
"minLength": 1,
|
|
7890
|
+
"maxLength": 140,
|
|
7891
|
+
"description": "The short description Slack shows beside the app, within Slack's own 140-character limit."
|
|
7892
|
+
}
|
|
7893
|
+
},
|
|
7894
|
+
"additionalProperties": false
|
|
7895
|
+
},
|
|
7027
7896
|
{
|
|
7028
7897
|
"type": "object",
|
|
7029
7898
|
"description": "Starts the hosted authorization for a WhooshBang-owned distributed app installed independently at the provider authority Slack derives during OAuth. The caller supplies neither installation identity nor credential material.",
|
|
@@ -7057,8 +7926,43 @@
|
|
|
7057
7926
|
"additionalProperties": false
|
|
7058
7927
|
},
|
|
7059
7928
|
"RotateChannelConnectionCredentialRequest": {
|
|
7060
|
-
"description": "Replaces the secret behind a connection, discriminated by where a replacement can come from at all. A customer-owned bot has one only its owner can produce and therefore sends it; a managed bot has one only Telegram can issue and therefore sends nothing. Either way the connection keeps its provider identity, so a subscriber stays bound to the identity it consented to and only the secret changes.",
|
|
7929
|
+
"description": "Replaces the secret behind a connection, discriminated by where a replacement can come from at all. A customer-owned bot has one only its owner can produce and therefore sends it; a managed bot has one only Telegram can issue and therefore sends nothing. A customer-owned Slack app has three separate authorities and names which of them it is replacing. Either way the connection keeps its provider identity, so a subscriber stays bound to the identity it consented to and only the secret changes.\n\nThe first submission during setup uses this same boundary. Handing a secret over and replacing it are the same operation — a write that never reads the old value back — and giving initial setup its own endpoint would have meant two ways to do one thing, differing only in what the connection's status happened to be.",
|
|
7061
7930
|
"oneOf": [
|
|
7931
|
+
{
|
|
7932
|
+
"type": "object",
|
|
7933
|
+
"description": "Supplies or replaces the app-specific authorities of a Slack app the customer owns. All three are sent together, both for the first submission during setup and for every later replacement.\n\nSending only the one that changed would read as the kinder API, and it is the wrong shape: the three are sealed as one versioned set, so replacing one alone would mean decrypting the other two and writing them back — reading secrets at a boundary whose whole purpose is that nothing reads them. Slack shows all three on one screen, so re-entering them costs the owner a copy each and costs the system no read at all.\n\n`client_identity` is safe metadata and is echoed back; the other two are write-only and are never readable afterwards by any surface.\n\nThere is no bot-token field here, and the absence is the point: WhooshBang obtains the bot grant from Slack's installation exchange, so it never passes through a browser, a clipboard, this request, or any response.",
|
|
7934
|
+
"required": [
|
|
7935
|
+
"provider",
|
|
7936
|
+
"client_identity",
|
|
7937
|
+
"client_secret",
|
|
7938
|
+
"signing_secret"
|
|
7939
|
+
],
|
|
7940
|
+
"properties": {
|
|
7941
|
+
"provider": { "type": "string", "const": "slack" },
|
|
7942
|
+
"client_identity": {
|
|
7943
|
+
"type": "string",
|
|
7944
|
+
"minLength": 7,
|
|
7945
|
+
"maxLength": 66,
|
|
7946
|
+
"pattern": "^[0-9]{3,32}\\.[0-9]{3,32}$",
|
|
7947
|
+
"description": "The app's Client ID, in Slack's own `<digits>.<digits>` shape. Safe app metadata rather than an authority: it identifies the app in an OAuth redirect the workspace can already see, so unlike the other two it is echoed back."
|
|
7948
|
+
},
|
|
7949
|
+
"client_secret": {
|
|
7950
|
+
"type": "string",
|
|
7951
|
+
"minLength": 1,
|
|
7952
|
+
"maxLength": 512,
|
|
7953
|
+
"writeOnly": true,
|
|
7954
|
+
"description": "The app's Client Secret. Write-only: sealed on arrival, never echoed here or by any later read, and reachable only by the installation and identity exchanges — delivery cannot obtain it, and holding it sends no message."
|
|
7955
|
+
},
|
|
7956
|
+
"signing_secret": {
|
|
7957
|
+
"type": "string",
|
|
7958
|
+
"minLength": 1,
|
|
7959
|
+
"maxLength": 512,
|
|
7960
|
+
"writeOnly": true,
|
|
7961
|
+
"description": "The app's Signing Secret, which verifies that an inbound request really came from Slack. Write-only on the same terms, and reachable only by ingress verification."
|
|
7962
|
+
}
|
|
7963
|
+
},
|
|
7964
|
+
"additionalProperties": false
|
|
7965
|
+
},
|
|
7062
7966
|
{
|
|
7063
7967
|
"type": "object",
|
|
7064
7968
|
"description": "Replaces the customer-owned credential behind a connection. The connection keeps its provider identity, so a subscriber stays bound to the identity it consented to and only the secret changes; a credential for a different identity is refused.",
|
|
@@ -7089,6 +7993,245 @@
|
|
|
7089
7993
|
}
|
|
7090
7994
|
]
|
|
7091
7995
|
},
|
|
7996
|
+
"ConnectionTestKind": {
|
|
7997
|
+
"type": "string",
|
|
7998
|
+
"enum": ["outbound_notification", "signed_interaction"],
|
|
7999
|
+
"description": "Which half of a connection a test exercises. `outbound_notification` proves this exact identity can put a message in front of the person who set it up. `signed_interaction` proves the opposite direction: that the provider can reach this deployment's interactivity URL and that the request it signs verifies against the secret we hold. The two fail for entirely different reasons, so they are never collapsed into one result.",
|
|
8000
|
+
"$comment": "Provider-neutral vocabulary. Slack is the only provider that implements either today; a provider that cannot run one refuses it rather than reporting a pass it never proved."
|
|
8001
|
+
},
|
|
8002
|
+
"ConnectionTestRecoveryAction": {
|
|
8003
|
+
"type": "string",
|
|
8004
|
+
"enum": [
|
|
8005
|
+
"none",
|
|
8006
|
+
"complete_setup",
|
|
8007
|
+
"replace_credentials",
|
|
8008
|
+
"reinstall",
|
|
8009
|
+
"retry_later",
|
|
8010
|
+
"check_request_url",
|
|
8011
|
+
"contact_support"
|
|
8012
|
+
],
|
|
8013
|
+
"description": "The exact safe next action for one test result, in product vocabulary.\n\nIt is a different vocabulary from a connection's own recovery action because the actions genuinely differ: a test that nothing answered is fixed by checking the request URL the provider was given, and a request that arrived and did not verify is fixed by re-entering the app's secrets — neither of which is a lifecycle transition on the connection.\n\n`check_request_url` is also the honest answer when nobody simply pressed the button: the window closed with nothing arriving, and that has exactly two causes."
|
|
8014
|
+
},
|
|
8015
|
+
"ConnectionTestStatus": {
|
|
8016
|
+
"type": "string",
|
|
8017
|
+
"enum": ["pending", "succeeded", "failed", "expired"],
|
|
8018
|
+
"description": "`pending` means the test is placed and its answer has not arrived; a signed-interaction test stays there until somebody presses the button it sent. `expired` is the honest reading of a pending test whose window closed: nothing failed, nobody answered."
|
|
8019
|
+
},
|
|
8020
|
+
"ConnectionTestFailureCode": {
|
|
8021
|
+
"type": "string",
|
|
8022
|
+
"enum": [
|
|
8023
|
+
"setup_incomplete",
|
|
8024
|
+
"verification_target_unknown",
|
|
8025
|
+
"credential_unavailable",
|
|
8026
|
+
"provider_rejected",
|
|
8027
|
+
"provider_unavailable",
|
|
8028
|
+
"provider_rate_limited",
|
|
8029
|
+
"unverified_request_received"
|
|
8030
|
+
],
|
|
8031
|
+
"description": "Why a test did not pass, in a vocabulary the owner can act on rather than the provider's wire error.\n\n`verification_target_unknown` means nobody has installed the app from this deployment yet, so there is no owner to send a test to. `unverified_request_received` is a statement of fact and not a diagnosis: a request carrying this test's own challenge reached the interactivity URL and its signature did not verify against the secret this connection holds.\n\nA window that closed with nothing arriving is not in this list, because it is not a failure: it reads as `expired`, which says nobody answered rather than that anything broke."
|
|
8032
|
+
},
|
|
8033
|
+
"ConnectionTestEvidence": {
|
|
8034
|
+
"type": "object",
|
|
8035
|
+
"description": "What a finished test actually observed. Every field is safe to render, log, and paste into an issue: no credential, no provider-native app, workspace, channel, or user identifier, and no message content.",
|
|
8036
|
+
"properties": {
|
|
8037
|
+
"identity_handle": {
|
|
8038
|
+
"type": "string",
|
|
8039
|
+
"minLength": 1,
|
|
8040
|
+
"maxLength": 128,
|
|
8041
|
+
"description": "The visible handle the test went out as — the same identity a subscriber would see."
|
|
8042
|
+
},
|
|
8043
|
+
"workspace_display_name": {
|
|
8044
|
+
"type": "string",
|
|
8045
|
+
"minLength": 1,
|
|
8046
|
+
"maxLength": 100,
|
|
8047
|
+
"description": "The provider workspace the test ran in, as its own members see it named."
|
|
8048
|
+
},
|
|
8049
|
+
"accepted_at": {
|
|
8050
|
+
"$ref": "#/components/schemas/Timestamp",
|
|
8051
|
+
"description": "When the provider accepted the outbound message. Acceptance is the provider's own answer, not our belief."
|
|
8052
|
+
},
|
|
8053
|
+
"observed_at": {
|
|
8054
|
+
"$ref": "#/components/schemas/Timestamp",
|
|
8055
|
+
"description": "When the signed request the test was waiting for arrived and verified."
|
|
8056
|
+
},
|
|
8057
|
+
"round_trip_ms": {
|
|
8058
|
+
"type": "integer",
|
|
8059
|
+
"minimum": 0,
|
|
8060
|
+
"maximum": 86400000,
|
|
8061
|
+
"description": "Milliseconds from placing the test to observing its answer."
|
|
8062
|
+
},
|
|
8063
|
+
"signature_verified": {
|
|
8064
|
+
"type": "boolean",
|
|
8065
|
+
"description": "Whether the provider's signature over the untouched request body verified against the secret this connection custodies. Present only once a request has arrived."
|
|
8066
|
+
},
|
|
8067
|
+
"ingress_host": {
|
|
8068
|
+
"type": "string",
|
|
8069
|
+
"minLength": 1,
|
|
8070
|
+
"maxLength": 253,
|
|
8071
|
+
"description": "The public host the provider actually called, which is what a misconfigured or stale request URL gets wrong. It is this deployment's own hostname and names no tenant."
|
|
8072
|
+
}
|
|
8073
|
+
},
|
|
8074
|
+
"additionalProperties": false
|
|
8075
|
+
},
|
|
8076
|
+
"ConnectionTest": {
|
|
8077
|
+
"type": "object",
|
|
8078
|
+
"description": "One owner-triggered proof that an exact connection still works, run on demand and answered with evidence rather than with a claim.\n\nIt is deliberately incapable of carrying a secret: nothing here is a credential, a provider-native identifier, or message content, and a test that failed says why in a closed vocabulary rather than by quoting a provider.",
|
|
8079
|
+
"required": [
|
|
8080
|
+
"id",
|
|
8081
|
+
"connection_id",
|
|
8082
|
+
"kind",
|
|
8083
|
+
"status",
|
|
8084
|
+
"requested_at",
|
|
8085
|
+
"expires_at",
|
|
8086
|
+
"recovery_action",
|
|
8087
|
+
"diagnostic_id"
|
|
8088
|
+
],
|
|
8089
|
+
"properties": {
|
|
8090
|
+
"id": { "$ref": "#/components/schemas/OpaqueIdentifier" },
|
|
8091
|
+
"connection_id": { "$ref": "#/components/schemas/OpaqueIdentifier" },
|
|
8092
|
+
"kind": { "$ref": "#/components/schemas/ConnectionTestKind" },
|
|
8093
|
+
"status": { "$ref": "#/components/schemas/ConnectionTestStatus" },
|
|
8094
|
+
"requested_at": { "$ref": "#/components/schemas/Timestamp" },
|
|
8095
|
+
"completed_at": {
|
|
8096
|
+
"$ref": "#/components/schemas/Timestamp",
|
|
8097
|
+
"description": "When the test settled. Absent while it is still pending."
|
|
8098
|
+
},
|
|
8099
|
+
"expires_at": {
|
|
8100
|
+
"$ref": "#/components/schemas/Timestamp",
|
|
8101
|
+
"description": "When a pending test stops waiting. A test that reaches this without an answer becomes `expired`, which is not a failure of the connection."
|
|
8102
|
+
},
|
|
8103
|
+
"evidence": { "$ref": "#/components/schemas/ConnectionTestEvidence" },
|
|
8104
|
+
"failure_code": {
|
|
8105
|
+
"$ref": "#/components/schemas/ConnectionTestFailureCode"
|
|
8106
|
+
},
|
|
8107
|
+
"recovery_action": {
|
|
8108
|
+
"$ref": "#/components/schemas/ConnectionTestRecoveryAction",
|
|
8109
|
+
"description": "The exact safe next action for this result. `none` on a pass."
|
|
8110
|
+
},
|
|
8111
|
+
"diagnostic_id": { "$ref": "#/components/schemas/DiagnosticId" }
|
|
8112
|
+
},
|
|
8113
|
+
"additionalProperties": false
|
|
8114
|
+
},
|
|
8115
|
+
"ConnectionTestCollection": {
|
|
8116
|
+
"type": "object",
|
|
8117
|
+
"description": "This connection's recent tests, newest first. It is the audit-safe failure history for a connection: what was tried, when, and what came back.",
|
|
8118
|
+
"required": ["items"],
|
|
8119
|
+
"properties": {
|
|
8120
|
+
"items": {
|
|
8121
|
+
"type": "array",
|
|
8122
|
+
"maxItems": 100,
|
|
8123
|
+
"items": { "$ref": "#/components/schemas/ConnectionTest" }
|
|
8124
|
+
},
|
|
8125
|
+
"next_cursor": {
|
|
8126
|
+
"type": "string",
|
|
8127
|
+
"minLength": 1,
|
|
8128
|
+
"maxLength": 256,
|
|
8129
|
+
"pattern": "^[A-Za-z0-9_-]+$"
|
|
8130
|
+
},
|
|
8131
|
+
"page": { "$ref": "#/components/schemas/ListPageNumber" },
|
|
8132
|
+
"page_size": { "$ref": "#/components/schemas/ListPageSize" },
|
|
8133
|
+
"total": { "$ref": "#/components/schemas/ListTotal" }
|
|
8134
|
+
},
|
|
8135
|
+
"additionalProperties": false
|
|
8136
|
+
},
|
|
8137
|
+
"CreateConnectionTestRequest": {
|
|
8138
|
+
"type": "object",
|
|
8139
|
+
"description": "Places one test against an exact connection. It carries nothing but which test to run: a test needs no target, because the only person it may address is the one who installed the app.",
|
|
8140
|
+
"required": ["kind"],
|
|
8141
|
+
"properties": {
|
|
8142
|
+
"kind": { "$ref": "#/components/schemas/ConnectionTestKind" }
|
|
8143
|
+
},
|
|
8144
|
+
"additionalProperties": false
|
|
8145
|
+
},
|
|
8146
|
+
"CustomerOwnedSlackAppManifest": {
|
|
8147
|
+
"type": "object",
|
|
8148
|
+
"description": "The exact configuration a customer-owned Slack app is created from, as data rather than as a URL-encoded blob.\n\nIt exists so the owner can read what they are about to approve, and so a reviewer can check it, without opening Slack. It carries configuration only: no account, project, or environment, no credential, and no provider-native identifier. The opaque handle inside the request URLs selects candidate setup state and authorizes nothing — a request still has to carry Slack's signature over the untouched body before anything inside it is believed.",
|
|
8149
|
+
"required": [
|
|
8150
|
+
"creation_url",
|
|
8151
|
+
"app_name",
|
|
8152
|
+
"bot_display_name",
|
|
8153
|
+
"description",
|
|
8154
|
+
"bot_scopes",
|
|
8155
|
+
"user_scopes",
|
|
8156
|
+
"request_urls",
|
|
8157
|
+
"manifest",
|
|
8158
|
+
"diagnostic_id"
|
|
8159
|
+
],
|
|
8160
|
+
"properties": {
|
|
8161
|
+
"creation_url": {
|
|
8162
|
+
"type": "string",
|
|
8163
|
+
"format": "uri",
|
|
8164
|
+
"pattern": "^https://api\\.slack\\.com/apps\\?[^\\s]+$",
|
|
8165
|
+
"maxLength": 8192,
|
|
8166
|
+
"description": "The same prefilled creation link the setup carries, repeated here so one read answers both questions."
|
|
8167
|
+
},
|
|
8168
|
+
"app_name": { "type": "string", "minLength": 1, "maxLength": 35 },
|
|
8169
|
+
"bot_display_name": {
|
|
8170
|
+
"type": "string",
|
|
8171
|
+
"minLength": 1,
|
|
8172
|
+
"maxLength": 80
|
|
8173
|
+
},
|
|
8174
|
+
"description": { "type": "string", "minLength": 1, "maxLength": 140 },
|
|
8175
|
+
"bot_scopes": {
|
|
8176
|
+
"type": "array",
|
|
8177
|
+
"maxItems": 20,
|
|
8178
|
+
"uniqueItems": true,
|
|
8179
|
+
"items": { "type": "string", "minLength": 1, "maxLength": 64 },
|
|
8180
|
+
"description": "Every provider scope the bot will ask for, exactly as Slack will show them. Raw provider vocabulary on purpose: this is the screen the owner is about to approve, and translating it would make it unverifiable."
|
|
8181
|
+
},
|
|
8182
|
+
"user_scopes": {
|
|
8183
|
+
"type": "array",
|
|
8184
|
+
"maxItems": 20,
|
|
8185
|
+
"uniqueItems": true,
|
|
8186
|
+
"items": { "type": "string", "minLength": 1, "maxLength": 64 },
|
|
8187
|
+
"description": "Every provider scope a recipient will be asked for when they sign in. Separate from the bot scopes because they are approved by different people at different moments."
|
|
8188
|
+
},
|
|
8189
|
+
"request_urls": {
|
|
8190
|
+
"type": "object",
|
|
8191
|
+
"description": "Where this deployment answers the app. A stale or edited value here is the failure a signed-interaction test detects.",
|
|
8192
|
+
"required": [
|
|
8193
|
+
"events",
|
|
8194
|
+
"interactions",
|
|
8195
|
+
"installation_redirect",
|
|
8196
|
+
"recipient_redirect"
|
|
8197
|
+
],
|
|
8198
|
+
"properties": {
|
|
8199
|
+
"events": {
|
|
8200
|
+
"type": "string",
|
|
8201
|
+
"format": "uri",
|
|
8202
|
+
"pattern": "^https://[^\\s]+$",
|
|
8203
|
+
"maxLength": 2048
|
|
8204
|
+
},
|
|
8205
|
+
"interactions": {
|
|
8206
|
+
"type": "string",
|
|
8207
|
+
"format": "uri",
|
|
8208
|
+
"pattern": "^https://[^\\s]+$",
|
|
8209
|
+
"maxLength": 2048
|
|
8210
|
+
},
|
|
8211
|
+
"installation_redirect": {
|
|
8212
|
+
"type": "string",
|
|
8213
|
+
"format": "uri",
|
|
8214
|
+
"pattern": "^https://[^\\s]+$",
|
|
8215
|
+
"maxLength": 2048
|
|
8216
|
+
},
|
|
8217
|
+
"recipient_redirect": {
|
|
8218
|
+
"type": "string",
|
|
8219
|
+
"format": "uri",
|
|
8220
|
+
"pattern": "^https://[^\\s]+$",
|
|
8221
|
+
"maxLength": 2048
|
|
8222
|
+
}
|
|
8223
|
+
},
|
|
8224
|
+
"additionalProperties": false
|
|
8225
|
+
},
|
|
8226
|
+
"manifest": {
|
|
8227
|
+
"type": "object",
|
|
8228
|
+
"description": "The literal manifest document Slack is handed. Reproduced whole so what the owner reviews and what Slack receives cannot drift apart.",
|
|
8229
|
+
"additionalProperties": true
|
|
8230
|
+
},
|
|
8231
|
+
"diagnostic_id": { "$ref": "#/components/schemas/DiagnosticId" }
|
|
8232
|
+
},
|
|
8233
|
+
"additionalProperties": false
|
|
8234
|
+
},
|
|
7092
8235
|
"NotifierConnectionRoute": {
|
|
7093
8236
|
"type": "object",
|
|
7094
8237
|
"description": "One notifier's use of one connection in the same environment. This is the record that decides which identity a send actually leaves through: a connection nothing routes to is configured and inert, and a subscriber cannot authorize against it either. Routing is deliberately separate from creating the connection so that adding a second identity never moves traffic on its own.",
|
|
@@ -7455,13 +8598,133 @@
|
|
|
7455
8598
|
},
|
|
7456
8599
|
"additionalProperties": false
|
|
7457
8600
|
},
|
|
7458
|
-
"supported_languages": {
|
|
8601
|
+
"supported_languages": {
|
|
8602
|
+
"type": "array",
|
|
8603
|
+
"description": "Every canonical recipient language independently addressable by the update endpoint.",
|
|
8604
|
+
"minItems": 16,
|
|
8605
|
+
"maxItems": 16,
|
|
8606
|
+
"uniqueItems": true,
|
|
8607
|
+
"items": { "$ref": "#/components/schemas/RecipientLanguage" }
|
|
8608
|
+
},
|
|
8609
|
+
"next_cursor": {
|
|
8610
|
+
"type": "string",
|
|
8611
|
+
"minLength": 1,
|
|
8612
|
+
"maxLength": 256,
|
|
8613
|
+
"pattern": "^[A-Za-z0-9_-]+$"
|
|
8614
|
+
},
|
|
8615
|
+
"page": { "$ref": "#/components/schemas/ListPageNumber" },
|
|
8616
|
+
"page_size": { "$ref": "#/components/schemas/ListPageSize" },
|
|
8617
|
+
"total": { "$ref": "#/components/schemas/ListTotal" }
|
|
8618
|
+
},
|
|
8619
|
+
"additionalProperties": false
|
|
8620
|
+
},
|
|
8621
|
+
"UpdateRecipientCopyOverrideRequest": {
|
|
8622
|
+
"type": "object",
|
|
8623
|
+
"description": "Sets one exact project + language + string value. Delete the same address to restore the shipped default.",
|
|
8624
|
+
"required": ["value"],
|
|
8625
|
+
"properties": {
|
|
8626
|
+
"value": { "$ref": "#/components/schemas/RecipientCopyOverrideValue" }
|
|
8627
|
+
},
|
|
8628
|
+
"additionalProperties": false,
|
|
8629
|
+
"x-max-json-bytes": 2048
|
|
8630
|
+
},
|
|
8631
|
+
"CreateApiCredentialRequest": {
|
|
8632
|
+
"type": "object",
|
|
8633
|
+
"description": "Creates one credential inside the project environment selected by the authenticated administration path.",
|
|
8634
|
+
"required": ["label", "scopes"],
|
|
8635
|
+
"properties": {
|
|
8636
|
+
"label": { "type": "string", "minLength": 1, "maxLength": 100 },
|
|
8637
|
+
"scopes": { "$ref": "#/components/schemas/ApiCredentialScopeSet" }
|
|
8638
|
+
},
|
|
8639
|
+
"additionalProperties": false
|
|
8640
|
+
},
|
|
8641
|
+
"CreateAccountCredentialRequest": {
|
|
8642
|
+
"type": "object",
|
|
8643
|
+
"description": "Creates one credential against the whole account rather than one project environment.",
|
|
8644
|
+
"required": ["label", "scopes"],
|
|
8645
|
+
"properties": {
|
|
8646
|
+
"label": { "type": "string", "minLength": 1, "maxLength": 100 },
|
|
8647
|
+
"scopes": { "$ref": "#/components/schemas/AccountCredentialScopeSet" }
|
|
8648
|
+
},
|
|
8649
|
+
"additionalProperties": false
|
|
8650
|
+
},
|
|
8651
|
+
"RotateAccountCredentialRequest": {
|
|
8652
|
+
"type": "object",
|
|
8653
|
+
"description": "Creates a replacement with the same label and scopes and schedules terminal revocation of the replaced credential after the declared overlap.",
|
|
8654
|
+
"required": ["overlap_seconds"],
|
|
8655
|
+
"properties": {
|
|
8656
|
+
"overlap_seconds": {
|
|
8657
|
+
"type": "integer",
|
|
8658
|
+
"minimum": 0,
|
|
8659
|
+
"maximum": 86400
|
|
8660
|
+
}
|
|
8661
|
+
},
|
|
8662
|
+
"additionalProperties": false
|
|
8663
|
+
},
|
|
8664
|
+
"AccountCredentialId": {
|
|
8665
|
+
"type": "string",
|
|
8666
|
+
"pattern": "^acred_[A-Za-z0-9_-]{21}[AQgw]$",
|
|
8667
|
+
"description": "acred_ followed by the canonical unpadded base64url encoding of exactly 16 random bytes."
|
|
8668
|
+
},
|
|
8669
|
+
"AccountCredential": {
|
|
8670
|
+
"type": "object",
|
|
8671
|
+
"description": "Safe account-scoped API credential metadata. It carries no project or environment, because it names neither: a request does, in its path. It never contains a bearer secret, digest, Clerk identifier, or digest-key reference.",
|
|
8672
|
+
"required": [
|
|
8673
|
+
"id",
|
|
8674
|
+
"prefix",
|
|
8675
|
+
"label",
|
|
8676
|
+
"scopes",
|
|
8677
|
+
"created_by",
|
|
8678
|
+
"created_at",
|
|
8679
|
+
"status"
|
|
8680
|
+
],
|
|
8681
|
+
"properties": {
|
|
8682
|
+
"id": { "$ref": "#/components/schemas/AccountCredentialId" },
|
|
8683
|
+
"prefix": { "$ref": "#/components/schemas/AccountCredentialPrefix" },
|
|
8684
|
+
"label": { "type": "string", "minLength": 1, "maxLength": 100 },
|
|
8685
|
+
"scopes": {
|
|
8686
|
+
"$ref": "#/components/schemas/AccountCredentialScopeSet"
|
|
8687
|
+
},
|
|
8688
|
+
"created_by": {
|
|
8689
|
+
"$ref": "#/components/schemas/OpaqueIdentifier",
|
|
8690
|
+
"description": "An opaque WhooshBang member identifier. It is never a Clerk user identifier."
|
|
8691
|
+
},
|
|
8692
|
+
"created_at": { "$ref": "#/components/schemas/Timestamp" },
|
|
8693
|
+
"last_used_at": { "$ref": "#/components/schemas/Timestamp" },
|
|
8694
|
+
"scheduled_revocation_at": {
|
|
8695
|
+
"$ref": "#/components/schemas/Timestamp",
|
|
8696
|
+
"description": "When an active credential in a rotation overlap becomes terminally revoked."
|
|
8697
|
+
},
|
|
8698
|
+
"status": { "type": "string", "enum": ["active", "revoked"] },
|
|
8699
|
+
"revoked_at": { "$ref": "#/components/schemas/Timestamp" }
|
|
8700
|
+
},
|
|
8701
|
+
"allOf": [
|
|
8702
|
+
{
|
|
8703
|
+
"if": {
|
|
8704
|
+
"properties": { "status": { "const": "revoked" } },
|
|
8705
|
+
"required": ["status"]
|
|
8706
|
+
},
|
|
8707
|
+
"then": {
|
|
8708
|
+
"required": ["revoked_at"],
|
|
8709
|
+
"properties": {
|
|
8710
|
+
"revoked_at": {},
|
|
8711
|
+
"scheduled_revocation_at": false
|
|
8712
|
+
}
|
|
8713
|
+
},
|
|
8714
|
+
"else": { "properties": { "revoked_at": false } }
|
|
8715
|
+
}
|
|
8716
|
+
],
|
|
8717
|
+
"additionalProperties": false
|
|
8718
|
+
},
|
|
8719
|
+
"AccountCredentialCollection": {
|
|
8720
|
+
"type": "object",
|
|
8721
|
+
"description": "A stable page of safe account-credential metadata for one account.",
|
|
8722
|
+
"required": ["items"],
|
|
8723
|
+
"properties": {
|
|
8724
|
+
"items": {
|
|
7459
8725
|
"type": "array",
|
|
7460
|
-
"
|
|
7461
|
-
"
|
|
7462
|
-
"maxItems": 16,
|
|
7463
|
-
"uniqueItems": true,
|
|
7464
|
-
"items": { "$ref": "#/components/schemas/RecipientLanguage" }
|
|
8726
|
+
"maxItems": 100,
|
|
8727
|
+
"items": { "$ref": "#/components/schemas/AccountCredential" }
|
|
7465
8728
|
},
|
|
7466
8729
|
"next_cursor": {
|
|
7467
8730
|
"type": "string",
|
|
@@ -7475,23 +8738,27 @@
|
|
|
7475
8738
|
},
|
|
7476
8739
|
"additionalProperties": false
|
|
7477
8740
|
},
|
|
7478
|
-
"
|
|
8741
|
+
"AccountCredentialSecretReveal": {
|
|
7479
8742
|
"type": "object",
|
|
7480
|
-
"description": "
|
|
7481
|
-
"required": ["
|
|
8743
|
+
"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.",
|
|
8744
|
+
"required": ["credential", "secret"],
|
|
7482
8745
|
"properties": {
|
|
7483
|
-
"
|
|
8746
|
+
"credential": { "$ref": "#/components/schemas/AccountCredential" },
|
|
8747
|
+
"secret": { "$ref": "#/components/schemas/AccountCredentialSecret" }
|
|
7484
8748
|
},
|
|
7485
|
-
"additionalProperties": false
|
|
7486
|
-
"x-max-json-bytes": 2048
|
|
8749
|
+
"additionalProperties": false
|
|
7487
8750
|
},
|
|
7488
|
-
"
|
|
8751
|
+
"AccountCredentialRotation": {
|
|
7489
8752
|
"type": "object",
|
|
7490
|
-
"description": "
|
|
7491
|
-
"required": ["
|
|
8753
|
+
"description": "A creation-only replacement secret plus safe metadata for the credential it replaces.",
|
|
8754
|
+
"required": ["credential", "replaced_credential"],
|
|
7492
8755
|
"properties": {
|
|
7493
|
-
"
|
|
7494
|
-
|
|
8756
|
+
"credential": {
|
|
8757
|
+
"$ref": "#/components/schemas/AccountCredentialSecretReveal"
|
|
8758
|
+
},
|
|
8759
|
+
"replaced_credential": {
|
|
8760
|
+
"$ref": "#/components/schemas/AccountCredential"
|
|
8761
|
+
}
|
|
7495
8762
|
},
|
|
7496
8763
|
"additionalProperties": false
|
|
7497
8764
|
},
|
|
@@ -8737,6 +10004,18 @@
|
|
|
8737
10004
|
"retryable": { "const": false }
|
|
8738
10005
|
}
|
|
8739
10006
|
},
|
|
10007
|
+
{
|
|
10008
|
+
"type": "object",
|
|
10009
|
+
"properties": {
|
|
10010
|
+
"type": {
|
|
10011
|
+
"const": "https://whooshbang.flowxo.com/problems/credential_account_mismatch"
|
|
10012
|
+
},
|
|
10013
|
+
"title": { "const": "Credential account mismatch" },
|
|
10014
|
+
"status": { "const": 403 },
|
|
10015
|
+
"code": { "const": "credential_account_mismatch" },
|
|
10016
|
+
"retryable": { "const": false }
|
|
10017
|
+
}
|
|
10018
|
+
},
|
|
8740
10019
|
{
|
|
8741
10020
|
"type": "object",
|
|
8742
10021
|
"properties": {
|
|
@@ -9957,6 +11236,7 @@
|
|
|
9957
11236
|
"resource_not_found",
|
|
9958
11237
|
"resource_expired",
|
|
9959
11238
|
"cancellation_too_late",
|
|
11239
|
+
"credential_account_mismatch",
|
|
9960
11240
|
"endpoint_rotation_conflict",
|
|
9961
11241
|
"rate_limited",
|
|
9962
11242
|
"provider_retryable",
|
|
@@ -10075,10 +11355,35 @@
|
|
|
10075
11355
|
}
|
|
10076
11356
|
]
|
|
10077
11357
|
},
|
|
10078
|
-
"
|
|
11358
|
+
"AccountCredentialPrefix": {
|
|
10079
11359
|
"type": "string",
|
|
10080
|
-
"
|
|
10081
|
-
"description": "
|
|
11360
|
+
"pattern": "^wb_ac1\\.acred_[A-Za-z0-9_-]{8}$",
|
|
11361
|
+
"description": "A safe display prefix containing the account-credential format marker and first eight base64url identity characters, never secret bytes."
|
|
11362
|
+
},
|
|
11363
|
+
"AccountCredentialScope": {
|
|
11364
|
+
"type": "string",
|
|
11365
|
+
"enum": [
|
|
11366
|
+
"account:read",
|
|
11367
|
+
"projects:write",
|
|
11368
|
+
"projects:read",
|
|
11369
|
+
"messages:write",
|
|
11370
|
+
"messages:read",
|
|
11371
|
+
"subscriptions:write",
|
|
11372
|
+
"subscriptions:read",
|
|
11373
|
+
"machine-clients:write",
|
|
11374
|
+
"machine-clients:read",
|
|
11375
|
+
"capabilities:read",
|
|
11376
|
+
"connections:write",
|
|
11377
|
+
"connections:read"
|
|
11378
|
+
],
|
|
11379
|
+
"description": "One account-credential capability. `account:read` is the account's project directory — listing the account's projects and reading one — and is the only scope here a project credential can never carry; it authorizes nothing below the account. Every other name is the project-credential vocabulary unchanged, and is exercised only on the environment-scoped routes, where the project and environment come from the path and the credential's authority is the intersection of these scopes with the project-credential vocabulary. An account credential is therefore never wider than a project credential would have been for the same project environment."
|
|
11380
|
+
},
|
|
11381
|
+
"AccountCredentialScopeSet": {
|
|
11382
|
+
"type": "array",
|
|
11383
|
+
"minItems": 1,
|
|
11384
|
+
"maxItems": 12,
|
|
11385
|
+
"uniqueItems": true,
|
|
11386
|
+
"items": { "$ref": "#/components/schemas/AccountCredentialScope" }
|
|
10082
11387
|
},
|
|
10083
11388
|
"ListPageSize": {
|
|
10084
11389
|
"type": "integer",
|
|
@@ -10091,6 +11396,16 @@
|
|
|
10091
11396
|
"minimum": 0,
|
|
10092
11397
|
"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."
|
|
10093
11398
|
},
|
|
11399
|
+
"AccountCredentialSecret": {
|
|
11400
|
+
"type": "string",
|
|
11401
|
+
"pattern": "^wb_ac1\\.acred_[A-Za-z0-9_-]{21}[AQgw]\\.[A-Za-z0-9_-]{42}[AEIMQUYcgkosw048]$",
|
|
11402
|
+
"description": "The exact one-time runtime bearer syntax. The final segment is canonical unpadded base64url for exactly 32 random secret bytes."
|
|
11403
|
+
},
|
|
11404
|
+
"Environment": {
|
|
11405
|
+
"type": "string",
|
|
11406
|
+
"enum": ["test", "live"],
|
|
11407
|
+
"description": "The single environment selected by the authenticated credential."
|
|
11408
|
+
},
|
|
10094
11409
|
"RecipientCopyOverrideValue": {
|
|
10095
11410
|
"type": "string",
|
|
10096
11411
|
"minLength": 1,
|
|
@@ -10830,6 +12145,7 @@
|
|
|
10830
12145
|
},
|
|
10831
12146
|
"setup": { "$ref": "#/components/schemas/ChannelConnectionSetup" },
|
|
10832
12147
|
"authorization_setup": false,
|
|
12148
|
+
"app_setup": false,
|
|
10833
12149
|
"installation": false,
|
|
10834
12150
|
"row_version": {
|
|
10835
12151
|
"type": "integer",
|
|
@@ -10991,6 +12307,7 @@
|
|
|
10991
12307
|
"$ref": "#/components/schemas/SlackConnectionIdentity"
|
|
10992
12308
|
},
|
|
10993
12309
|
"setup": false,
|
|
12310
|
+
"app_setup": false,
|
|
10994
12311
|
"authorization_setup": {
|
|
10995
12312
|
"$ref": "#/components/schemas/HostedChannelConnectionSetup",
|
|
10996
12313
|
"description": "The additive hosted authorization state. Older tolerant readers discard this unknown field rather than interpreting it as Telegram setup."
|
|
@@ -11034,6 +12351,177 @@
|
|
|
11034
12351
|
},
|
|
11035
12352
|
"additionalProperties": false
|
|
11036
12353
|
},
|
|
12354
|
+
"CustomerOwnedSlackSetupStatus": {
|
|
12355
|
+
"type": "string",
|
|
12356
|
+
"enum": [
|
|
12357
|
+
"awaiting_app_creation",
|
|
12358
|
+
"awaiting_authorities",
|
|
12359
|
+
"awaiting_authorization",
|
|
12360
|
+
"awaiting_ingress_proof",
|
|
12361
|
+
"provisioning",
|
|
12362
|
+
"failed",
|
|
12363
|
+
"expired"
|
|
12364
|
+
],
|
|
12365
|
+
"description": "How far a customer-owned Slack setup has reached, which is the only thing a caller can act on while the connection is still `pending`. Each verification state names a different authority, because Slack hands the app's secrets over together but exercises them at different moments: `awaiting_authorization` has the three values and is waiting for the install, `awaiting_ingress_proof` has a bot grant Slack confirmed and is waiting for the first request Slack itself signs. Collapsing them would let a connection report a verified ingress it has never verified."
|
|
12366
|
+
},
|
|
12367
|
+
"CustomerOwnedSlackAppAuthority": {
|
|
12368
|
+
"type": "string",
|
|
12369
|
+
"enum": ["client_identity", "client_secret", "signing_secret"],
|
|
12370
|
+
"description": "One app-specific authority the owner hands over. Product vocabulary for what each value is for, never the value: `client_identity` is safe metadata, and the other two are write-only."
|
|
12371
|
+
},
|
|
12372
|
+
"CustomerOwnedSlackConnectionSetup": {
|
|
12373
|
+
"type": "object",
|
|
12374
|
+
"description": "The unfinished half of a customer-owned Slack connection: what the owner still has to do in Slack, and by when.\n\nIt is deliberately incapable of carrying a secret. The creation link contains configuration and nothing else, the two secrets the owner supplies are write-only and have no field here or on the connection, and the bot grant never travels through a browser at all — WhooshBang obtains it from Slack's own installation exchange. Everything here is therefore safe to poll, render, and hand to whoever is doing the setup.",
|
|
12375
|
+
"required": [
|
|
12376
|
+
"status",
|
|
12377
|
+
"creation_url",
|
|
12378
|
+
"expires_at",
|
|
12379
|
+
"app_name",
|
|
12380
|
+
"bot_display_name",
|
|
12381
|
+
"outstanding_authorities"
|
|
12382
|
+
],
|
|
12383
|
+
"properties": {
|
|
12384
|
+
"status": {
|
|
12385
|
+
"$ref": "#/components/schemas/CustomerOwnedSlackSetupStatus"
|
|
12386
|
+
},
|
|
12387
|
+
"creation_url": {
|
|
12388
|
+
"type": "string",
|
|
12389
|
+
"format": "uri",
|
|
12390
|
+
"pattern": "^https://api\\.slack\\.com/apps\\?[^\\s]+$",
|
|
12391
|
+
"maxLength": 8192,
|
|
12392
|
+
"description": "Slack's own app-creation flow, opened with this app's manifest already filled in, so the owner reviews and presses Create rather than writing or pasting YAML. The URL carries configuration only: it names no account, project, or environment and contains no credential. The opaque handle it does carry selects candidate setup state and authorizes nothing."
|
|
12393
|
+
},
|
|
12394
|
+
"expires_at": {
|
|
12395
|
+
"$ref": "#/components/schemas/Timestamp",
|
|
12396
|
+
"description": "When an unfinished setup stops being usable. A setup that already produced a Slack app stays recoverable past this, because the app exists whether or not the browser came back."
|
|
12397
|
+
},
|
|
12398
|
+
"authorization_url": {
|
|
12399
|
+
"type": "string",
|
|
12400
|
+
"format": "uri",
|
|
12401
|
+
"pattern": "^https://[^\\s]+$",
|
|
12402
|
+
"maxLength": 2048,
|
|
12403
|
+
"description": "The short-lived handoff that installs the owner's app into their workspace, present once the three authorities have arrived and an installation has been started. It carries opaque state, never a provider credential or a raw authorization code, and the bot grant it produces is returned to WhooshBang by Slack rather than to the browser."
|
|
12404
|
+
},
|
|
12405
|
+
"app_name": {
|
|
12406
|
+
"type": "string",
|
|
12407
|
+
"minLength": 1,
|
|
12408
|
+
"maxLength": 35,
|
|
12409
|
+
"description": "The name the owner chose, echoed so a resumed session shows the same app it started with. Slack's own limit."
|
|
12410
|
+
},
|
|
12411
|
+
"bot_display_name": {
|
|
12412
|
+
"type": "string",
|
|
12413
|
+
"minLength": 1,
|
|
12414
|
+
"maxLength": 80,
|
|
12415
|
+
"description": "The bot display name the owner chose, echoed for the same reason. Slack's own limit."
|
|
12416
|
+
},
|
|
12417
|
+
"outstanding_authorities": {
|
|
12418
|
+
"type": "array",
|
|
12419
|
+
"maxItems": 3,
|
|
12420
|
+
"uniqueItems": true,
|
|
12421
|
+
"items": {
|
|
12422
|
+
"$ref": "#/components/schemas/CustomerOwnedSlackAppAuthority"
|
|
12423
|
+
},
|
|
12424
|
+
"description": "Which of the three app-specific values this setup is still waiting for. Empty once they have all arrived; it never says what any of them are."
|
|
12425
|
+
},
|
|
12426
|
+
"failure_reason": {
|
|
12427
|
+
"type": "string",
|
|
12428
|
+
"minLength": 1,
|
|
12429
|
+
"maxLength": 200,
|
|
12430
|
+
"description": "Why this attempt ended, in terms the owner can act on. It never echoes a submitted secret, whole or in part, and never names another customer: a Slack app already connected elsewhere says only that."
|
|
12431
|
+
}
|
|
12432
|
+
},
|
|
12433
|
+
"additionalProperties": false
|
|
12434
|
+
},
|
|
12435
|
+
"CustomerOwnedSlackChannelConnection": {
|
|
12436
|
+
"type": "object",
|
|
12437
|
+
"description": "A Slack app the customer created and owns, referenced by one exact environment connection.\n\nIt differs from the hosted arm in who owns the app and its lifecycle, not in what a notification can do. WhooshBang custodies the three app-specific authorities Slack makes unavoidable and none of them appears here or in any other read: absence, not redaction, is the mechanism.",
|
|
12438
|
+
"required": [
|
|
12439
|
+
"id",
|
|
12440
|
+
"project_id",
|
|
12441
|
+
"environment_id",
|
|
12442
|
+
"environment",
|
|
12443
|
+
"provider",
|
|
12444
|
+
"mode",
|
|
12445
|
+
"display_name",
|
|
12446
|
+
"status",
|
|
12447
|
+
"health",
|
|
12448
|
+
"capabilities",
|
|
12449
|
+
"recovery_action",
|
|
12450
|
+
"row_version",
|
|
12451
|
+
"created_at",
|
|
12452
|
+
"updated_at",
|
|
12453
|
+
"diagnostic_id"
|
|
12454
|
+
],
|
|
12455
|
+
"properties": {
|
|
12456
|
+
"id": { "$ref": "#/components/schemas/OpaqueIdentifier" },
|
|
12457
|
+
"project_id": { "$ref": "#/components/schemas/OpaqueIdentifier" },
|
|
12458
|
+
"environment_id": { "$ref": "#/components/schemas/OpaqueIdentifier" },
|
|
12459
|
+
"environment": { "$ref": "#/components/schemas/Environment" },
|
|
12460
|
+
"provider": { "const": "slack" },
|
|
12461
|
+
"mode": { "const": "customer_managed" },
|
|
12462
|
+
"display_name": {
|
|
12463
|
+
"type": "string",
|
|
12464
|
+
"minLength": 1,
|
|
12465
|
+
"maxLength": 100
|
|
12466
|
+
},
|
|
12467
|
+
"status": { "$ref": "#/components/schemas/ChannelConnectionStatus" },
|
|
12468
|
+
"health": { "$ref": "#/components/schemas/ProviderHealthState" },
|
|
12469
|
+
"capabilities": {
|
|
12470
|
+
"type": "array",
|
|
12471
|
+
"maxItems": 1,
|
|
12472
|
+
"uniqueItems": true,
|
|
12473
|
+
"items": {
|
|
12474
|
+
"$ref": "#/components/schemas/HostedConnectionCapability"
|
|
12475
|
+
}
|
|
12476
|
+
},
|
|
12477
|
+
"identity": {
|
|
12478
|
+
"$ref": "#/components/schemas/SlackConnectionIdentity"
|
|
12479
|
+
},
|
|
12480
|
+
"setup": false,
|
|
12481
|
+
"authorization_setup": false,
|
|
12482
|
+
"app_setup": {
|
|
12483
|
+
"$ref": "#/components/schemas/CustomerOwnedSlackConnectionSetup",
|
|
12484
|
+
"description": "The guided customer-owned setup state. A distinct field from Telegram's `setup` and the hosted arm's `authorization_setup` so a tolerant reader can never mistake one journey's progress for another's."
|
|
12485
|
+
},
|
|
12486
|
+
"installation": {
|
|
12487
|
+
"$ref": "#/components/schemas/ProviderInstallationSummary",
|
|
12488
|
+
"description": "The safe installation this exact connection owns. It exposes no provider-native identifier and no credential reference."
|
|
12489
|
+
},
|
|
12490
|
+
"management_url": {
|
|
12491
|
+
"type": "string",
|
|
12492
|
+
"format": "uri",
|
|
12493
|
+
"pattern": "^slack://app\\?[^\\s]+$",
|
|
12494
|
+
"maxLength": 2048,
|
|
12495
|
+
"description": "A provider-owned App Home deep link for the customer's own workspace and their own app. It is not a caller-supplied return URL."
|
|
12496
|
+
},
|
|
12497
|
+
"recovery_action": {
|
|
12498
|
+
"$ref": "#/components/schemas/HostedConnectionRecoveryAction"
|
|
12499
|
+
},
|
|
12500
|
+
"last_successful_verification_at": {
|
|
12501
|
+
"$ref": "#/components/schemas/Timestamp"
|
|
12502
|
+
},
|
|
12503
|
+
"last_successful_delivery_at": {
|
|
12504
|
+
"$ref": "#/components/schemas/Timestamp"
|
|
12505
|
+
},
|
|
12506
|
+
"row_version": {
|
|
12507
|
+
"type": "integer",
|
|
12508
|
+
"minimum": 1,
|
|
12509
|
+
"description": "Send this back on an update. A stale value is refused rather than silently overwriting a change made in between."
|
|
12510
|
+
},
|
|
12511
|
+
"created_at": { "$ref": "#/components/schemas/Timestamp" },
|
|
12512
|
+
"updated_at": { "$ref": "#/components/schemas/Timestamp" },
|
|
12513
|
+
"archived_at": { "$ref": "#/components/schemas/Timestamp" },
|
|
12514
|
+
"health_checked_at": { "$ref": "#/components/schemas/Timestamp" },
|
|
12515
|
+
"last_transition_at": { "$ref": "#/components/schemas/Timestamp" },
|
|
12516
|
+
"last_transition_reason": {
|
|
12517
|
+
"type": "string",
|
|
12518
|
+
"minLength": 1,
|
|
12519
|
+
"maxLength": 200
|
|
12520
|
+
},
|
|
12521
|
+
"diagnostic_id": { "$ref": "#/components/schemas/DiagnosticId" }
|
|
12522
|
+
},
|
|
12523
|
+
"additionalProperties": false
|
|
12524
|
+
},
|
|
11037
12525
|
"RecipientBrand": {
|
|
11038
12526
|
"type": "object",
|
|
11039
12527
|
"description": "Constrained recipient-facing brand tokens. Arbitrary markup, CSS, scripts, remote assets, fonts, and tracking inputs are structurally absent.",
|
|
@@ -11295,7 +12783,7 @@
|
|
|
11295
12783
|
"provider-capabilities-v1.schema": {
|
|
11296
12784
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
11297
12785
|
"$id": "https://whooshbang.flowxo.com/schemas/provider-capabilities-v1.schema.json",
|
|
11298
|
-
"x-contract-version": "1.0.0-rc.
|
|
12786
|
+
"x-contract-version": "1.0.0-rc.21",
|
|
11299
12787
|
"title": "ProviderCapabilitiesV1",
|
|
11300
12788
|
"description": "Provider-neutral, renderer-facing delivery and interaction capabilities with explicit degradation.",
|
|
11301
12789
|
"type": "object",
|
|
@@ -11947,5 +13435,5 @@
|
|
|
11947
13435
|
}
|
|
11948
13436
|
}
|
|
11949
13437
|
},
|
|
11950
|
-
"x-contract-version": "1.0.0-rc.
|
|
13438
|
+
"x-contract-version": "1.0.0-rc.21"
|
|
11951
13439
|
}
|