@whooshbang/contracts 2.2.0 → 2.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (96) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/contract-manifest.json +32 -32
  3. package/dist/generated/artifact-index.d.ts +33 -33
  4. package/dist/generated/artifact-index.js +32 -32
  5. package/dist/generated/interaction-v1.d.ts +72 -4
  6. package/dist/generated/validators-standalone.d.ts +492 -480
  7. package/dist/generated/validators-standalone.js +4191 -2276
  8. package/dist/generated/validators-standalone.js.map +1 -1
  9. package/dist/generated/validators.d.ts +2 -1
  10. package/dist/generated/validators.js +2 -1
  11. package/dist/generated/validators.js.map +1 -1
  12. package/dist/generated/whooshbang-v1.d.ts +94 -17
  13. package/dist/index.d.ts +3 -2
  14. package/dist/index.js +2 -1
  15. package/dist/index.js.map +1 -1
  16. package/dist/interaction-expiry.d.ts +17 -0
  17. package/dist/interaction-expiry.js +67 -0
  18. package/dist/interaction-expiry.js.map +1 -0
  19. package/fixtures/core-v1/fixture-index.json +1 -1
  20. package/fixtures/core-v1/scenarios/api-credential-invalid-indistinguishable.json +1 -1
  21. package/fixtures/core-v1/scenarios/api-credential-one-time-reveal.json +1 -1
  22. package/fixtures/core-v1/scenarios/api-credential-revocation-replay.json +1 -1
  23. package/fixtures/core-v1/scenarios/api-credential-rotation-overlap.json +1 -1
  24. package/fixtures/core-v1/scenarios/cancellation-too-late.json +1 -1
  25. package/fixtures/core-v1/scenarios/cross-organization-project.json +1 -1
  26. package/fixtures/core-v1/scenarios/cross-project-resource.json +1 -1
  27. package/fixtures/core-v1/scenarios/environment-mismatch.json +1 -1
  28. package/fixtures/core-v1/scenarios/idempotency-conflict.json +1 -1
  29. package/fixtures/core-v1/scenarios/idempotency-equivalent-replay.json +1 -1
  30. package/fixtures/core-v1/scenarios/malformed-bearer.json +1 -1
  31. package/fixtures/core-v1/scenarios/metadata-overrun.json +1 -1
  32. package/fixtures/core-v1/scenarios/missing-scope.json +1 -1
  33. package/fixtures/core-v1/scenarios/project-create-equivalent-replay.json +1 -1
  34. package/fixtures/core-v1/scenarios/project-slug-conflict.json +1 -1
  35. package/fixtures/core-v1/scenarios/provider-outcome-unknown.json +1 -1
  36. package/fixtures/core-v1/scenarios/subscription-activation-replay.json +1 -1
  37. package/fixtures/core-v1/scenarios/subscription-authorization-capability-opacity.json +1 -1
  38. package/fixtures/core-v1/scenarios/subscription-cross-connection-callback-rejected.json +1 -1
  39. package/fixtures/core-v1/scenarios/subscription-expired-replay.json +1 -1
  40. package/fixtures/core-v1/scenarios/subscription-selected-connection-replay.json +1 -1
  41. package/fixtures/interaction-machine-v1/scenarios/ack-conflict.json +1 -1
  42. package/fixtures/interaction-machine-v1/scenarios/ack-cross-machine.json +1 -1
  43. package/fixtures/interaction-machine-v1/scenarios/ack-out-of-order.json +1 -1
  44. package/fixtures/interaction-machine-v1/scenarios/ack-replay.json +1 -1
  45. package/fixtures/interaction-machine-v1/scenarios/ambiguous-free-text.json +1 -1
  46. package/fixtures/interaction-machine-v1/scenarios/callback-token-opacity.json +1 -1
  47. package/fixtures/interaction-machine-v1/scenarios/credential-registration-conflict.json +1 -1
  48. package/fixtures/interaction-machine-v1/scenarios/credential-registration-replay.json +1 -1
  49. package/fixtures/interaction-machine-v1/scenarios/credential-revocation-replay.json +1 -1
  50. package/fixtures/interaction-machine-v1/scenarios/credential-rotation.json +1 -1
  51. package/fixtures/interaction-machine-v1/scenarios/first-credential-activates.json +1 -1
  52. package/fixtures/interaction-machine-v1/scenarios/interaction-duplicate.json +1 -1
  53. package/fixtures/interaction-machine-v1/scenarios/interaction-expired.json +1 -1
  54. package/fixtures/interaction-machine-v1/scenarios/interaction-mismatch.json +1 -1
  55. package/fixtures/interaction-machine-v1/scenarios/interaction-retired.json +1 -1
  56. package/fixtures/interaction-machine-v1/scenarios/interaction-stale.json +1 -1
  57. package/fixtures/interaction-machine-v1/scenarios/machine-admin-denied.json +1 -1
  58. package/fixtures/interaction-machine-v1/scenarios/machine-bound-message-create.json +1 -1
  59. package/fixtures/interaction-machine-v1/scenarios/machine-cancel-denied.json +1 -1
  60. package/fixtures/interaction-machine-v1/scenarios/machine-crash-before-ack.json +1 -1
  61. package/fixtures/interaction-machine-v1/scenarios/machine-notifier-mismatch.json +1 -1
  62. package/fixtures/interaction-machine-v1/scenarios/machine-other-message-read.json +1 -1
  63. package/fixtures/interaction-machine-v1/scenarios/machine-owned-message-read.json +1 -1
  64. package/fixtures/interaction-machine-v1/scenarios/machine-subscriber-mismatch.json +1 -1
  65. package/fixtures/interaction-machine-v1/scenarios/poll-uncommitted-cursor-rejected.json +1 -1
  66. package/fixtures/interaction-machine-v1/scenarios/project-credential-machine-endpoint-denied.json +1 -1
  67. package/fixtures/interaction-machine-v1/scenarios/quarantine-integrity-rejected.json +1 -1
  68. package/fixtures/interaction-machine-v1/scenarios/quarantine-signature-rejected.json +1 -1
  69. package/fixtures/interaction-machine-v1/scenarios/quarantine-valid.json +1 -1
  70. package/fixtures/interaction-machine-v1/scenarios/revoked-machine-credential.json +1 -1
  71. package/fixtures/recipient-experience-v1/scenarios/membership-broadcast-snapshot.json +1 -1
  72. package/fixtures/recipient-experience-v1/scenarios/preferred-binding-resolution.json +1 -1
  73. package/fixtures/recipient-experience-v1/scenarios/recipient-session-security.json +1 -1
  74. package/fixtures/recipient-experience-v1/scenarios/subscriber-initial-groups-transaction.json +1 -1
  75. package/openapi/generated/whooshbang-v1.bundle.json +165 -28
  76. package/openapi/whooshbang-v1.yaml +33 -2
  77. package/package.json +1 -1
  78. package/schemas/core-scenario.schema.json +1 -1
  79. package/schemas/events/agent-binding-degraded-v1.schema.json +1 -1
  80. package/schemas/events/interaction-expired-v1.schema.json +1 -1
  81. package/schemas/events/interaction-received-v1.schema.json +1 -1
  82. package/schemas/events/message-delivered-v1.schema.json +1 -1
  83. package/schemas/events/message-expired-v1.schema.json +1 -1
  84. package/schemas/events/message-failed-v1.schema.json +1 -1
  85. package/schemas/events/message-provider-accepted-v1.schema.json +1 -1
  86. package/schemas/events/subscription-activated-v1.schema.json +1 -1
  87. package/schemas/events/subscription-revoked-v1.schema.json +1 -1
  88. package/schemas/events/whooshbang-event-v1.schema.json +1 -1
  89. package/schemas/fixture-index.schema.json +1 -1
  90. package/schemas/interaction-machine-scenario.schema.json +1 -1
  91. package/schemas/interaction-v1.schema.json +151 -19
  92. package/schemas/machine-v1.schema.json +1 -1
  93. package/schemas/provider-capabilities-v1.schema.json +1 -1
  94. package/schemas/webhooks/webhook-delivery-case-v1.schema.json +1 -1
  95. package/schemas/webhooks/webhook-signing-vector-v1.schema.json +1 -1
  96. package/schemas/whooshbang-v1.schema.json +56 -50
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://json-schema.org/draft/2020-12/schema",
3
3
  "$id": "https://whooshbang.flowxo.com/schemas/whooshbang-v1.schema.json",
