@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
|
@@ -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": "1.0.0-rc.
|
|
4
|
+
"x-contract-version": "1.0.0-rc.21",
|
|
5
5
|
"title": "WhooshBangV1ContractDocument",
|
|
6
6
|
"description": "Public request, resource, summary, and Problem Details models for the WhooshBang V1 REST API.",
|
|
7
7
|
"anyOf": [
|
|
@@ -41,6 +41,24 @@
|
|
|
41
41
|
{
|
|
42
42
|
"$ref": "#/$defs/ApiCredentialRotation"
|
|
43
43
|
},
|
|
44
|
+
{
|
|
45
|
+
"$ref": "#/$defs/CreateAccountCredentialRequest"
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
"$ref": "#/$defs/RotateAccountCredentialRequest"
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
"$ref": "#/$defs/AccountCredential"
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
"$ref": "#/$defs/AccountCredentialCollection"
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
"$ref": "#/$defs/AccountCredentialSecretReveal"
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
"$ref": "#/$defs/AccountCredentialRotation"
|
|
61
|
+
},
|
|
44
62
|
{
|
|
45
63
|
"$ref": "#/$defs/CreateCustomerEndpointRequest"
|
|
46
64
|
},
|
|
@@ -104,6 +122,18 @@
|
|
|
104
122
|
{
|
|
105
123
|
"$ref": "#/$defs/AttachNotifierConnectionRequest"
|
|
106
124
|
},
|
|
125
|
+
{
|
|
126
|
+
"$ref": "#/$defs/ConnectionTest"
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
"$ref": "#/$defs/ConnectionTestCollection"
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
"$ref": "#/$defs/CreateConnectionTestRequest"
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
"$ref": "#/$defs/CustomerOwnedSlackAppManifest"
|
|
136
|
+
},
|
|
107
137
|
{
|
|
108
138
|
"$ref": "#/$defs/ProjectRecipientExperience"
|
|
109
139
|
},
|
|
@@ -916,6 +946,210 @@
|
|
|
916
946
|
},
|
|
917
947
|
"additionalProperties": false
|
|
918
948
|
},
|
|
949
|
+
"AccountCredentialId": {
|
|
950
|
+
"type": "string",
|
|
951
|
+
"pattern": "^acred_[A-Za-z0-9_-]{21}[AQgw]$",
|
|
952
|
+
"description": "acred_ followed by the canonical unpadded base64url encoding of exactly 16 random bytes."
|
|
953
|
+
},
|
|
954
|
+
"AccountCredentialPrefix": {
|
|
955
|
+
"type": "string",
|
|
956
|
+
"pattern": "^wb_ac1\\.acred_[A-Za-z0-9_-]{8}$",
|
|
957
|
+
"description": "A safe display prefix containing the account-credential format marker and first eight base64url identity characters, never secret bytes."
|
|
958
|
+
},
|
|
959
|
+
"AccountCredentialSecret": {
|
|
960
|
+
"type": "string",
|
|
961
|
+
"pattern": "^wb_ac1\\.acred_[A-Za-z0-9_-]{21}[AQgw]\\.[A-Za-z0-9_-]{42}[AEIMQUYcgkosw048]$",
|
|
962
|
+
"description": "The exact one-time runtime bearer syntax. The final segment is canonical unpadded base64url for exactly 32 random secret bytes."
|
|
963
|
+
},
|
|
964
|
+
"AccountCredentialScope": {
|
|
965
|
+
"type": "string",
|
|
966
|
+
"enum": [
|
|
967
|
+
"account:read",
|
|
968
|
+
"projects:write",
|
|
969
|
+
"projects:read",
|
|
970
|
+
"messages:write",
|
|
971
|
+
"messages:read",
|
|
972
|
+
"subscriptions:write",
|
|
973
|
+
"subscriptions:read",
|
|
974
|
+
"machine-clients:write",
|
|
975
|
+
"machine-clients:read",
|
|
976
|
+
"capabilities:read",
|
|
977
|
+
"connections:write",
|
|
978
|
+
"connections:read"
|
|
979
|
+
],
|
|
980
|
+
"description": "One account-credential capability. `account:read` is the account's project directory — listing the account's projects and reading one — and is the only scope here a project credential can never carry; it authorizes nothing below the account. Every other name is the project-credential vocabulary unchanged, and is exercised only on the environment-scoped routes, where the project and environment come from the path and the credential's authority is the intersection of these scopes with the project-credential vocabulary. An account credential is therefore never wider than a project credential would have been for the same project environment."
|
|
981
|
+
},
|
|
982
|
+
"AccountCredentialScopeSet": {
|
|
983
|
+
"type": "array",
|
|
984
|
+
"minItems": 1,
|
|
985
|
+
"maxItems": 12,
|
|
986
|
+
"uniqueItems": true,
|
|
987
|
+
"items": {
|
|
988
|
+
"$ref": "#/$defs/AccountCredentialScope"
|
|
989
|
+
}
|
|
990
|
+
},
|
|
991
|
+
"CreateAccountCredentialRequest": {
|
|
992
|
+
"type": "object",
|
|
993
|
+
"description": "Creates one credential against the whole account rather than one project environment.",
|
|
994
|
+
"required": ["label", "scopes"],
|
|
995
|
+
"properties": {
|
|
996
|
+
"label": {
|
|
997
|
+
"type": "string",
|
|
998
|
+
"minLength": 1,
|
|
999
|
+
"maxLength": 100
|
|
1000
|
+
},
|
|
1001
|
+
"scopes": {
|
|
1002
|
+
"$ref": "#/$defs/AccountCredentialScopeSet"
|
|
1003
|
+
}
|
|
1004
|
+
},
|
|
1005
|
+
"additionalProperties": false
|
|
1006
|
+
},
|
|
1007
|
+
"RotateAccountCredentialRequest": {
|
|
1008
|
+
"type": "object",
|
|
1009
|
+
"description": "Creates a replacement with the same label and scopes and schedules terminal revocation of the replaced credential after the declared overlap.",
|
|
1010
|
+
"required": ["overlap_seconds"],
|
|
1011
|
+
"properties": {
|
|
1012
|
+
"overlap_seconds": {
|
|
1013
|
+
"type": "integer",
|
|
1014
|
+
"minimum": 0,
|
|
1015
|
+
"maximum": 86400
|
|
1016
|
+
}
|
|
1017
|
+
},
|
|
1018
|
+
"additionalProperties": false
|
|
1019
|
+
},
|
|
1020
|
+
"AccountCredential": {
|
|
1021
|
+
"type": "object",
|
|
1022
|
+
"description": "Safe account-scoped API credential metadata. It carries no project or environment, because it names neither: a request does, in its path. It never contains a bearer secret, digest, Clerk identifier, or digest-key reference.",
|
|
1023
|
+
"required": [
|
|
1024
|
+
"id",
|
|
1025
|
+
"prefix",
|
|
1026
|
+
"label",
|
|
1027
|
+
"scopes",
|
|
1028
|
+
"created_by",
|
|
1029
|
+
"created_at",
|
|
1030
|
+
"status"
|
|
1031
|
+
],
|
|
1032
|
+
"properties": {
|
|
1033
|
+
"id": {
|
|
1034
|
+
"$ref": "#/$defs/AccountCredentialId"
|
|
1035
|
+
},
|
|
1036
|
+
"prefix": {
|
|
1037
|
+
"$ref": "#/$defs/AccountCredentialPrefix"
|
|
1038
|
+
},
|
|
1039
|
+
"label": {
|
|
1040
|
+
"type": "string",
|
|
1041
|
+
"minLength": 1,
|
|
1042
|
+
"maxLength": 100
|
|
1043
|
+
},
|
|
1044
|
+
"scopes": {
|
|
1045
|
+
"$ref": "#/$defs/AccountCredentialScopeSet"
|
|
1046
|
+
},
|
|
1047
|
+
"created_by": {
|
|
1048
|
+
"$ref": "#/$defs/OpaqueIdentifier",
|
|
1049
|
+
"description": "An opaque WhooshBang member identifier. It is never a Clerk user identifier."
|
|
1050
|
+
},
|
|
1051
|
+
"created_at": {
|
|
1052
|
+
"$ref": "#/$defs/Timestamp"
|
|
1053
|
+
},
|
|
1054
|
+
"last_used_at": {
|
|
1055
|
+
"$ref": "#/$defs/Timestamp"
|
|
1056
|
+
},
|
|
1057
|
+
"scheduled_revocation_at": {
|
|
1058
|
+
"$ref": "#/$defs/Timestamp",
|
|
1059
|
+
"description": "When an active credential in a rotation overlap becomes terminally revoked."
|
|
1060
|
+
},
|
|
1061
|
+
"status": {
|
|
1062
|
+
"type": "string",
|
|
1063
|
+
"enum": ["active", "revoked"]
|
|
1064
|
+
},
|
|
1065
|
+
"revoked_at": {
|
|
1066
|
+
"$ref": "#/$defs/Timestamp"
|
|
1067
|
+
}
|
|
1068
|
+
},
|
|
1069
|
+
"allOf": [
|
|
1070
|
+
{
|
|
1071
|
+
"if": {
|
|
1072
|
+
"properties": {
|
|
1073
|
+
"status": {
|
|
1074
|
+
"const": "revoked"
|
|
1075
|
+
}
|
|
1076
|
+
},
|
|
1077
|
+
"required": ["status"]
|
|
1078
|
+
},
|
|
1079
|
+
"then": {
|
|
1080
|
+
"required": ["revoked_at"],
|
|
1081
|
+
"properties": {
|
|
1082
|
+
"revoked_at": {},
|
|
1083
|
+
"scheduled_revocation_at": false
|
|
1084
|
+
}
|
|
1085
|
+
},
|
|
1086
|
+
"else": {
|
|
1087
|
+
"properties": {
|
|
1088
|
+
"revoked_at": false
|
|
1089
|
+
}
|
|
1090
|
+
}
|
|
1091
|
+
}
|
|
1092
|
+
],
|
|
1093
|
+
"additionalProperties": false
|
|
1094
|
+
},
|
|
1095
|
+
"AccountCredentialCollection": {
|
|
1096
|
+
"type": "object",
|
|
1097
|
+
"description": "A stable page of safe account-credential metadata for one account.",
|
|
1098
|
+
"required": ["items"],
|
|
1099
|
+
"properties": {
|
|
1100
|
+
"items": {
|
|
1101
|
+
"type": "array",
|
|
1102
|
+
"maxItems": 100,
|
|
1103
|
+
"items": {
|
|
1104
|
+
"$ref": "#/$defs/AccountCredential"
|
|
1105
|
+
}
|
|
1106
|
+
},
|
|
1107
|
+
"next_cursor": {
|
|
1108
|
+
"type": "string",
|
|
1109
|
+
"minLength": 1,
|
|
1110
|
+
"maxLength": 256,
|
|
1111
|
+
"pattern": "^[A-Za-z0-9_-]+$"
|
|
1112
|
+
},
|
|
1113
|
+
"page": {
|
|
1114
|
+
"$ref": "#/$defs/ListPageNumber"
|
|
1115
|
+
},
|
|
1116
|
+
"page_size": {
|
|
1117
|
+
"$ref": "#/$defs/ListPageSize"
|
|
1118
|
+
},
|
|
1119
|
+
"total": {
|
|
1120
|
+
"$ref": "#/$defs/ListTotal"
|
|
1121
|
+
}
|
|
1122
|
+
},
|
|
1123
|
+
"additionalProperties": false
|
|
1124
|
+
},
|
|
1125
|
+
"AccountCredentialSecretReveal": {
|
|
1126
|
+
"type": "object",
|
|
1127
|
+
"description": "The creation-only response. The secret is emitted in this response exactly once and is never available from list, rotate-target, revoke, or later read responses.",
|
|
1128
|
+
"required": ["credential", "secret"],
|
|
1129
|
+
"properties": {
|
|
1130
|
+
"credential": {
|
|
1131
|
+
"$ref": "#/$defs/AccountCredential"
|
|
1132
|
+
},
|
|
1133
|
+
"secret": {
|
|
1134
|
+
"$ref": "#/$defs/AccountCredentialSecret"
|
|
1135
|
+
}
|
|
1136
|
+
},
|
|
1137
|
+
"additionalProperties": false
|
|
1138
|
+
},
|
|
1139
|
+
"AccountCredentialRotation": {
|
|
1140
|
+
"type": "object",
|
|
1141
|
+
"description": "A creation-only replacement secret plus safe metadata for the credential it replaces.",
|
|
1142
|
+
"required": ["credential", "replaced_credential"],
|
|
1143
|
+
"properties": {
|
|
1144
|
+
"credential": {
|
|
1145
|
+
"$ref": "#/$defs/AccountCredentialSecretReveal"
|
|
1146
|
+
},
|
|
1147
|
+
"replaced_credential": {
|
|
1148
|
+
"$ref": "#/$defs/AccountCredential"
|
|
1149
|
+
}
|
|
1150
|
+
},
|
|
1151
|
+
"additionalProperties": false
|
|
1152
|
+
},
|
|
919
1153
|
"BindingSummary": {
|
|
920
1154
|
"type": "object",
|
|
921
1155
|
"description": "A safe channel binding identity. Raw Telegram chat and user identifiers are never exposed. Legacy bindings may omit connection_id because no exact connection was recorded when they were authorized.",
|
|
@@ -3611,6 +3845,7 @@
|
|
|
3611
3845
|
"resource_not_found",
|
|
3612
3846
|
"resource_expired",
|
|
3613
3847
|
"cancellation_too_late",
|
|
3848
|
+
"credential_account_mismatch",
|
|
3614
3849
|
"endpoint_rotation_conflict",
|
|
3615
3850
|
"rate_limited",
|
|
3616
3851
|
"provider_retryable",
|
|
@@ -4318,6 +4553,26 @@
|
|
|
4318
4553
|
}
|
|
4319
4554
|
}
|
|
4320
4555
|
},
|
|
4556
|
+
{
|
|
4557
|
+
"type": "object",
|
|
4558
|
+
"properties": {
|
|
4559
|
+
"type": {
|
|
4560
|
+
"const": "https://whooshbang.flowxo.com/problems/credential_account_mismatch"
|
|
4561
|
+
},
|
|
4562
|
+
"title": {
|
|
4563
|
+
"const": "Credential account mismatch"
|
|
4564
|
+
},
|
|
4565
|
+
"status": {
|
|
4566
|
+
"const": 403
|
|
4567
|
+
},
|
|
4568
|
+
"code": {
|
|
4569
|
+
"const": "credential_account_mismatch"
|
|
4570
|
+
},
|
|
4571
|
+
"retryable": {
|
|
4572
|
+
"const": false
|
|
4573
|
+
}
|
|
4574
|
+
}
|
|
4575
|
+
},
|
|
4321
4576
|
{
|
|
4322
4577
|
"type": "object",
|
|
4323
4578
|
"properties": {
|
|
@@ -4564,7 +4819,12 @@
|
|
|
4564
4819
|
},
|
|
4565
4820
|
"setup_modes": {
|
|
4566
4821
|
"type": "array",
|
|
4567
|
-
"
|
|
4822
|
+
"minItems": 1,
|
|
4823
|
+
"maxItems": 2,
|
|
4824
|
+
"uniqueItems": true,
|
|
4825
|
+
"items": {
|
|
4826
|
+
"enum": ["whooshbang_hosted", "customer_managed"]
|
|
4827
|
+
}
|
|
4568
4828
|
},
|
|
4569
4829
|
"lifecycle_operations": {
|
|
4570
4830
|
"type": "array",
|
|
@@ -4685,35 +4945,112 @@
|
|
|
4685
4945
|
}
|
|
4686
4946
|
]
|
|
4687
4947
|
},
|
|
4688
|
-
"
|
|
4948
|
+
"CustomerOwnedSlackSetupStatus": {
|
|
4689
4949
|
"type": "string",
|
|
4690
4950
|
"enum": [
|
|
4691
|
-
"
|
|
4692
|
-
"
|
|
4693
|
-
"
|
|
4694
|
-
"
|
|
4695
|
-
"
|
|
4951
|
+
"awaiting_app_creation",
|
|
4952
|
+
"awaiting_authorities",
|
|
4953
|
+
"awaiting_authorization",
|
|
4954
|
+
"awaiting_ingress_proof",
|
|
4955
|
+
"provisioning",
|
|
4956
|
+
"failed",
|
|
4957
|
+
"expired"
|
|
4696
4958
|
],
|
|
4697
|
-
"description": "
|
|
4959
|
+
"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."
|
|
4698
4960
|
},
|
|
4699
|
-
"
|
|
4961
|
+
"CustomerOwnedSlackAppAuthority": {
|
|
4962
|
+
"type": "string",
|
|
4963
|
+
"enum": ["client_identity", "client_secret", "signing_secret"],
|
|
4964
|
+
"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."
|
|
4965
|
+
},
|
|
4966
|
+
"CustomerOwnedSlackConnectionSetup": {
|
|
4700
4967
|
"type": "object",
|
|
4701
|
-
"description": "
|
|
4702
|
-
"required": [
|
|
4968
|
+
"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.",
|
|
4969
|
+
"required": [
|
|
4970
|
+
"status",
|
|
4971
|
+
"creation_url",
|
|
4972
|
+
"expires_at",
|
|
4973
|
+
"app_name",
|
|
4974
|
+
"bot_display_name",
|
|
4975
|
+
"outstanding_authorities"
|
|
4976
|
+
],
|
|
4703
4977
|
"properties": {
|
|
4704
|
-
"
|
|
4978
|
+
"status": { "$ref": "#/$defs/CustomerOwnedSlackSetupStatus" },
|
|
4979
|
+
"creation_url": {
|
|
4980
|
+
"type": "string",
|
|
4981
|
+
"format": "uri",
|
|
4982
|
+
"pattern": "^https://api\\.slack\\.com/apps\\?[^\\s]+$",
|
|
4983
|
+
"maxLength": 8192,
|
|
4984
|
+
"description": "Slack's own app-creation flow, opened with this app's manifest already filled in, so the owner reviews and presses Create rather than writing or pasting YAML. The URL carries configuration only: it names no account, project, or environment and contains no credential. The opaque handle it does carry selects candidate setup state and authorizes nothing."
|
|
4985
|
+
},
|
|
4986
|
+
"expires_at": {
|
|
4987
|
+
"$ref": "#/$defs/Timestamp",
|
|
4988
|
+
"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."
|
|
4989
|
+
},
|
|
4990
|
+
"authorization_url": {
|
|
4991
|
+
"type": "string",
|
|
4992
|
+
"format": "uri",
|
|
4993
|
+
"pattern": "^https://[^\\s]+$",
|
|
4994
|
+
"maxLength": 2048,
|
|
4995
|
+
"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."
|
|
4996
|
+
},
|
|
4997
|
+
"app_name": {
|
|
4705
4998
|
"type": "string",
|
|
4706
4999
|
"minLength": 1,
|
|
4707
|
-
"maxLength":
|
|
5000
|
+
"maxLength": 35,
|
|
5001
|
+
"description": "The name the owner chose, echoed so a resumed session shows the same app it started with. Slack's own limit."
|
|
4708
5002
|
},
|
|
4709
|
-
"
|
|
4710
|
-
"
|
|
5003
|
+
"bot_display_name": {
|
|
5004
|
+
"type": "string",
|
|
5005
|
+
"minLength": 1,
|
|
5006
|
+
"maxLength": 80,
|
|
5007
|
+
"description": "The bot display name the owner chose, echoed for the same reason. Slack's own limit."
|
|
4711
5008
|
},
|
|
4712
|
-
"
|
|
4713
|
-
"
|
|
4714
|
-
|
|
4715
|
-
|
|
4716
|
-
|
|
5009
|
+
"outstanding_authorities": {
|
|
5010
|
+
"type": "array",
|
|
5011
|
+
"maxItems": 3,
|
|
5012
|
+
"uniqueItems": true,
|
|
5013
|
+
"items": { "$ref": "#/$defs/CustomerOwnedSlackAppAuthority" },
|
|
5014
|
+
"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."
|
|
5015
|
+
},
|
|
5016
|
+
"failure_reason": {
|
|
5017
|
+
"type": "string",
|
|
5018
|
+
"minLength": 1,
|
|
5019
|
+
"maxLength": 200,
|
|
5020
|
+
"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."
|
|
5021
|
+
}
|
|
5022
|
+
},
|
|
5023
|
+
"additionalProperties": false
|
|
5024
|
+
},
|
|
5025
|
+
"ProviderInstallationStatus": {
|
|
5026
|
+
"type": "string",
|
|
5027
|
+
"enum": [
|
|
5028
|
+
"pending",
|
|
5029
|
+
"active",
|
|
5030
|
+
"reauthorization_required",
|
|
5031
|
+
"revoked",
|
|
5032
|
+
"invalid"
|
|
5033
|
+
],
|
|
5034
|
+
"description": "The safe customer-facing lifecycle of a provider installation. Credential and provider-native state stay private."
|
|
5035
|
+
},
|
|
5036
|
+
"ProviderInstallationSummary": {
|
|
5037
|
+
"type": "object",
|
|
5038
|
+
"description": "A safe summary projected only through an exact authorized connection. Internal installation ids, provider-native workspace ids, scope arrays, grant metadata and credentials are absent.",
|
|
5039
|
+
"required": ["display_name", "status", "health"],
|
|
5040
|
+
"properties": {
|
|
5041
|
+
"display_name": {
|
|
5042
|
+
"type": "string",
|
|
5043
|
+
"minLength": 1,
|
|
5044
|
+
"maxLength": 100
|
|
5045
|
+
},
|
|
5046
|
+
"status": {
|
|
5047
|
+
"$ref": "#/$defs/ProviderInstallationStatus"
|
|
5048
|
+
},
|
|
5049
|
+
"health": {
|
|
5050
|
+
"$ref": "#/$defs/ProviderHealthState"
|
|
5051
|
+
}
|
|
5052
|
+
},
|
|
5053
|
+
"additionalProperties": false
|
|
4717
5054
|
},
|
|
4718
5055
|
"HostedConnectionCapability": {
|
|
4719
5056
|
"type": "string",
|
|
@@ -4800,6 +5137,7 @@
|
|
|
4800
5137
|
"identity": { "$ref": "#/$defs/TelegramConnectionIdentity" },
|
|
4801
5138
|
"setup": { "$ref": "#/$defs/ChannelConnectionSetup" },
|
|
4802
5139
|
"authorization_setup": false,
|
|
5140
|
+
"app_setup": false,
|
|
4803
5141
|
"installation": false,
|
|
4804
5142
|
"row_version": {
|
|
4805
5143
|
"type": "integer",
|
|
@@ -4862,6 +5200,7 @@
|
|
|
4862
5200
|
},
|
|
4863
5201
|
"identity": { "$ref": "#/$defs/SlackConnectionIdentity" },
|
|
4864
5202
|
"setup": false,
|
|
5203
|
+
"app_setup": false,
|
|
4865
5204
|
"authorization_setup": {
|
|
4866
5205
|
"$ref": "#/$defs/HostedChannelConnectionSetup",
|
|
4867
5206
|
"description": "The additive hosted authorization state. Older tolerant readers discard this unknown field rather than interpreting it as Telegram setup."
|
|
@@ -4905,11 +5244,94 @@
|
|
|
4905
5244
|
},
|
|
4906
5245
|
"additionalProperties": false
|
|
4907
5246
|
},
|
|
5247
|
+
"CustomerOwnedSlackChannelConnection": {
|
|
5248
|
+
"type": "object",
|
|
5249
|
+
"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.",
|
|
5250
|
+
"required": [
|
|
5251
|
+
"id",
|
|
5252
|
+
"project_id",
|
|
5253
|
+
"environment_id",
|
|
5254
|
+
"environment",
|
|
5255
|
+
"provider",
|
|
5256
|
+
"mode",
|
|
5257
|
+
"display_name",
|
|
5258
|
+
"status",
|
|
5259
|
+
"health",
|
|
5260
|
+
"capabilities",
|
|
5261
|
+
"recovery_action",
|
|
5262
|
+
"row_version",
|
|
5263
|
+
"created_at",
|
|
5264
|
+
"updated_at",
|
|
5265
|
+
"diagnostic_id"
|
|
5266
|
+
],
|
|
5267
|
+
"properties": {
|
|
5268
|
+
"id": { "$ref": "#/$defs/OpaqueIdentifier" },
|
|
5269
|
+
"project_id": { "$ref": "#/$defs/OpaqueIdentifier" },
|
|
5270
|
+
"environment_id": { "$ref": "#/$defs/OpaqueIdentifier" },
|
|
5271
|
+
"environment": { "$ref": "#/$defs/Environment" },
|
|
5272
|
+
"provider": { "const": "slack" },
|
|
5273
|
+
"mode": { "const": "customer_managed" },
|
|
5274
|
+
"display_name": {
|
|
5275
|
+
"type": "string",
|
|
5276
|
+
"minLength": 1,
|
|
5277
|
+
"maxLength": 100
|
|
5278
|
+
},
|
|
5279
|
+
"status": { "$ref": "#/$defs/ChannelConnectionStatus" },
|
|
5280
|
+
"health": { "$ref": "#/$defs/ProviderHealthState" },
|
|
5281
|
+
"capabilities": {
|
|
5282
|
+
"type": "array",
|
|
5283
|
+
"maxItems": 1,
|
|
5284
|
+
"uniqueItems": true,
|
|
5285
|
+
"items": { "$ref": "#/$defs/HostedConnectionCapability" }
|
|
5286
|
+
},
|
|
5287
|
+
"identity": { "$ref": "#/$defs/SlackConnectionIdentity" },
|
|
5288
|
+
"setup": false,
|
|
5289
|
+
"authorization_setup": false,
|
|
5290
|
+
"app_setup": {
|
|
5291
|
+
"$ref": "#/$defs/CustomerOwnedSlackConnectionSetup",
|
|
5292
|
+
"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."
|
|
5293
|
+
},
|
|
5294
|
+
"installation": {
|
|
5295
|
+
"$ref": "#/$defs/ProviderInstallationSummary",
|
|
5296
|
+
"description": "The safe installation this exact connection owns. It exposes no provider-native identifier and no credential reference."
|
|
5297
|
+
},
|
|
5298
|
+
"management_url": {
|
|
5299
|
+
"type": "string",
|
|
5300
|
+
"format": "uri",
|
|
5301
|
+
"pattern": "^slack://app\\?[^\\s]+$",
|
|
5302
|
+
"maxLength": 2048,
|
|
5303
|
+
"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."
|
|
5304
|
+
},
|
|
5305
|
+
"recovery_action": {
|
|
5306
|
+
"$ref": "#/$defs/HostedConnectionRecoveryAction"
|
|
5307
|
+
},
|
|
5308
|
+
"last_successful_verification_at": { "$ref": "#/$defs/Timestamp" },
|
|
5309
|
+
"last_successful_delivery_at": { "$ref": "#/$defs/Timestamp" },
|
|
5310
|
+
"row_version": {
|
|
5311
|
+
"type": "integer",
|
|
5312
|
+
"minimum": 1,
|
|
5313
|
+
"description": "Send this back on an update. A stale value is refused rather than silently overwriting a change made in between."
|
|
5314
|
+
},
|
|
5315
|
+
"created_at": { "$ref": "#/$defs/Timestamp" },
|
|
5316
|
+
"updated_at": { "$ref": "#/$defs/Timestamp" },
|
|
5317
|
+
"archived_at": { "$ref": "#/$defs/Timestamp" },
|
|
5318
|
+
"health_checked_at": { "$ref": "#/$defs/Timestamp" },
|
|
5319
|
+
"last_transition_at": { "$ref": "#/$defs/Timestamp" },
|
|
5320
|
+
"last_transition_reason": {
|
|
5321
|
+
"type": "string",
|
|
5322
|
+
"minLength": 1,
|
|
5323
|
+
"maxLength": 200
|
|
5324
|
+
},
|
|
5325
|
+
"diagnostic_id": { "$ref": "#/$defs/DiagnosticId" }
|
|
5326
|
+
},
|
|
5327
|
+
"additionalProperties": false
|
|
5328
|
+
},
|
|
4908
5329
|
"ChannelConnection": {
|
|
4909
5330
|
"description": "One environment's provider-discriminated connection. Credentials and provider-native authority identifiers are structurally absent.",
|
|
4910
5331
|
"oneOf": [
|
|
4911
5332
|
{ "$ref": "#/$defs/TelegramChannelConnection" },
|
|
4912
|
-
{ "$ref": "#/$defs/HostedChannelConnection" }
|
|
5333
|
+
{ "$ref": "#/$defs/HostedChannelConnection" },
|
|
5334
|
+
{ "$ref": "#/$defs/CustomerOwnedSlackChannelConnection" }
|
|
4913
5335
|
]
|
|
4914
5336
|
},
|
|
4915
5337
|
"ChannelConnectionCollection": {
|
|
@@ -5036,6 +5458,53 @@
|
|
|
5036
5458
|
},
|
|
5037
5459
|
"additionalProperties": false
|
|
5038
5460
|
},
|
|
5461
|
+
{
|
|
5462
|
+
"type": "object",
|
|
5463
|
+
"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.",
|
|
5464
|
+
"required": [
|
|
5465
|
+
"mode",
|
|
5466
|
+
"provider",
|
|
5467
|
+
"display_name",
|
|
5468
|
+
"app_name",
|
|
5469
|
+
"bot_display_name",
|
|
5470
|
+
"app_description"
|
|
5471
|
+
],
|
|
5472
|
+
"properties": {
|
|
5473
|
+
"mode": {
|
|
5474
|
+
"type": "string",
|
|
5475
|
+
"const": "customer_managed"
|
|
5476
|
+
},
|
|
5477
|
+
"provider": {
|
|
5478
|
+
"type": "string",
|
|
5479
|
+
"const": "slack"
|
|
5480
|
+
},
|
|
5481
|
+
"display_name": {
|
|
5482
|
+
"type": "string",
|
|
5483
|
+
"minLength": 1,
|
|
5484
|
+
"maxLength": 100,
|
|
5485
|
+
"description": "What this connection is called inside WhooshBang. Unrelated to what Slack shows the workspace."
|
|
5486
|
+
},
|
|
5487
|
+
"app_name": {
|
|
5488
|
+
"type": "string",
|
|
5489
|
+
"minLength": 1,
|
|
5490
|
+
"maxLength": 35,
|
|
5491
|
+
"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."
|
|
5492
|
+
},
|
|
5493
|
+
"bot_display_name": {
|
|
5494
|
+
"type": "string",
|
|
5495
|
+
"minLength": 1,
|
|
5496
|
+
"maxLength": 80,
|
|
5497
|
+
"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."
|
|
5498
|
+
},
|
|
5499
|
+
"app_description": {
|
|
5500
|
+
"type": "string",
|
|
5501
|
+
"minLength": 1,
|
|
5502
|
+
"maxLength": 140,
|
|
5503
|
+
"description": "The short description Slack shows beside the app, within Slack's own 140-character limit."
|
|
5504
|
+
}
|
|
5505
|
+
},
|
|
5506
|
+
"additionalProperties": false
|
|
5507
|
+
},
|
|
5039
5508
|
{
|
|
5040
5509
|
"type": "object",
|
|
5041
5510
|
"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.",
|
|
@@ -5060,8 +5529,46 @@
|
|
|
5060
5529
|
]
|
|
5061
5530
|
},
|
|
5062
5531
|
"RotateChannelConnectionCredentialRequest": {
|
|
5063
|
-
"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.",
|
|
5532
|
+
"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.",
|
|
5064
5533
|
"oneOf": [
|
|
5534
|
+
{
|
|
5535
|
+
"type": "object",
|
|
5536
|
+
"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.",
|
|
5537
|
+
"required": [
|
|
5538
|
+
"provider",
|
|
5539
|
+
"client_identity",
|
|
5540
|
+
"client_secret",
|
|
5541
|
+
"signing_secret"
|
|
5542
|
+
],
|
|
5543
|
+
"properties": {
|
|
5544
|
+
"provider": {
|
|
5545
|
+
"type": "string",
|
|
5546
|
+
"const": "slack"
|
|
5547
|
+
},
|
|
5548
|
+
"client_identity": {
|
|
5549
|
+
"type": "string",
|
|
5550
|
+
"minLength": 7,
|
|
5551
|
+
"maxLength": 66,
|
|
5552
|
+
"pattern": "^[0-9]{3,32}\\.[0-9]{3,32}$",
|
|
5553
|
+
"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."
|
|
5554
|
+
},
|
|
5555
|
+
"client_secret": {
|
|
5556
|
+
"type": "string",
|
|
5557
|
+
"minLength": 1,
|
|
5558
|
+
"maxLength": 512,
|
|
5559
|
+
"writeOnly": true,
|
|
5560
|
+
"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."
|
|
5561
|
+
},
|
|
5562
|
+
"signing_secret": {
|
|
5563
|
+
"type": "string",
|
|
5564
|
+
"minLength": 1,
|
|
5565
|
+
"maxLength": 512,
|
|
5566
|
+
"writeOnly": true,
|
|
5567
|
+
"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."
|
|
5568
|
+
}
|
|
5569
|
+
},
|
|
5570
|
+
"additionalProperties": false
|
|
5571
|
+
},
|
|
5065
5572
|
{
|
|
5066
5573
|
"type": "object",
|
|
5067
5574
|
"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.",
|
|
@@ -5113,6 +5620,287 @@
|
|
|
5113
5620
|
},
|
|
5114
5621
|
"additionalProperties": false
|
|
5115
5622
|
},
|
|
5623
|
+
"ConnectionTestKind": {
|
|
5624
|
+
"type": "string",
|
|
5625
|
+
"enum": ["outbound_notification", "signed_interaction"],
|
|
5626
|
+
"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.",
|
|
5627
|
+
"$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."
|
|
5628
|
+
},
|
|
5629
|
+
"ConnectionTestStatus": {
|
|
5630
|
+
"type": "string",
|
|
5631
|
+
"enum": ["pending", "succeeded", "failed", "expired"],
|
|
5632
|
+
"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."
|
|
5633
|
+
},
|
|
5634
|
+
"ConnectionTestFailureCode": {
|
|
5635
|
+
"type": "string",
|
|
5636
|
+
"enum": [
|
|
5637
|
+
"setup_incomplete",
|
|
5638
|
+
"verification_target_unknown",
|
|
5639
|
+
"credential_unavailable",
|
|
5640
|
+
"provider_rejected",
|
|
5641
|
+
"provider_unavailable",
|
|
5642
|
+
"provider_rate_limited",
|
|
5643
|
+
"unverified_request_received"
|
|
5644
|
+
],
|
|
5645
|
+
"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."
|
|
5646
|
+
},
|
|
5647
|
+
"ConnectionTestRecoveryAction": {
|
|
5648
|
+
"type": "string",
|
|
5649
|
+
"enum": [
|
|
5650
|
+
"none",
|
|
5651
|
+
"complete_setup",
|
|
5652
|
+
"replace_credentials",
|
|
5653
|
+
"reinstall",
|
|
5654
|
+
"retry_later",
|
|
5655
|
+
"check_request_url",
|
|
5656
|
+
"contact_support"
|
|
5657
|
+
],
|
|
5658
|
+
"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."
|
|
5659
|
+
},
|
|
5660
|
+
"ConnectionTestEvidence": {
|
|
5661
|
+
"type": "object",
|
|
5662
|
+
"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.",
|
|
5663
|
+
"properties": {
|
|
5664
|
+
"identity_handle": {
|
|
5665
|
+
"type": "string",
|
|
5666
|
+
"minLength": 1,
|
|
5667
|
+
"maxLength": 128,
|
|
5668
|
+
"description": "The visible handle the test went out as — the same identity a subscriber would see."
|
|
5669
|
+
},
|
|
5670
|
+
"workspace_display_name": {
|
|
5671
|
+
"type": "string",
|
|
5672
|
+
"minLength": 1,
|
|
5673
|
+
"maxLength": 100,
|
|
5674
|
+
"description": "The provider workspace the test ran in, as its own members see it named."
|
|
5675
|
+
},
|
|
5676
|
+
"accepted_at": {
|
|
5677
|
+
"$ref": "#/$defs/Timestamp",
|
|
5678
|
+
"description": "When the provider accepted the outbound message. Acceptance is the provider's own answer, not our belief."
|
|
5679
|
+
},
|
|
5680
|
+
"observed_at": {
|
|
5681
|
+
"$ref": "#/$defs/Timestamp",
|
|
5682
|
+
"description": "When the signed request the test was waiting for arrived and verified."
|
|
5683
|
+
},
|
|
5684
|
+
"round_trip_ms": {
|
|
5685
|
+
"type": "integer",
|
|
5686
|
+
"minimum": 0,
|
|
5687
|
+
"maximum": 86400000,
|
|
5688
|
+
"description": "Milliseconds from placing the test to observing its answer."
|
|
5689
|
+
},
|
|
5690
|
+
"signature_verified": {
|
|
5691
|
+
"type": "boolean",
|
|
5692
|
+
"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."
|
|
5693
|
+
},
|
|
5694
|
+
"ingress_host": {
|
|
5695
|
+
"type": "string",
|
|
5696
|
+
"minLength": 1,
|
|
5697
|
+
"maxLength": 253,
|
|
5698
|
+
"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."
|
|
5699
|
+
}
|
|
5700
|
+
},
|
|
5701
|
+
"additionalProperties": false
|
|
5702
|
+
},
|
|
5703
|
+
"ConnectionTest": {
|
|
5704
|
+
"type": "object",
|
|
5705
|
+
"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.",
|
|
5706
|
+
"required": [
|
|
5707
|
+
"id",
|
|
5708
|
+
"connection_id",
|
|
5709
|
+
"kind",
|
|
5710
|
+
"status",
|
|
5711
|
+
"requested_at",
|
|
5712
|
+
"expires_at",
|
|
5713
|
+
"recovery_action",
|
|
5714
|
+
"diagnostic_id"
|
|
5715
|
+
],
|
|
5716
|
+
"properties": {
|
|
5717
|
+
"id": {
|
|
5718
|
+
"$ref": "#/$defs/OpaqueIdentifier"
|
|
5719
|
+
},
|
|
5720
|
+
"connection_id": {
|
|
5721
|
+
"$ref": "#/$defs/OpaqueIdentifier"
|
|
5722
|
+
},
|
|
5723
|
+
"kind": {
|
|
5724
|
+
"$ref": "#/$defs/ConnectionTestKind"
|
|
5725
|
+
},
|
|
5726
|
+
"status": {
|
|
5727
|
+
"$ref": "#/$defs/ConnectionTestStatus"
|
|
5728
|
+
},
|
|
5729
|
+
"requested_at": {
|
|
5730
|
+
"$ref": "#/$defs/Timestamp"
|
|
5731
|
+
},
|
|
5732
|
+
"completed_at": {
|
|
5733
|
+
"$ref": "#/$defs/Timestamp",
|
|
5734
|
+
"description": "When the test settled. Absent while it is still pending."
|
|
5735
|
+
},
|
|
5736
|
+
"expires_at": {
|
|
5737
|
+
"$ref": "#/$defs/Timestamp",
|
|
5738
|
+
"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."
|
|
5739
|
+
},
|
|
5740
|
+
"evidence": {
|
|
5741
|
+
"$ref": "#/$defs/ConnectionTestEvidence"
|
|
5742
|
+
},
|
|
5743
|
+
"failure_code": {
|
|
5744
|
+
"$ref": "#/$defs/ConnectionTestFailureCode"
|
|
5745
|
+
},
|
|
5746
|
+
"recovery_action": {
|
|
5747
|
+
"$ref": "#/$defs/ConnectionTestRecoveryAction",
|
|
5748
|
+
"description": "The exact safe next action for this result. `none` on a pass."
|
|
5749
|
+
},
|
|
5750
|
+
"diagnostic_id": {
|
|
5751
|
+
"$ref": "#/$defs/DiagnosticId"
|
|
5752
|
+
}
|
|
5753
|
+
},
|
|
5754
|
+
"additionalProperties": false
|
|
5755
|
+
},
|
|
5756
|
+
"ConnectionTestCollection": {
|
|
5757
|
+
"type": "object",
|
|
5758
|
+
"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.",
|
|
5759
|
+
"required": ["items"],
|
|
5760
|
+
"properties": {
|
|
5761
|
+
"items": {
|
|
5762
|
+
"type": "array",
|
|
5763
|
+
"maxItems": 100,
|
|
5764
|
+
"items": {
|
|
5765
|
+
"$ref": "#/$defs/ConnectionTest"
|
|
5766
|
+
}
|
|
5767
|
+
},
|
|
5768
|
+
"next_cursor": {
|
|
5769
|
+
"type": "string",
|
|
5770
|
+
"minLength": 1,
|
|
5771
|
+
"maxLength": 256,
|
|
5772
|
+
"pattern": "^[A-Za-z0-9_-]+$"
|
|
5773
|
+
},
|
|
5774
|
+
"page": {
|
|
5775
|
+
"$ref": "#/$defs/ListPageNumber"
|
|
5776
|
+
},
|
|
5777
|
+
"page_size": {
|
|
5778
|
+
"$ref": "#/$defs/ListPageSize"
|
|
5779
|
+
},
|
|
5780
|
+
"total": {
|
|
5781
|
+
"$ref": "#/$defs/ListTotal"
|
|
5782
|
+
}
|
|
5783
|
+
},
|
|
5784
|
+
"additionalProperties": false
|
|
5785
|
+
},
|
|
5786
|
+
"CreateConnectionTestRequest": {
|
|
5787
|
+
"type": "object",
|
|
5788
|
+
"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.",
|
|
5789
|
+
"required": ["kind"],
|
|
5790
|
+
"properties": {
|
|
5791
|
+
"kind": {
|
|
5792
|
+
"$ref": "#/$defs/ConnectionTestKind"
|
|
5793
|
+
}
|
|
5794
|
+
},
|
|
5795
|
+
"additionalProperties": false
|
|
5796
|
+
},
|
|
5797
|
+
"CustomerOwnedSlackAppManifest": {
|
|
5798
|
+
"type": "object",
|
|
5799
|
+
"description": "The exact configuration a customer-owned Slack app is created from, as data rather than as a URL-encoded blob.\n\nIt exists so the owner can read what they are about to approve, and so a reviewer can check it, without opening Slack. It carries configuration only: no account, project, or environment, no credential, and no provider-native identifier. The opaque handle inside the request URLs selects candidate setup state and authorizes nothing — a request still has to carry Slack's signature over the untouched body before anything inside it is believed.",
|
|
5800
|
+
"required": [
|
|
5801
|
+
"creation_url",
|
|
5802
|
+
"app_name",
|
|
5803
|
+
"bot_display_name",
|
|
5804
|
+
"description",
|
|
5805
|
+
"bot_scopes",
|
|
5806
|
+
"user_scopes",
|
|
5807
|
+
"request_urls",
|
|
5808
|
+
"manifest",
|
|
5809
|
+
"diagnostic_id"
|
|
5810
|
+
],
|
|
5811
|
+
"properties": {
|
|
5812
|
+
"creation_url": {
|
|
5813
|
+
"type": "string",
|
|
5814
|
+
"format": "uri",
|
|
5815
|
+
"pattern": "^https://api\\.slack\\.com/apps\\?[^\\s]+$",
|
|
5816
|
+
"maxLength": 8192,
|
|
5817
|
+
"description": "The same prefilled creation link the setup carries, repeated here so one read answers both questions."
|
|
5818
|
+
},
|
|
5819
|
+
"app_name": {
|
|
5820
|
+
"type": "string",
|
|
5821
|
+
"minLength": 1,
|
|
5822
|
+
"maxLength": 35
|
|
5823
|
+
},
|
|
5824
|
+
"bot_display_name": {
|
|
5825
|
+
"type": "string",
|
|
5826
|
+
"minLength": 1,
|
|
5827
|
+
"maxLength": 80
|
|
5828
|
+
},
|
|
5829
|
+
"description": {
|
|
5830
|
+
"type": "string",
|
|
5831
|
+
"minLength": 1,
|
|
5832
|
+
"maxLength": 140
|
|
5833
|
+
},
|
|
5834
|
+
"bot_scopes": {
|
|
5835
|
+
"type": "array",
|
|
5836
|
+
"maxItems": 20,
|
|
5837
|
+
"uniqueItems": true,
|
|
5838
|
+
"items": {
|
|
5839
|
+
"type": "string",
|
|
5840
|
+
"minLength": 1,
|
|
5841
|
+
"maxLength": 64
|
|
5842
|
+
},
|
|
5843
|
+
"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."
|
|
5844
|
+
},
|
|
5845
|
+
"user_scopes": {
|
|
5846
|
+
"type": "array",
|
|
5847
|
+
"maxItems": 20,
|
|
5848
|
+
"uniqueItems": true,
|
|
5849
|
+
"items": {
|
|
5850
|
+
"type": "string",
|
|
5851
|
+
"minLength": 1,
|
|
5852
|
+
"maxLength": 64
|
|
5853
|
+
},
|
|
5854
|
+
"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."
|
|
5855
|
+
},
|
|
5856
|
+
"request_urls": {
|
|
5857
|
+
"type": "object",
|
|
5858
|
+
"description": "Where this deployment answers the app. A stale or edited value here is the failure a signed-interaction test detects.",
|
|
5859
|
+
"required": [
|
|
5860
|
+
"events",
|
|
5861
|
+
"interactions",
|
|
5862
|
+
"installation_redirect",
|
|
5863
|
+
"recipient_redirect"
|
|
5864
|
+
],
|
|
5865
|
+
"properties": {
|
|
5866
|
+
"events": {
|
|
5867
|
+
"type": "string",
|
|
5868
|
+
"format": "uri",
|
|
5869
|
+
"pattern": "^https://[^\\s]+$",
|
|
5870
|
+
"maxLength": 2048
|
|
5871
|
+
},
|
|
5872
|
+
"interactions": {
|
|
5873
|
+
"type": "string",
|
|
5874
|
+
"format": "uri",
|
|
5875
|
+
"pattern": "^https://[^\\s]+$",
|
|
5876
|
+
"maxLength": 2048
|
|
5877
|
+
},
|
|
5878
|
+
"installation_redirect": {
|
|
5879
|
+
"type": "string",
|
|
5880
|
+
"format": "uri",
|
|
5881
|
+
"pattern": "^https://[^\\s]+$",
|
|
5882
|
+
"maxLength": 2048
|
|
5883
|
+
},
|
|
5884
|
+
"recipient_redirect": {
|
|
5885
|
+
"type": "string",
|
|
5886
|
+
"format": "uri",
|
|
5887
|
+
"pattern": "^https://[^\\s]+$",
|
|
5888
|
+
"maxLength": 2048
|
|
5889
|
+
}
|
|
5890
|
+
},
|
|
5891
|
+
"additionalProperties": false
|
|
5892
|
+
},
|
|
5893
|
+
"manifest": {
|
|
5894
|
+
"type": "object",
|
|
5895
|
+
"description": "The literal manifest document Slack is handed. Reproduced whole so what the owner reviews and what Slack receives cannot drift apart.",
|
|
5896
|
+
"additionalProperties": true
|
|
5897
|
+
},
|
|
5898
|
+
"diagnostic_id": {
|
|
5899
|
+
"$ref": "#/$defs/DiagnosticId"
|
|
5900
|
+
}
|
|
5901
|
+
},
|
|
5902
|
+
"additionalProperties": false
|
|
5903
|
+
},
|
|
5116
5904
|
"NotifierConnectionRoute": {
|
|
5117
5905
|
"type": "object",
|
|
5118
5906
|
"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.",
|