@whooshbang/contracts 1.0.0-rc.19 → 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 +44 -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 +25421 -15666
- package/dist/generated/validators-standalone.js.map +1 -1
- package/dist/generated/validators.d.ts +11 -1
- package/dist/generated/validators.js +132 -31
- package/dist/generated/validators.js.map +1 -1
- package/dist/generated/whooshbang-v1.d.ts +836 -9
- 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 +1623 -99
- package/openapi/whooshbang-v1.yaml +687 -14
- 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 +798 -8
|
@@ -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
|
},
|
|
@@ -719,6 +749,8 @@
|
|
|
719
749
|
"ApiCredentialScope": {
|
|
720
750
|
"type": "string",
|
|
721
751
|
"enum": [
|
|
752
|
+
"projects:write",
|
|
753
|
+
"projects:read",
|
|
722
754
|
"messages:write",
|
|
723
755
|
"messages:read",
|
|
724
756
|
"subscriptions:write",
|
|
@@ -729,12 +761,12 @@
|
|
|
729
761
|
"connections:write",
|
|
730
762
|
"connections:read"
|
|
731
763
|
],
|
|
732
|
-
"description": "One project-credential capability. Environment authority always comes from the credential record, never a scope or request field."
|
|
764
|
+
"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 account 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."
|
|
733
765
|
},
|
|
734
766
|
"ApiCredentialScopeSet": {
|
|
735
767
|
"type": "array",
|
|
736
768
|
"minItems": 1,
|
|
737
|
-
"maxItems":
|
|
769
|
+
"maxItems": 11,
|
|
738
770
|
"uniqueItems": true,
|
|
739
771
|
"items": {
|
|
740
772
|
"$ref": "#/$defs/ApiCredentialScope"
|
|
@@ -914,6 +946,210 @@
|
|
|
914
946
|
},
|
|
915
947
|
"additionalProperties": false
|
|
916
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
|
+
},
|
|
917
1153
|
"BindingSummary": {
|
|
918
1154
|
"type": "object",
|
|
919
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.",
|
|
@@ -968,8 +1204,8 @@
|
|
|
968
1204
|
"format": "uri",
|
|
969
1205
|
"minLength": 8,
|
|
970
1206
|
"maxLength": 255,
|
|
971
|
-
"pattern": "^https?://[^/?#*\\s]+(?::[0-9]{1,5})
|
|
972
|
-
"description": "One exact scheme, host, and optional port. Paths, queries, fragments, credentials, and wildcards are forbidden; HTTP is accepted only for localhost or synthetic .test configuration."
|
|
1207
|
+
"pattern": "^https?://[^/?#*\\s]+(?::[0-9]{1,5})?/?$",
|
|
1208
|
+
"description": "One exact scheme, host, and optional port, in any spelling that means that same origin. A trailing slash, an uppercase host, and an explicitly written default port are accepted and stored in canonical form. Paths, queries, fragments, credentials, and wildcards are forbidden; HTTP is accepted only for localhost or synthetic .test configuration."
|
|
973
1209
|
},
|
|
974
1210
|
"RegisteredReturnUrl": {
|
|
975
1211
|
"type": "string",
|
|
@@ -3609,6 +3845,7 @@
|
|
|
3609
3845
|
"resource_not_found",
|
|
3610
3846
|
"resource_expired",
|
|
3611
3847
|
"cancellation_too_late",
|
|
3848
|
+
"credential_account_mismatch",
|
|
3612
3849
|
"endpoint_rotation_conflict",
|
|
3613
3850
|
"rate_limited",
|
|
3614
3851
|
"provider_retryable",
|
|
@@ -4316,6 +4553,26 @@
|
|
|
4316
4553
|
}
|
|
4317
4554
|
}
|
|
4318
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
|
+
},
|
|
4319
4576
|
{
|
|
4320
4577
|
"type": "object",
|
|
4321
4578
|
"properties": {
|
|
@@ -4562,7 +4819,12 @@
|
|
|
4562
4819
|
},
|
|
4563
4820
|
"setup_modes": {
|
|
4564
4821
|
"type": "array",
|
|
4565
|
-
"
|
|
4822
|
+
"minItems": 1,
|
|
4823
|
+
"maxItems": 2,
|
|
4824
|
+
"uniqueItems": true,
|
|
4825
|
+
"items": {
|
|
4826
|
+
"enum": ["whooshbang_hosted", "customer_managed"]
|
|
4827
|
+
}
|
|
4566
4828
|
},
|
|
4567
4829
|
"lifecycle_operations": {
|
|
4568
4830
|
"type": "array",
|
|
@@ -4683,6 +4945,83 @@
|
|
|
4683
4945
|
}
|
|
4684
4946
|
]
|
|
4685
4947
|
},
|
|
4948
|
+
"CustomerOwnedSlackSetupStatus": {
|
|
4949
|
+
"type": "string",
|
|
4950
|
+
"enum": [
|
|
4951
|
+
"awaiting_app_creation",
|
|
4952
|
+
"awaiting_authorities",
|
|
4953
|
+
"awaiting_authorization",
|
|
4954
|
+
"awaiting_ingress_proof",
|
|
4955
|
+
"provisioning",
|
|
4956
|
+
"failed",
|
|
4957
|
+
"expired"
|
|
4958
|
+
],
|
|
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."
|
|
4960
|
+
},
|
|
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": {
|
|
4967
|
+
"type": "object",
|
|
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
|
+
],
|
|
4977
|
+
"properties": {
|
|
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": {
|
|
4998
|
+
"type": "string",
|
|
4999
|
+
"minLength": 1,
|
|
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."
|
|
5002
|
+
},
|
|
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."
|
|
5008
|
+
},
|
|
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
|
+
},
|
|
4686
5025
|
"ProviderInstallationStatus": {
|
|
4687
5026
|
"type": "string",
|
|
4688
5027
|
"enum": [
|
|
@@ -4798,6 +5137,7 @@
|
|
|
4798
5137
|
"identity": { "$ref": "#/$defs/TelegramConnectionIdentity" },
|
|
4799
5138
|
"setup": { "$ref": "#/$defs/ChannelConnectionSetup" },
|
|
4800
5139
|
"authorization_setup": false,
|
|
5140
|
+
"app_setup": false,
|
|
4801
5141
|
"installation": false,
|
|
4802
5142
|
"row_version": {
|
|
4803
5143
|
"type": "integer",
|
|
@@ -4860,6 +5200,7 @@
|
|
|
4860
5200
|
},
|
|
4861
5201
|
"identity": { "$ref": "#/$defs/SlackConnectionIdentity" },
|
|
4862
5202
|
"setup": false,
|
|
5203
|
+
"app_setup": false,
|
|
4863
5204
|
"authorization_setup": {
|
|
4864
5205
|
"$ref": "#/$defs/HostedChannelConnectionSetup",
|
|
4865
5206
|
"description": "The additive hosted authorization state. Older tolerant readers discard this unknown field rather than interpreting it as Telegram setup."
|
|
@@ -4903,11 +5244,94 @@
|
|
|
4903
5244
|
},
|
|
4904
5245
|
"additionalProperties": false
|
|
4905
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
|
+
},
|
|
4906
5329
|
"ChannelConnection": {
|
|
4907
5330
|
"description": "One environment's provider-discriminated connection. Credentials and provider-native authority identifiers are structurally absent.",
|
|
4908
5331
|
"oneOf": [
|
|
4909
5332
|
{ "$ref": "#/$defs/TelegramChannelConnection" },
|
|
4910
|
-
{ "$ref": "#/$defs/HostedChannelConnection" }
|
|
5333
|
+
{ "$ref": "#/$defs/HostedChannelConnection" },
|
|
5334
|
+
{ "$ref": "#/$defs/CustomerOwnedSlackChannelConnection" }
|
|
4911
5335
|
]
|
|
4912
5336
|
},
|
|
4913
5337
|
"ChannelConnectionCollection": {
|
|
@@ -5034,6 +5458,53 @@
|
|
|
5034
5458
|
},
|
|
5035
5459
|
"additionalProperties": false
|
|
5036
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
|
+
},
|
|
5037
5508
|
{
|
|
5038
5509
|
"type": "object",
|
|
5039
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.",
|
|
@@ -5058,8 +5529,46 @@
|
|
|
5058
5529
|
]
|
|
5059
5530
|
},
|
|
5060
5531
|
"RotateChannelConnectionCredentialRequest": {
|
|
5061
|
-
"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.",
|
|
5062
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
|
+
},
|
|
5063
5572
|
{
|
|
5064
5573
|
"type": "object",
|
|
5065
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.",
|
|
@@ -5111,6 +5620,287 @@
|
|
|
5111
5620
|
},
|
|
5112
5621
|
"additionalProperties": false
|
|
5113
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
|
+
},
|
|
5114
5904
|
"NotifierConnectionRoute": {
|
|
5115
5905
|
"type": "object",
|
|
5116
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.",
|