4
- "x-contract-version": "2.2.0",
4
+ "x-contract-version": "2.4.0",
5
5
  "title": "WhooshBangV1ContractDocument",
6
6
  "description": "Public request, resource, summary, and Problem Details models for the WhooshBang V1 REST API.",
7
7
  "anyOf": [
@@ -445,7 +445,7 @@
445
445
  "type": "integer",
446
446
  "minimum": 1,
447
447
  "maximum": 50000,
448
- "description": "A 1-based page number. Present on a response only when the request addressed the list by page rather than by cursor: a cursor walk cannot know which page it is on. What is bounded is the row a page starts at, not the page number: a request whose first row would be past 50,000 — the deepest row any published list ceiling reaches — is refused as `request_invalid`. Bounding the page number instead would make the last rows of a large list unreachable at a small page size, which is exactly when a person is paging through one."
448
+ "description": "A 1-based page number. Present on a response only when the request addressed the list by page rather than by cursor: a cursor walk cannot know which page it is on. What is bounded is the row a page starts at, not the page number: a request whose first row would be past 50,000 \u2014 the deepest row any published list ceiling reaches \u2014 is refused as `request_invalid`. Bounding the page number instead would make the last rows of a large list unreachable at a small page size, which is exactly when a person is paging through one."
449
449
  },
450
450
  "ListPageSize": {
451
451
  "type": "integer",
@@ -457,7 +457,7 @@
457
457
  "type": "string",
458
458
  "minLength": 1,
459
459
  "maxLength": 128,
460
- "pattern": "^[^\u0000-\u001f-Ÿ

]+$",
460
+ "pattern": "^[^\u0000-\u001f-Ÿ

\u007f-\u009f\u2028\u2029]+$",
461
461
  "description": "An opaque identifier. Its shape, ordering, and embedded content are not public promises."
462
462
  },
463
463
  "DiagnosticId": {
@@ -1907,7 +1907,7 @@
1907
1907
  "connections:write",
1908
1908
  "connections:read"
1909
1909
  ],
1910
- "description": "One project-credential capability. Environment authority always comes from the credential record, never a scope or request field. `projects:write` configures the credential's own project and environment; it never creates a project, which remains an organization authority. The project-wide singletons — brand, default locale, logo and recipient copy — are what a live recipient sees, so writing them requires a live credential; a test credential may read them and may write its own environment's policy."
1910
+ "description": "One project-credential capability. Environment authority always comes from the credential record, never a scope or request field. `projects:write` configures the credential's own project and environment; it never creates a project, which remains an organization authority. The project-wide singletons \u2014 brand, default locale, logo and recipient copy \u2014 are what a live recipient sees, so writing them requires a live credential; a test credential may read them and may write its own environment's policy."
1911
1911
  },
1912
1912
  "ApiCredentialScopeSet": {
1913
1913
  "type": "array",
@@ -2123,7 +2123,7 @@
2123
2123
  "connections:write",
2124
2124
  "connections:read"
2125
2125
  ],
2126
- "description": "One organization-credential capability. `organization:read` is the organization's project directory — listing the organization's projects and reading one — and is the only scope here a project credential can never carry; it authorizes nothing below the organization. Every other name is the project-credential vocabulary unchanged, and is exercised only on the environment-scoped routes, where the project and environment come from the path and the credential's authority is the intersection of these scopes with the project-credential vocabulary. An organization credential is therefore never wider than a project credential would have been for the same project environment."
2126
+ "description": "One organization-credential capability. `organization:read` is the organization's project directory \u2014 listing the organization's projects and reading one \u2014 and is the only scope here a project credential can never carry; it authorizes nothing below the organization. Every other name is the project-credential vocabulary unchanged, and is exercised only on the environment-scoped routes, where the project and environment come from the path and the credential's authority is the intersection of these scopes with the project-credential vocabulary. An organization credential is therefore never wider than a project credential would have been for the same project environment."
2127
2127
  },
2128
2128
  "OrganizationCredentialScopeSet": {
2129
2129
  "type": "array",
@@ -2456,7 +2456,7 @@
2456
2456
  "type": "string",
2457
2457
  "minLength": 1,
2458
2458
  "maxLength": 100,
2459
- "pattern": "^[^\u0000-\u001f-Ÿ

]+$"
2459
+ "pattern": "^[^\u0000-\u001f-Ÿ

\u007f-\u009f\u2028\u2029]+$"
2460
2460
  },
2461
2461
  "asserted_at": {
2462
2462
  "$ref": "#/$defs/Timestamp"
@@ -2542,7 +2542,7 @@
2542
2542
  "type": "string",
2543
2543
  "minLength": 1,
2544
2544
  "maxLength": 100,
2545
- "pattern": "^[^\u0000-\u001f-Ÿ

]+$"
2545
+ "pattern": "^[^\u0000-\u001f-Ÿ

\u007f-\u009f\u2028\u2029]+$"
2546
2546
  },
2547
2547
  "asserted_at": {
2548
2548
  "$ref": "#/$defs/Timestamp"
@@ -2631,7 +2631,7 @@
2631
2631
  "type": "string",
2632
2632
  "minLength": 1,
2633
2633
  "maxLength": 100,
2634
- "pattern": "^[^\u0000-\u001f-Ÿ

]+$"
2634
+ "pattern": "^[^\u0000-\u001f-Ÿ

\u007f-\u009f\u2028\u2029]+$"
2635
2635
  },
2636
2636
  "asserted_at": {
2637
2637
  "$ref": "#/$defs/Timestamp"
@@ -2718,7 +2718,7 @@
2718
2718
  "type": "string",
2719
2719
  "minLength": 1,
2720
2720
  "maxLength": 100,
2721
- "pattern": "^[^\u0000-\u001f-Ÿ

]+$"
2721
+ "pattern": "^[^\u0000-\u001f-Ÿ

\u007f-\u009f\u2028\u2029]+$"
2722
2722
  },
2723
2723
  "asserted_at": {
2724
2724
  "$ref": "#/$defs/Timestamp"
@@ -4356,7 +4356,7 @@
4356
4356
  "$ref": "#/$defs/Metadata"
4357
4357
  },
4358
4358
  "interaction": {
4359
- "$ref": "interaction-v1.schema.json#/$defs/InteractionRequest"
4359
+ "$ref": "interaction-v1.schema.json#/$defs/InteractionSendRequest"
4360
4360
  },
4361
4361
  "purpose": {
4362
4362
  "enum": ["service", "marketing"],
@@ -4479,6 +4479,10 @@
4479
4479
  },
4480
4480
  "diagnostic_id": {
4481
4481
  "$ref": "#/$defs/DiagnosticId"
4482
+ },
4483
+ "interaction_expires_at": {
4484
+ "$ref": "#/$defs/Timestamp",
4485
+ "description": "Authoritatively resolved answer deadline shared by this broadcast\u2019s interactions, independent of delivery expires_at."
4482
4486
  }
4483
4487
  },
4484
4488
  "additionalProperties": false
@@ -5398,7 +5402,7 @@
5398
5402
  "$ref": "#/$defs/Metadata"
5399
5403
  },
5400
5404
  "interaction": {
5401
- "$ref": "interaction-v1.schema.json#/$defs/InteractionRequest"
5405
+ "$ref": "interaction-v1.schema.json#/$defs/InteractionSendRequest"
5402
5406
  },
5403
5407
  "purpose": {
5404
5408
  "enum": ["service", "marketing"],
@@ -7025,9 +7029,11 @@
7025
7029
  "billing:write",
7026
7030
  "billing:read",
7027
7031
  "teammates:write",
7028
- "teammates:read"
7032
+ "teammates:read",
7033
+ "notifications:self",
7034
+ "notifications:team"
7029
7035
  ],
7030
- "description": "An organization-level capability carried by a human session or an OAuth grant. Teammate scopes reach the organization's people: who is in it, their roles and their projects. Billing scopes grant financial access separately from project configuration. Project and organization API credentials cannot administer billing."
7036
+ "description": "An organization-level capability carried by a human session or an OAuth grant. notifications:self permits only personal operator notifications, own preferences and project discovery. notifications:team additionally permits authorized teammate lookup, direct notifications and predefined operators broadcasts. Notification profiles exclude customer messaging and administration and are requested separately from broader scopes. Teammate scopes reach the organization's people: who is in it, their roles and their projects. Billing scopes grant financial access separately from project configuration. Project and organization API credentials cannot administer billing."
7031
7037
  },
7032
7038
  "OAuthScope": {
7033
7039
  "description": "A scope an OAuth grant may carry. Project-level scopes are the credential vocabulary unchanged; organization-level scopes exist only here and on a human session.",
@@ -7060,7 +7066,7 @@
7060
7066
  "minLength": 5,
7061
7067
  "maxLength": 32,
7062
7068
  "pattern": "^[A-Za-z][A-Za-z0-9_]{1,28}[Bb][Oo][Tt]$",
7063
- "description": "The @username Telegram's own creation dialog is prefilled with, constrained to Telegram's own rule: 5 to 32 characters, a letter first, letters, digits, and underscores after it, ending in `bot` in any case. The caller proposes it because it is the handle every subscriber sees forever and it cannot be changed once the bot exists — a WhooshBang-generated handle would permanently brand a customer's own bot with our naming, which is the one thing customer-owned modes exist to prevent. It is a suggestion and not a reservation: the administrator can type a different handle into Telegram's dialog, which validates the shape and refuses a handle somebody already holds."
7069
+ "description": "The @username Telegram's own creation dialog is prefilled with, constrained to Telegram's own rule: 5 to 32 characters, a letter first, letters, digits, and underscores after it, ending in `bot` in any case. The caller proposes it because it is the handle every subscriber sees forever and it cannot be changed once the bot exists \u2014 a WhooshBang-generated handle would permanently brand a customer's own bot with our naming, which is the one thing customer-owned modes exist to prevent. It is a suggestion and not a reservation: the administrator can type a different handle into Telegram's dialog, which validates the shape and refuses a handle somebody already holds."
7064
7070
  },
7065
7071
  "ChannelConnectionStatus": {
7066
7072
  "type": "string",
@@ -7076,7 +7082,7 @@
7076
7082
  "failed",
7077
7083
  "expired"
7078
7084
  ],
7079
- "description": "How far a guided setup has reached, which is the only thing a caller can act on while a connection is still `pending`. `awaiting_handoff` waits for the administrator to open the link on the Telegram account that will own the bot; `awaiting_creation` means that Telegram account is bound and the bot does not exist yet; `provisioning` means the bot exists and WhooshBang is installing delivery on it. `failed` and `expired` end the attempt without ending the connection — another attempt can be started on the same `pending` connection — and neither withdraws a bot Telegram has already created, because no provider API can delete one."
7085
+ "description": "How far a guided setup has reached, which is the only thing a caller can act on while a connection is still `pending`. `awaiting_handoff` waits for the administrator to open the link on the Telegram account that will own the bot; `awaiting_creation` means that Telegram account is bound and the bot does not exist yet; `provisioning` means the bot exists and WhooshBang is installing delivery on it. `failed` and `expired` end the attempt without ending the connection \u2014 another attempt can be started on the same `pending` connection \u2014 and neither withdraws a bot Telegram has already created, because no provider API can delete one."
7080
7086
  },
7081
7087
  "HostedChannelConnectionSetupStatus": {
7082
7088
  "type": "string",
@@ -7293,7 +7299,7 @@
7293
7299
  },
7294
7300
  "ChannelConnectionSetup": {
7295
7301
  "type": "object",
7296
- "description": "The unfinished half of a customer-managed connection: what the administrator still has to do, and by when. It is deliberately incapable of carrying a secret. The new bot's token travels from Telegram to WhooshBang server to server, so nothing here is credential material and nothing here references any — which is what makes this object safe to poll, render in a dashboard, or hand to whoever is doing the setup.",
7302
+ "description": "The unfinished half of a customer-managed connection: what the administrator still has to do, and by when. It is deliberately incapable of carrying a secret. The new bot's token travels from Telegram to WhooshBang server to server, so nothing here is credential material and nothing here references any \u2014 which is what makes this object safe to poll, render in a dashboard, or hand to whoever is doing the setup.",
7297
7303
  "required": [
7298
7304
  "status",
7299
7305
  "handoff_url",
@@ -7419,7 +7425,7 @@
7419
7425
  },
7420
7426
  "CustomerApplicationConnectionSetup": {
7421
7427
  "type": "object",
7422
- "description": "The unfinished half of a connection through an application the customer creates and owns: what the owner still has to do at the provider, and by when.\n\nIt is deliberately incapable of carrying a secret. The creation link contains configuration and nothing else, the secrets the owner supplies are write-only and have no field here or on the connection, and the installation grant never travels through a browser at all — WhooshBang obtains it from the provider's own installation exchange. Everything here is therefore safe to poll, render, and hand to whoever is doing the setup.",
7428
+ "description": "The unfinished half of a connection through an application the customer creates and owns: what the owner still has to do at the provider, and by when.\n\nIt is deliberately incapable of carrying a secret. The creation link contains configuration and nothing else, the secrets the owner supplies are write-only and have no field here or on the connection, and the installation grant never travels through a browser at all \u2014 WhooshBang obtains it from the provider's own installation exchange. Everything here is therefore safe to poll, render, and hand to whoever is doing the setup.",
7423
7429
  "required": [
7424
7430
  "status",
7425
7431
  "creation_url",
@@ -7505,7 +7511,7 @@
7505
7511
  "type": "string",
7506
7512
  "minLength": 1,
7507
7513
  "maxLength": 100,
7508
- "pattern": "^[^\u0000-\u001f-Ÿ

]+$",
7514
+ "pattern": "^[^\u0000-\u001f-Ÿ

\u007f-\u009f\u2028\u2029]+$",
7509
7515
  "description": "A sender display name without control characters. It is encoded safely as a structured address field and never accepted as a raw header."
7510
7516
  },
7511
7517
  "EmailSubjectPolicy": {
@@ -7585,7 +7591,7 @@
7585
7591
  "type": "string",
7586
7592
  "minLength": 1,
7587
7593
  "maxLength": 100,
7588
- "pattern": "^[^\u0000-\u001f-Ÿ

]+$",
7594
+ "pattern": "^[^\u0000-\u001f-Ÿ

\u007f-\u009f\u2028\u2029]+$",
7589
7595
  "description": "A bounded customer description of where consent was collected. The authenticated asserting actor is recorded by the service, never accepted from this object."
7590
7596
  },
7591
7597
  "asserted_at": {
@@ -7677,13 +7683,13 @@
7677
7683
  "type": "string",
7678
7684
  "minLength": 1,
7679
7685
  "maxLength": 253,
7680
- "pattern": "^[^\u0000-\u001f-Ÿ

]+$"
7686
+ "pattern": "^[^\u0000-\u001f-Ÿ

\u007f-\u009f\u2028\u2029]+$"
7681
7687
  },
7682
7688
  "value": {
7683
7689
  "type": "string",
7684
7690
  "minLength": 1,
7685
7691
  "maxLength": 2048,
7686
- "pattern": "^[^\u0000-\u001f-Ÿ

]+$"
7692
+ "pattern": "^[^\u0000-\u001f-Ÿ

\u007f-\u009f\u2028\u2029]+$"
7687
7693
  },
7688
7694
  "priority": {
7689
7695
  "type": "integer",
@@ -7713,13 +7719,13 @@
7713
7719
  "type": "string",
7714
7720
  "minLength": 1,
7715
7721
  "maxLength": 253,
7716
- "pattern": "^[^\u0000-\u001f-Ÿ

]+$"
7722
+ "pattern": "^[^\u0000-\u001f-Ÿ

\u007f-\u009f\u2028\u2029]+$"
7717
7723
  },
7718
7724
  "value": {
7719
7725
  "type": "string",
7720
7726
  "minLength": 1,
7721
7727
  "maxLength": 2048,
7722
- "pattern": "^[^\u0000-\u001f-Ÿ

]+$"
7728
+ "pattern": "^[^\u0000-\u001f-Ÿ

\u007f-\u009f\u2028\u2029]+$"
7723
7729
  },
7724
7730
  "status": {
7725
7731
  "enum": [
@@ -7752,7 +7758,7 @@
7752
7758
  "type": "string",
7753
7759
  "minLength": 1,
7754
7760
  "maxLength": 100,
7755
- "pattern": "^[^\u0000-\u001f-Ÿ

]+$"
7761
+ "pattern": "^[^\u0000-\u001f-Ÿ

\u007f-\u009f\u2028\u2029]+$"
7756
7762
  },
7757
7763
  "instructions_url": {
7758
7764
  "type": "string",
@@ -8190,7 +8196,7 @@
8190
8196
  "handle": {
8191
8197
  "type": "string",
8192
8198
  "minLength": 1,
8193
- "description": "The visible provider handle without a provider-specific sigil — a bot username, a workspace or app display name, a sender address. Never a credential, installation identifier or provider-native identity reference."
8199
+ "description": "The visible provider handle without a provider-specific sigil \u2014 a bot username, a workspace or app display name, a sender address. Never a credential, installation identifier or provider-native identity reference."
8194
8200
  }
8195
8201
  },
8196
8202
  "allOf": [
@@ -8312,7 +8318,7 @@
8312
8318
  },
8313
8319
  "HostedChannelConnection": {
8314
8320
  "type": "object",
8315
- "description": "A WhooshBang-owned application installed at one provider authority — a workspace, a tenant, a server — and referenced by one exact environment connection.",
8321
+ "description": "A WhooshBang-owned application installed at one provider authority \u2014 a workspace, a tenant, a server \u2014 and referenced by one exact environment connection.",
8316
8322
  "required": [
8317
8323
  "id",
8318
8324
  "project_id",
@@ -8804,7 +8810,7 @@
8804
8810
  "additionalProperties": false
8805
8811
  },
8806
8812
  "CreateChannelConnectionRequest": {
8807
- "description": "Selects the provider identity this environment will send through, discriminated on `mode`, and naming the provider it is for. The two customer-owned modes differ only in where the bot comes from: `customer_byok` connects one the customer already has, and `customer_managed` has Telegram create a new one on the administrator's own Telegram account. Neither supersedes the other, because a bot that already exists can never be adopted into managed mode — the manager that may act for a bot is fixed when the bot is created.",
8813
+ "description": "Selects the provider identity this environment will send through, discriminated on `mode`, and naming the provider it is for. The two customer-owned modes differ only in where the bot comes from: `customer_byok` connects one the customer already has, and `customer_managed` has Telegram create a new one on the administrator's own Telegram account. Neither supersedes the other, because a bot that already exists can never be adopted into managed mode \u2014 the manager that may act for a bot is fixed when the bot is created.",
8808
8814
  "oneOf": [
8809
8815
  {
8810
8816
  "type": "object",
@@ -8819,7 +8825,7 @@
8819
8825
  "type": "string",
8820
8826
  "const": "telegram",
8821
8827
  "default": "telegram",
8822
- "description": "This established setup arm is Telegram-only. Omitted means telegram. A provider whose setup is structurally different — an OAuth install rather than a credential — has its own arm of this union rather than bending this one."
8828
+ "description": "This established setup arm is Telegram-only. Omitted means telegram. A provider whose setup is structurally different \u2014 an OAuth install rather than a credential \u2014 has its own arm of this union rather than bending this one."
8823
8829
  },
8824
8830
  "display_name": {
8825
8831
  "type": "string",
@@ -8834,7 +8840,7 @@
8834
8840
  },
8835
8841
  {
8836
8842
  "type": "object",
8837
- "description": "Connects a provider identity the customer already owns by supplying its credential.\n\nThis arm carries no identity field, and the absence is deliberate. `provider_identity_digest` is unique across every tenant, so an endpoint that digested a client-supplied identity would answer \"already claimed\" for a bot the caller does not own — an oracle for whether any given bot is connected to WhooshBang by anyone. The identity is derived server-side from a provider-validated `getMe` on the submitted credential, which no caller can forge without holding the credential itself.",
8843
+ "description": "Connects a provider identity the customer already owns by supplying its credential.\n\nThis arm carries no identity field, and the absence is deliberate. `provider_identity_digest` is unique across every tenant, so an endpoint that digested a client-supplied identity would answer \"already claimed\" for a bot the caller does not own \u2014 an oracle for whether any given bot is connected to WhooshBang by anyone. The identity is derived server-side from a provider-validated `getMe` on the submitted credential, which no caller can forge without holding the credential itself.",
8838
8844
  "required": ["mode", "display_name", "credential"],
8839
8845
  "properties": {
8840
8846
  "mode": {
@@ -8845,7 +8851,7 @@
8845
8851
  "type": "string",
8846
8852
  "const": "telegram",
8847
8853
  "default": "telegram",
8848
- "description": "This established setup arm is Telegram-only. Omitted means telegram. A provider whose setup is structurally different — an OAuth install rather than a credential — has its own arm of this union rather than bending this one."
8854
+ "description": "This established setup arm is Telegram-only. Omitted means telegram. A provider whose setup is structurally different \u2014 an OAuth install rather than a credential \u2014 has its own arm of this union rather than bending this one."
8849
8855
  },
8850
8856
  "display_name": {
8851
8857
  "type": "string",
@@ -8879,7 +8885,7 @@
8879
8885
  "type": "string",
8880
8886
  "const": "telegram",
8881
8887
  "default": "telegram",
8882
- "description": "This established setup arm is Telegram-only. Omitted means telegram. A provider whose setup is structurally different — an OAuth install rather than a credential — has its own arm of this union rather than bending this one."
8888
+ "description": "This established setup arm is Telegram-only. Omitted means telegram. A provider whose setup is structurally different \u2014 an OAuth install rather than a credential \u2014 has its own arm of this union rather than bending this one."
8883
8889
  },
8884
8890
  "display_name": {
8885
8891
  "type": "string",
@@ -8893,14 +8899,14 @@
8893
8899
  "type": "string",
8894
8900
  "minLength": 1,
8895
8901
  "maxLength": 64,
8896
- "description": "The display name prefilled in the same dialog — what a subscriber reads at the top of the chat, as opposed to the @username they address. Unlike the username this one can be changed afterwards, so it is a convenience rather than a commitment. Omit it and Telegram's dialog opens with no name suggested."
8902
+ "description": "The display name prefilled in the same dialog \u2014 what a subscriber reads at the top of the chat, as opposed to the @username they address. Unlike the username this one can be changed afterwards, so it is a convenience rather than a commitment. Omit it and Telegram's dialog opens with no name suggested."
8897
8903
  }
8898
8904
  },
8899
8905
  "additionalProperties": false
8900
8906
  },
8901
8907
  {
8902
8908
  "type": "object",
8903
- "description": "Starts a guided setup in which the customer creates and owns their own application at the provider. The connection comes back `pending` with an `app_setup` carrying the provider's own creation link, already filled in with this application's configuration where the provider allows it.\n\nThis arm carries no credential field, because at this point no credential exists: the provider issues the application's secrets when the owner creates it. They arrive later through the write-only rotation boundary, and the installation grant never arrives from the caller at all — WhooshBang obtains it from the provider's installation exchange. The three names here are what the owner's own recipients will see, which is why the caller chooses them rather than WhooshBang: a generated name would permanently brand a customer's own application with ours, and that is the one thing customer-owned modes exist to prevent.\n\nA provider that does not set a connection up this way refuses the request as an unavailable setup mode. Each provider may also refuse a name its own rules do not allow, within the bounds stated here.",
8909
+ "description": "Starts a guided setup in which the customer creates and owns their own application at the provider. The connection comes back `pending` with an `app_setup` carrying the provider's own creation link, already filled in with this application's configuration where the provider allows it.\n\nThis arm carries no credential field, because at this point no credential exists: the provider issues the application's secrets when the owner creates it. They arrive later through the write-only rotation boundary, and the installation grant never arrives from the caller at all \u2014 WhooshBang obtains it from the provider's installation exchange. The three names here are what the owner's own recipients will see, which is why the caller chooses them rather than WhooshBang: a generated name would permanently brand a customer's own application with ours, and that is the one thing customer-owned modes exist to prevent.\n\nA provider that does not set a connection up this way refuses the request as an unavailable setup mode. Each provider may also refuse a name its own rules do not allow, within the bounds stated here.",
8904
8910
  "required": [
8905
8911
  "mode",
8906
8912
  "provider",
@@ -8933,7 +8939,7 @@
8933
8939
  "type": "string",
8934
8940
  "minLength": 1,
8935
8941
  "maxLength": 80,
8936
- "description": "The bot display name in the same flow — what a recipient reads at the top of the conversation."
8942
+ "description": "The bot display name in the same flow \u2014 what a recipient reads at the top of the conversation."
8937
8943
  },
8938
8944
  "app_description": {
8939
8945
  "type": "string",
@@ -9033,11 +9039,11 @@
9033
9039
  ]
9034
9040
  },
9035
9041
  "RotateChannelConnectionCredentialRequest": {
9036
- "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. An application the customer owns has separate authorities and names each one it hands over. 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.",
9042
+ "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. An application the customer owns has separate authorities and names each one it hands over. 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 \u2014 a write that never reads the old value back \u2014 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.",
9037
9043
  "oneOf": [
9038
9044
  {
9039
9045
  "type": "object",
9040
- "description": "Supplies or replaces the application-specific authorities of an application the customer owns. Every authority that provider's application takes is 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 authorities are sealed as one versioned set, so replacing one alone would mean decrypting the others and writing them back — reading secrets at a boundary whose whole purpose is that nothing reads them.\n\n`application_identity` is safe metadata; the secrets are write-only and are never readable afterwards by any surface. `ingress_secret` is optional in shape because not every provider signs inbound requests with a secret the owner holds; a provider whose application does have one refuses a request that leaves it out, and refuses an `application_identity` that is not in its own format.\n\nThere is no bot-token field here, and the absence is the point: WhooshBang obtains the installation grant from the provider's installation exchange, so it never passes through a browser, a clipboard, this request, or any response.",
9046
+ "description": "Supplies or replaces the application-specific authorities of an application the customer owns. Every authority that provider's application takes is 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 authorities are sealed as one versioned set, so replacing one alone would mean decrypting the others and writing them back \u2014 reading secrets at a boundary whose whole purpose is that nothing reads them.\n\n`application_identity` is safe metadata; the secrets are write-only and are never readable afterwards by any surface. `ingress_secret` is optional in shape because not every provider signs inbound requests with a secret the owner holds; a provider whose application does have one refuses a request that leaves it out, and refuses an `application_identity` that is not in its own format.\n\nThere is no bot-token field here, and the absence is the point: WhooshBang obtains the installation grant from the provider's installation exchange, so it never passes through a browser, a clipboard, this request, or any response.",
9041
9047
  "required": [
9042
9048
  "provider",
9043
9049
  "application_identity",
@@ -9052,21 +9058,21 @@
9052
9058
  "minLength": 1,
9053
9059
  "maxLength": 128,
9054
9060
  "pattern": "^[\\x21-\\x7e]+$",
9055
- "description": "The identifier the provider issued for the application — for Slack, its Client ID. Safe application metadata rather than an authority: it identifies the application in an authorization redirect the provider's users can already see."
9061
+ "description": "The identifier the provider issued for the application \u2014 for Slack, its Client ID. Safe application metadata rather than an authority: it identifies the application in an authorization redirect the provider's users can already see."
9056
9062
  },
9057
9063
  "application_secret": {
9058
9064
  "type": "string",
9059
9065
  "minLength": 1,
9060
9066
  "maxLength": 512,
9061
9067
  "writeOnly": true,
9062
- "description": "The secret the application authenticates to the provider with — for Slack, its Client Secret. Write-only: sealed on arrival, never echoed here or by any later read, and reachable only by the exchanges that need it."
9068
+ "description": "The secret the application authenticates to the provider with \u2014 for Slack, its Client Secret. Write-only: sealed on arrival, never echoed here or by any later read, and reachable only by the exchanges that need it."
9063
9069
  },
9064
9070
  "ingress_secret": {
9065
9071
  "type": "string",
9066
9072
  "minLength": 1,
9067
9073
  "maxLength": 512,
9068
9074
  "writeOnly": true,
9069
- "description": "The secret that verifies an inbound request really came from the provider — for Slack, its Signing Secret. Write-only on the same terms, and reachable only by ingress verification."
9075
+ "description": "The secret that verifies an inbound request really came from the provider \u2014 for Slack, its Signing Secret. Write-only on the same terms, and reachable only by ingress verification."
9070
9076
  }
9071
9077
  },
9072
9078
  "additionalProperties": false
@@ -9088,7 +9094,7 @@
9088
9094
  },
9089
9095
  {
9090
9096
  "type": "object",
9091
- "description": "Rotates a `customer_managed` connection, whose replacement only Telegram can issue. Asked to rotate, Telegram revokes the bot's current token and returns the successor to the manager that created it, so there is no customer-supplied credential to send and no field here to send one in — a caller able to supply one would be describing a bot this mode never handed it. The identity is untouched: the same bot keeps the same @username and the same subscribers, and only the secret behind it is new.",
9097
+ "description": "Rotates a `customer_managed` connection, whose replacement only Telegram can issue. Asked to rotate, Telegram revokes the bot's current token and returns the successor to the manager that created it, so there is no customer-supplied credential to send and no field here to send one in \u2014 a caller able to supply one would be describing a bot this mode never handed it. The identity is untouched: the same bot keeps the same @username and the same subscribers, and only the secret behind it is new.",
9092
9098
  "required": ["source"],
9093
9099
  "properties": {
9094
9100
  "source": {
@@ -9161,7 +9167,7 @@
9161
9167
  "check_request_url",
9162
9168
  "contact_support"
9163
9169
  ],
9164
- "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."
9170
+ "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 \u2014 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."
9165
9171
  },
9166
9172
  "ConnectionTestEvidence": {
9167
9173
  "type": "object",
@@ -9171,7 +9177,7 @@
9171
9177
  "type": "string",
9172
9178
  "minLength": 1,
9173
9179
  "maxLength": 128,
9174
- "description": "The visible handle the test went out as — the same identity a subscriber would see."
9180
+ "description": "The visible handle the test went out as \u2014 the same identity a subscriber would see."
9175
9181
  },
9176
9182
  "workspace_display_name": {
9177
9183
  "type": "string",
@@ -9322,7 +9328,7 @@
9322
9328
  "$ref": "#/$defs/MessageContent"
9323
9329
  },
9324
9330
  "interaction": {
9325
- "$ref": "interaction-v1.schema.json#/$defs/InteractionRequest"
9331
+ "$ref": "interaction-v1.schema.json#/$defs/InteractionSendRequest"
9326
9332
  }
9327
9333
  },
9328
9334
  "allOf": [
@@ -9371,7 +9377,7 @@
9371
9377
  },
9372
9378
  "CustomerApplicationManifest": {
9373
9379
  "type": "object",
9374
- "description": "The exact configuration an application the customer creates and owns 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 the provider. It is built by the provider that owns the format, so `manifest` is that provider's own document and `provider` says whose. It carries configuration only: no organization, project, or environment, no credential, and no provider-native identifier. Any opaque handle inside the request URLs selects candidate setup state and authorizes nothing — a request still has to carry the provider's own proof over the untouched body before anything inside it is believed.",
9380
+ "description": "The exact configuration an application the customer creates and owns 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 the provider. It is built by the provider that owns the format, so `manifest` is that provider's own document and `provider` says whose. It carries configuration only: no organization, project, or environment, no credential, and no provider-native identifier. Any opaque handle inside the request URLs selects candidate setup state and authorizes nothing \u2014 a request still has to carry the provider's own proof over the untouched body before anything inside it is believed.",
9375
9381
  "required": [
9376
9382
  "provider",
9377
9383
  "creation_url",
@@ -9627,7 +9633,7 @@
9627
9633
  "type": "string",
9628
9634
  "minLength": 1,
9629
9635
  "maxLength": 100,
9630
- "pattern": "^[^\u0000-\u001f-Ÿ

]+$",
9636
+ "pattern": "^[^\u0000-\u001f-Ÿ

\u007f-\u009f\u2028\u2029]+$",
9631
9637
  "description": "A bounded customer description of where consent was collected. The authenticated asserting actor is recorded by the service, never accepted from this object."
9632
9638
  }
9633
9639
  },
@@ -9666,7 +9672,7 @@
9666
9672
  "type": "string",
9667
9673
  "minLength": 1,
9668
9674
  "maxLength": 100,
9669
- "pattern": "^[^\u0000-\u001f-Ÿ

]+$",
9675
+ "pattern": "^[^\u0000-\u001f-Ÿ

\u007f-\u009f\u2028\u2029]+$",
9670
9676
  "description": "A bounded customer description of the service relationship. The authenticated asserting actor is recorded by the service, never accepted from this object."
9671
9677
  },
9672
9678
  "basis": {
@@ -9733,7 +9739,7 @@
9733
9739
  },
9734
9740
  "SharedEmailStatus": {
9735
9741
  "type": "object",
9736
- "description": "Safe state of the exact shared sender generation and this organization’s domain hold. It conveys no recipient consent and never authorizes fallback.",
9742
+ "description": "Safe state of the exact shared sender generation and this organization\u2019s domain hold. It conveys no recipient consent and never authorizes fallback.",
9737
9743
  "required": [
9738
9744
  "sender_generation",
9739
9745
  "sender_state",
@@ -11641,7 +11647,7 @@
11641
11647
  "$ref": "#/$defs/Metadata"
11642
11648
  },
11643
11649
  "interaction": {
11644
- "$ref": "interaction-v1.schema.json#/$defs/InteractionRequest"
11650
+ "$ref": "interaction-v1.schema.json#/$defs/InteractionSendRequest"
11645
11651
  },
11646
11652
  "purpose": {
11647
11653
  "enum": ["service", "marketing"],
@@ -11732,7 +11738,7 @@
11732
11738
  },
11733
11739
  "OperatorPreferences": {
11734
11740
  "type": "object",
11735
- "description": "The authenticated person’s own destinations and preferred channel in this project. Shared credentials cannot manage a human’s preferences.",
11741
+ "description": "The authenticated person\u2019s own destinations and preferred channel in this project. Shared credentials cannot manage a human\u2019s preferences.",
11736
11742
  "required": ["operator", "settings", "connections"],
11737
11743
  "properties": {
11738
11744
  "operator": {
@@ -11768,7 +11774,7 @@
11768
11774
  },
11769
11775
  "OperatorConnectRequest": {
11770
11776
  "type": "object",
11771
- "description": "Connect the signed-in person’s own destination through a configured channel. This never changes another person’s channels.",
11777
+ "description": "Connect the signed-in person\u2019s own destination through a configured channel. This never changes another person\u2019s channels.",
11772
11778
  "required": ["connection_id", "channel"],
11773
11779
  "properties": {
11774
11780
  "connection_id": {