@shipeasy/openapi 3.1.0 → 3.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-UV333BIK.js → chunk-DW33MG4B.js} +147 -2
- package/dist/chunk-DW33MG4B.js.map +1 -0
- package/dist/{chunk-V4ZESAIF.js → chunk-RTN572TI.js} +67 -1
- package/dist/chunk-RTN572TI.js.map +1 -0
- package/dist/{client-yqGYju-M.d.ts → client-BPVn4hMC.d.ts} +687 -3
- package/dist/client.d.ts +1 -1
- package/dist/client.js +15 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +16 -2
- package/dist/index.js.map +1 -1
- package/dist/schemas.d.ts +376 -1
- package/dist/schemas.js +67 -1
- package/openapi-sdk.json +1747 -0
- package/openapi.json +1485 -178
- package/openapi.yaml +922 -2
- package/package.json +6 -4
- package/spec/components/responses.yaml +39 -0
- package/spec/components/schemas/gates.yaml +87 -0
- package/spec/components/schemas/keys.yaml +2 -2
- package/spec/components/schemas/killswitches.yaml +64 -0
- package/spec/components/schemas/ops.yaml +185 -0
- package/spec/openapi-sdk.yaml +86 -0
- package/spec/openapi.yaml +18 -0
- package/spec/paths/gates.yaml +282 -0
- package/spec/paths/killswitches.yaml +96 -0
- package/spec/paths/ops.yaml +179 -0
- package/dist/chunk-UV333BIK.js.map +0 -1
- package/dist/chunk-V4ZESAIF.js.map +0 -1
package/openapi.json
CHANGED
|
@@ -1001,6 +1001,393 @@
|
|
|
1001
1001
|
}
|
|
1002
1002
|
}
|
|
1003
1003
|
},
|
|
1004
|
+
"/api/admin/gates/{id}/whitelist": {
|
|
1005
|
+
"get": {
|
|
1006
|
+
"operationId": "getGateWhitelist",
|
|
1007
|
+
"summary": "Read a gate's whitelist",
|
|
1008
|
+
"description": "Returns the gate's whitelist — the always-first allowlist that admits the listed identities before any targeting rule or percentage rollout is evaluated.\n\nA gate with no whitelist returns `entries: []` (and the default `attr`), never a 404 — so a caller can read-then-write without special-casing the empty gate.\n\n**Use case:** Check whether an account is already let through before adding it.",
|
|
1009
|
+
"tags": [
|
|
1010
|
+
"Flags"
|
|
1011
|
+
],
|
|
1012
|
+
"parameters": [
|
|
1013
|
+
{
|
|
1014
|
+
"$ref": "#/components/parameters/ProjectId"
|
|
1015
|
+
},
|
|
1016
|
+
{
|
|
1017
|
+
"name": "id",
|
|
1018
|
+
"in": "path",
|
|
1019
|
+
"required": true,
|
|
1020
|
+
"description": "Stable opaque gate id (`gate_…`) or the gate's `name`.",
|
|
1021
|
+
"schema": {
|
|
1022
|
+
"$ref": "#/components/schemas/ResourceId"
|
|
1023
|
+
}
|
|
1024
|
+
}
|
|
1025
|
+
],
|
|
1026
|
+
"responses": {
|
|
1027
|
+
"200": {
|
|
1028
|
+
"description": "Read a gate's whitelist",
|
|
1029
|
+
"content": {
|
|
1030
|
+
"application/json": {
|
|
1031
|
+
"schema": {
|
|
1032
|
+
"$ref": "#/components/schemas/GateWhitelist"
|
|
1033
|
+
},
|
|
1034
|
+
"example": {
|
|
1035
|
+
"id": "gate_01j7w8a1b2c3d4e5f6g7h8i9j0",
|
|
1036
|
+
"name": "new_checkout",
|
|
1037
|
+
"attr": "email",
|
|
1038
|
+
"entries": [
|
|
1039
|
+
"alice@acme.dev",
|
|
1040
|
+
"bob@acme.dev"
|
|
1041
|
+
]
|
|
1042
|
+
}
|
|
1043
|
+
}
|
|
1044
|
+
}
|
|
1045
|
+
},
|
|
1046
|
+
"400": {
|
|
1047
|
+
"$ref": "#/components/responses/BadRequest"
|
|
1048
|
+
},
|
|
1049
|
+
"401": {
|
|
1050
|
+
"$ref": "#/components/responses/Unauthorized"
|
|
1051
|
+
},
|
|
1052
|
+
"403": {
|
|
1053
|
+
"$ref": "#/components/responses/Forbidden"
|
|
1054
|
+
},
|
|
1055
|
+
"404": {
|
|
1056
|
+
"$ref": "#/components/responses/NotFound"
|
|
1057
|
+
},
|
|
1058
|
+
"409": {
|
|
1059
|
+
"$ref": "#/components/responses/Conflict"
|
|
1060
|
+
},
|
|
1061
|
+
"422": {
|
|
1062
|
+
"$ref": "#/components/responses/UnprocessableEntity"
|
|
1063
|
+
}
|
|
1064
|
+
},
|
|
1065
|
+
"x-error-codes": [
|
|
1066
|
+
"BAD_REQUEST",
|
|
1067
|
+
"UNAUTHORIZED",
|
|
1068
|
+
"FORBIDDEN",
|
|
1069
|
+
"NOT_FOUND"
|
|
1070
|
+
],
|
|
1071
|
+
"x-cli": {
|
|
1072
|
+
"name": "whitelist",
|
|
1073
|
+
"positional": [
|
|
1074
|
+
"id"
|
|
1075
|
+
]
|
|
1076
|
+
}
|
|
1077
|
+
},
|
|
1078
|
+
"put": {
|
|
1079
|
+
"operationId": "setGateWhitelist",
|
|
1080
|
+
"summary": "Replace a gate's whitelist",
|
|
1081
|
+
"description": "Replaces the gate's whole whitelist with `entries`, creating the block if the gate didn't have one. Idempotent — the same call twice leaves the same list.\n\nThis is the only whitelist call that can switch `attr` (`email` ⇄ `user_id`) or clear the block: `entries: []` removes the whitelist from the gate entirely.\n\n**Use cases**\n\n- **Pin an exact list** — `{ \"entries\": [\"alice@acme.dev\", \"bob@acme.dev\"] }`.\n- **Switch to user ids** — `{ \"attr\": \"user_id\", \"entries\": [\"usr_123\"] }`.\n- **Drop the whitelist** — `{ \"entries\": [] }`.",
|
|
1082
|
+
"tags": [
|
|
1083
|
+
"Flags"
|
|
1084
|
+
],
|
|
1085
|
+
"parameters": [
|
|
1086
|
+
{
|
|
1087
|
+
"$ref": "#/components/parameters/ProjectId"
|
|
1088
|
+
},
|
|
1089
|
+
{
|
|
1090
|
+
"name": "id",
|
|
1091
|
+
"in": "path",
|
|
1092
|
+
"required": true,
|
|
1093
|
+
"description": "Stable opaque gate id (`gate_…`) or the gate's `name`.",
|
|
1094
|
+
"schema": {
|
|
1095
|
+
"$ref": "#/components/schemas/ResourceId"
|
|
1096
|
+
}
|
|
1097
|
+
}
|
|
1098
|
+
],
|
|
1099
|
+
"requestBody": {
|
|
1100
|
+
"required": true,
|
|
1101
|
+
"content": {
|
|
1102
|
+
"application/json": {
|
|
1103
|
+
"schema": {
|
|
1104
|
+
"$ref": "#/components/schemas/SetGateWhitelistRequest"
|
|
1105
|
+
},
|
|
1106
|
+
"examples": {
|
|
1107
|
+
"pinList": {
|
|
1108
|
+
"summary": "Pin an exact list of emails",
|
|
1109
|
+
"value": {
|
|
1110
|
+
"entries": [
|
|
1111
|
+
"alice@acme.dev",
|
|
1112
|
+
"bob@acme.dev"
|
|
1113
|
+
]
|
|
1114
|
+
}
|
|
1115
|
+
},
|
|
1116
|
+
"switchToUserIds": {
|
|
1117
|
+
"summary": "Re-key the whitelist onto user ids",
|
|
1118
|
+
"description": "Switching `attr` replaces the entries wholesale — the old emails are not translated.",
|
|
1119
|
+
"value": {
|
|
1120
|
+
"attr": "user_id",
|
|
1121
|
+
"entries": [
|
|
1122
|
+
"usr_01j7w8a1b2c3d4e5f6g7h8i9j0"
|
|
1123
|
+
]
|
|
1124
|
+
}
|
|
1125
|
+
},
|
|
1126
|
+
"clear": {
|
|
1127
|
+
"summary": "Remove the whitelist",
|
|
1128
|
+
"description": "An empty list drops the pinned block from the gate's stack.",
|
|
1129
|
+
"value": {
|
|
1130
|
+
"entries": []
|
|
1131
|
+
}
|
|
1132
|
+
}
|
|
1133
|
+
}
|
|
1134
|
+
}
|
|
1135
|
+
}
|
|
1136
|
+
},
|
|
1137
|
+
"responses": {
|
|
1138
|
+
"200": {
|
|
1139
|
+
"description": "Replace a gate's whitelist",
|
|
1140
|
+
"content": {
|
|
1141
|
+
"application/json": {
|
|
1142
|
+
"schema": {
|
|
1143
|
+
"$ref": "#/components/schemas/GateWhitelist"
|
|
1144
|
+
},
|
|
1145
|
+
"example": {
|
|
1146
|
+
"id": "gate_01j7w8a1b2c3d4e5f6g7h8i9j0",
|
|
1147
|
+
"name": "new_checkout",
|
|
1148
|
+
"attr": "email",
|
|
1149
|
+
"entries": [
|
|
1150
|
+
"alice@acme.dev",
|
|
1151
|
+
"bob@acme.dev"
|
|
1152
|
+
]
|
|
1153
|
+
}
|
|
1154
|
+
}
|
|
1155
|
+
}
|
|
1156
|
+
},
|
|
1157
|
+
"400": {
|
|
1158
|
+
"$ref": "#/components/responses/BadRequest"
|
|
1159
|
+
},
|
|
1160
|
+
"401": {
|
|
1161
|
+
"$ref": "#/components/responses/Unauthorized"
|
|
1162
|
+
},
|
|
1163
|
+
"403": {
|
|
1164
|
+
"$ref": "#/components/responses/Forbidden"
|
|
1165
|
+
},
|
|
1166
|
+
"404": {
|
|
1167
|
+
"$ref": "#/components/responses/NotFound"
|
|
1168
|
+
},
|
|
1169
|
+
"409": {
|
|
1170
|
+
"$ref": "#/components/responses/Conflict"
|
|
1171
|
+
},
|
|
1172
|
+
"422": {
|
|
1173
|
+
"$ref": "#/components/responses/UnprocessableEntity"
|
|
1174
|
+
}
|
|
1175
|
+
},
|
|
1176
|
+
"x-error-codes": [
|
|
1177
|
+
"BAD_REQUEST",
|
|
1178
|
+
"UNAUTHORIZED",
|
|
1179
|
+
"FORBIDDEN",
|
|
1180
|
+
"NOT_FOUND",
|
|
1181
|
+
"VALIDATION"
|
|
1182
|
+
],
|
|
1183
|
+
"x-cli": {
|
|
1184
|
+
"name": "whitelist-set",
|
|
1185
|
+
"positional": [
|
|
1186
|
+
"id"
|
|
1187
|
+
]
|
|
1188
|
+
}
|
|
1189
|
+
},
|
|
1190
|
+
"post": {
|
|
1191
|
+
"operationId": "addToGateWhitelist",
|
|
1192
|
+
"summary": "Add entries to a gate's whitelist",
|
|
1193
|
+
"description": "Adds identities to the gate's whitelist, creating the block if the gate doesn't have one yet. Entries already on the list are skipped, so the call is idempotent and safe to retry.\n\nAdding to a gate that already has a whitelist keyed on the other attribute is rejected (409) rather than silently re-keying the entries already there — use `PUT` to switch `attr` deliberately.\n\n**Use case:** Let one more customer into a private beta without reading the current list first.",
|
|
1194
|
+
"tags": [
|
|
1195
|
+
"Flags"
|
|
1196
|
+
],
|
|
1197
|
+
"parameters": [
|
|
1198
|
+
{
|
|
1199
|
+
"$ref": "#/components/parameters/ProjectId"
|
|
1200
|
+
},
|
|
1201
|
+
{
|
|
1202
|
+
"name": "id",
|
|
1203
|
+
"in": "path",
|
|
1204
|
+
"required": true,
|
|
1205
|
+
"description": "Stable opaque gate id (`gate_…`) or the gate's `name`.",
|
|
1206
|
+
"schema": {
|
|
1207
|
+
"$ref": "#/components/schemas/ResourceId"
|
|
1208
|
+
}
|
|
1209
|
+
}
|
|
1210
|
+
],
|
|
1211
|
+
"requestBody": {
|
|
1212
|
+
"required": true,
|
|
1213
|
+
"content": {
|
|
1214
|
+
"application/json": {
|
|
1215
|
+
"schema": {
|
|
1216
|
+
"$ref": "#/components/schemas/AddToGateWhitelistRequest"
|
|
1217
|
+
},
|
|
1218
|
+
"examples": {
|
|
1219
|
+
"addOne": {
|
|
1220
|
+
"summary": "Admit one more email",
|
|
1221
|
+
"value": {
|
|
1222
|
+
"entries": [
|
|
1223
|
+
"carol@acme.dev"
|
|
1224
|
+
]
|
|
1225
|
+
}
|
|
1226
|
+
},
|
|
1227
|
+
"createOnUserIds": {
|
|
1228
|
+
"summary": "Start a whitelist keyed on user ids",
|
|
1229
|
+
"description": "`attr` is honoured only because this gate has no whitelist yet.",
|
|
1230
|
+
"value": {
|
|
1231
|
+
"attr": "user_id",
|
|
1232
|
+
"entries": [
|
|
1233
|
+
"usr_01j7w8a1b2c3d4e5f6g7h8i9j0"
|
|
1234
|
+
]
|
|
1235
|
+
}
|
|
1236
|
+
}
|
|
1237
|
+
}
|
|
1238
|
+
}
|
|
1239
|
+
}
|
|
1240
|
+
},
|
|
1241
|
+
"responses": {
|
|
1242
|
+
"200": {
|
|
1243
|
+
"description": "Add entries to a gate's whitelist",
|
|
1244
|
+
"content": {
|
|
1245
|
+
"application/json": {
|
|
1246
|
+
"schema": {
|
|
1247
|
+
"$ref": "#/components/schemas/GateWhitelist"
|
|
1248
|
+
},
|
|
1249
|
+
"example": {
|
|
1250
|
+
"id": "gate_01j7w8a1b2c3d4e5f6g7h8i9j0",
|
|
1251
|
+
"name": "new_checkout",
|
|
1252
|
+
"attr": "email",
|
|
1253
|
+
"entries": [
|
|
1254
|
+
"alice@acme.dev",
|
|
1255
|
+
"bob@acme.dev",
|
|
1256
|
+
"carol@acme.dev"
|
|
1257
|
+
]
|
|
1258
|
+
}
|
|
1259
|
+
}
|
|
1260
|
+
}
|
|
1261
|
+
},
|
|
1262
|
+
"400": {
|
|
1263
|
+
"$ref": "#/components/responses/BadRequest"
|
|
1264
|
+
},
|
|
1265
|
+
"401": {
|
|
1266
|
+
"$ref": "#/components/responses/Unauthorized"
|
|
1267
|
+
},
|
|
1268
|
+
"403": {
|
|
1269
|
+
"$ref": "#/components/responses/Forbidden"
|
|
1270
|
+
},
|
|
1271
|
+
"404": {
|
|
1272
|
+
"$ref": "#/components/responses/NotFound"
|
|
1273
|
+
},
|
|
1274
|
+
"409": {
|
|
1275
|
+
"$ref": "#/components/responses/Conflict"
|
|
1276
|
+
},
|
|
1277
|
+
"422": {
|
|
1278
|
+
"$ref": "#/components/responses/UnprocessableEntity"
|
|
1279
|
+
}
|
|
1280
|
+
},
|
|
1281
|
+
"x-error-codes": [
|
|
1282
|
+
"BAD_REQUEST",
|
|
1283
|
+
"UNAUTHORIZED",
|
|
1284
|
+
"FORBIDDEN",
|
|
1285
|
+
"NOT_FOUND",
|
|
1286
|
+
"IMMUTABLE_FIELD",
|
|
1287
|
+
"VALIDATION"
|
|
1288
|
+
],
|
|
1289
|
+
"x-cli": {
|
|
1290
|
+
"name": "whitelist-add",
|
|
1291
|
+
"positional": [
|
|
1292
|
+
"id"
|
|
1293
|
+
]
|
|
1294
|
+
}
|
|
1295
|
+
},
|
|
1296
|
+
"delete": {
|
|
1297
|
+
"operationId": "removeFromGateWhitelist",
|
|
1298
|
+
"summary": "Remove entries from a gate's whitelist",
|
|
1299
|
+
"description": "Removes identities from the gate's whitelist. Entries that aren't on the list are skipped, so the call is idempotent.\n\nRemoving the last entry leaves an empty whitelist block in place; to drop the block itself use `PUT` with `entries: []`.\n\n**Use case:** Revoke one beta tester's access without touching anyone else's.",
|
|
1300
|
+
"tags": [
|
|
1301
|
+
"Flags"
|
|
1302
|
+
],
|
|
1303
|
+
"parameters": [
|
|
1304
|
+
{
|
|
1305
|
+
"$ref": "#/components/parameters/ProjectId"
|
|
1306
|
+
},
|
|
1307
|
+
{
|
|
1308
|
+
"name": "id",
|
|
1309
|
+
"in": "path",
|
|
1310
|
+
"required": true,
|
|
1311
|
+
"description": "Stable opaque gate id (`gate_…`) or the gate's `name`.",
|
|
1312
|
+
"schema": {
|
|
1313
|
+
"$ref": "#/components/schemas/ResourceId"
|
|
1314
|
+
}
|
|
1315
|
+
}
|
|
1316
|
+
],
|
|
1317
|
+
"requestBody": {
|
|
1318
|
+
"required": true,
|
|
1319
|
+
"content": {
|
|
1320
|
+
"application/json": {
|
|
1321
|
+
"schema": {
|
|
1322
|
+
"$ref": "#/components/schemas/RemoveFromGateWhitelistRequest"
|
|
1323
|
+
},
|
|
1324
|
+
"examples": {
|
|
1325
|
+
"removeOne": {
|
|
1326
|
+
"summary": "Revoke one email",
|
|
1327
|
+
"value": {
|
|
1328
|
+
"entries": [
|
|
1329
|
+
"carol@acme.dev"
|
|
1330
|
+
]
|
|
1331
|
+
}
|
|
1332
|
+
}
|
|
1333
|
+
}
|
|
1334
|
+
}
|
|
1335
|
+
}
|
|
1336
|
+
},
|
|
1337
|
+
"responses": {
|
|
1338
|
+
"200": {
|
|
1339
|
+
"description": "Remove entries from a gate's whitelist",
|
|
1340
|
+
"content": {
|
|
1341
|
+
"application/json": {
|
|
1342
|
+
"schema": {
|
|
1343
|
+
"$ref": "#/components/schemas/GateWhitelist"
|
|
1344
|
+
},
|
|
1345
|
+
"example": {
|
|
1346
|
+
"id": "gate_01j7w8a1b2c3d4e5f6g7h8i9j0",
|
|
1347
|
+
"name": "new_checkout",
|
|
1348
|
+
"attr": "email",
|
|
1349
|
+
"entries": [
|
|
1350
|
+
"alice@acme.dev",
|
|
1351
|
+
"bob@acme.dev"
|
|
1352
|
+
]
|
|
1353
|
+
}
|
|
1354
|
+
}
|
|
1355
|
+
}
|
|
1356
|
+
},
|
|
1357
|
+
"400": {
|
|
1358
|
+
"$ref": "#/components/responses/BadRequest"
|
|
1359
|
+
},
|
|
1360
|
+
"401": {
|
|
1361
|
+
"$ref": "#/components/responses/Unauthorized"
|
|
1362
|
+
},
|
|
1363
|
+
"403": {
|
|
1364
|
+
"$ref": "#/components/responses/Forbidden"
|
|
1365
|
+
},
|
|
1366
|
+
"404": {
|
|
1367
|
+
"$ref": "#/components/responses/NotFound"
|
|
1368
|
+
},
|
|
1369
|
+
"409": {
|
|
1370
|
+
"$ref": "#/components/responses/Conflict"
|
|
1371
|
+
},
|
|
1372
|
+
"422": {
|
|
1373
|
+
"$ref": "#/components/responses/UnprocessableEntity"
|
|
1374
|
+
}
|
|
1375
|
+
},
|
|
1376
|
+
"x-error-codes": [
|
|
1377
|
+
"BAD_REQUEST",
|
|
1378
|
+
"UNAUTHORIZED",
|
|
1379
|
+
"FORBIDDEN",
|
|
1380
|
+
"NOT_FOUND",
|
|
1381
|
+
"VALIDATION"
|
|
1382
|
+
],
|
|
1383
|
+
"x-cli": {
|
|
1384
|
+
"name": "whitelist-remove",
|
|
1385
|
+
"positional": [
|
|
1386
|
+
"id"
|
|
1387
|
+
]
|
|
1388
|
+
}
|
|
1389
|
+
}
|
|
1390
|
+
},
|
|
1004
1391
|
"/api/admin/experiments": {
|
|
1005
1392
|
"get": {
|
|
1006
1393
|
"operationId": "listExperiments",
|
|
@@ -4316,15 +4703,115 @@
|
|
|
4316
4703
|
"content": {
|
|
4317
4704
|
"application/json": {
|
|
4318
4705
|
"schema": {
|
|
4319
|
-
"$ref": "#/components/schemas/UnsetKillswitchSwitchRequest"
|
|
4706
|
+
"$ref": "#/components/schemas/UnsetKillswitchSwitchRequest"
|
|
4707
|
+
},
|
|
4708
|
+
"examples": {
|
|
4709
|
+
"removeRegion": {
|
|
4710
|
+
"summary": "Remove eu_region from prod",
|
|
4711
|
+
"description": "Drops the `eu_region` override on prod so the flat `value` applies again.",
|
|
4712
|
+
"value": {
|
|
4713
|
+
"env": "prod",
|
|
4714
|
+
"switchKey": "eu_region"
|
|
4715
|
+
}
|
|
4716
|
+
}
|
|
4717
|
+
}
|
|
4718
|
+
}
|
|
4719
|
+
}
|
|
4720
|
+
},
|
|
4721
|
+
"responses": {
|
|
4722
|
+
"200": {
|
|
4723
|
+
"description": "Remove one switch entry",
|
|
4724
|
+
"content": {
|
|
4725
|
+
"application/json": {
|
|
4726
|
+
"schema": {
|
|
4727
|
+
"$ref": "#/components/schemas/UnsetKillswitchSwitchResponse"
|
|
4728
|
+
},
|
|
4729
|
+
"example": {
|
|
4730
|
+
"id": "ksw_01j7w9d8h2k4m6n8p0q2r4s6t8",
|
|
4731
|
+
"env": "prod",
|
|
4732
|
+
"switchKey": "eu_region",
|
|
4733
|
+
"removed": true
|
|
4734
|
+
}
|
|
4735
|
+
}
|
|
4736
|
+
}
|
|
4737
|
+
},
|
|
4738
|
+
"400": {
|
|
4739
|
+
"$ref": "#/components/responses/BadRequest"
|
|
4740
|
+
},
|
|
4741
|
+
"401": {
|
|
4742
|
+
"$ref": "#/components/responses/Unauthorized"
|
|
4743
|
+
},
|
|
4744
|
+
"403": {
|
|
4745
|
+
"$ref": "#/components/responses/Forbidden"
|
|
4746
|
+
},
|
|
4747
|
+
"404": {
|
|
4748
|
+
"$ref": "#/components/responses/NotFound"
|
|
4749
|
+
},
|
|
4750
|
+
"409": {
|
|
4751
|
+
"$ref": "#/components/responses/Conflict"
|
|
4752
|
+
},
|
|
4753
|
+
"422": {
|
|
4754
|
+
"$ref": "#/components/responses/UnprocessableEntity"
|
|
4755
|
+
}
|
|
4756
|
+
},
|
|
4757
|
+
"x-error-codes": [
|
|
4758
|
+
"BAD_REQUEST",
|
|
4759
|
+
"UNAUTHORIZED",
|
|
4760
|
+
"FORBIDDEN",
|
|
4761
|
+
"NOT_FOUND"
|
|
4762
|
+
],
|
|
4763
|
+
"x-cli": {
|
|
4764
|
+
"name": "unset",
|
|
4765
|
+
"positional": [
|
|
4766
|
+
"id"
|
|
4767
|
+
]
|
|
4768
|
+
}
|
|
4769
|
+
}
|
|
4770
|
+
},
|
|
4771
|
+
"/api/admin/killswitches/{id}/value": {
|
|
4772
|
+
"put": {
|
|
4773
|
+
"operationId": "setKillswitchValue",
|
|
4774
|
+
"summary": "Set the flat value on one env",
|
|
4775
|
+
"description": "Sets the flat `value` on a single `env`, publishing one new version on that env only. `switches` and other envs are untouched.\n\nUse this to trip (or untrip) a killswitch on one environment without replacing its per-key overrides.\n\n**Use cases**\n\n- **Trip on prod** — `{ \"env\": \"prod\", \"value\": true }`.\n- **Untrip on prod** — `{ \"env\": \"prod\", \"value\": false }`.",
|
|
4776
|
+
"tags": [
|
|
4777
|
+
"Killswitch"
|
|
4778
|
+
],
|
|
4779
|
+
"parameters": [
|
|
4780
|
+
{
|
|
4781
|
+
"$ref": "#/components/parameters/ProjectId"
|
|
4782
|
+
},
|
|
4783
|
+
{
|
|
4784
|
+
"name": "id",
|
|
4785
|
+
"in": "path",
|
|
4786
|
+
"required": true,
|
|
4787
|
+
"description": "Stable opaque killswitch id (`ksw_…`) or the killswitch's `name`.",
|
|
4788
|
+
"schema": {
|
|
4789
|
+
"$ref": "#/components/schemas/ResourceId"
|
|
4790
|
+
}
|
|
4791
|
+
}
|
|
4792
|
+
],
|
|
4793
|
+
"requestBody": {
|
|
4794
|
+
"required": true,
|
|
4795
|
+
"content": {
|
|
4796
|
+
"application/json": {
|
|
4797
|
+
"schema": {
|
|
4798
|
+
"$ref": "#/components/schemas/SetKillswitchValueRequest"
|
|
4320
4799
|
},
|
|
4321
4800
|
"examples": {
|
|
4322
|
-
"
|
|
4323
|
-
"summary": "
|
|
4324
|
-
"description": "
|
|
4801
|
+
"tripProd": {
|
|
4802
|
+
"summary": "Trip on prod",
|
|
4803
|
+
"description": "Publishes `value: true` on prod only, bumping that env's version.",
|
|
4325
4804
|
"value": {
|
|
4326
4805
|
"env": "prod",
|
|
4327
|
-
"
|
|
4806
|
+
"value": true
|
|
4807
|
+
}
|
|
4808
|
+
},
|
|
4809
|
+
"untripProd": {
|
|
4810
|
+
"summary": "Untrip on prod",
|
|
4811
|
+
"description": "Publishes `value: false` on prod only.",
|
|
4812
|
+
"value": {
|
|
4813
|
+
"env": "prod",
|
|
4814
|
+
"value": false
|
|
4328
4815
|
}
|
|
4329
4816
|
}
|
|
4330
4817
|
}
|
|
@@ -4333,17 +4820,22 @@
|
|
|
4333
4820
|
},
|
|
4334
4821
|
"responses": {
|
|
4335
4822
|
"200": {
|
|
4336
|
-
"description": "
|
|
4823
|
+
"description": "Set the flat value on one env",
|
|
4337
4824
|
"content": {
|
|
4338
4825
|
"application/json": {
|
|
4339
4826
|
"schema": {
|
|
4340
|
-
"$ref": "#/components/schemas/
|
|
4827
|
+
"$ref": "#/components/schemas/SetKillswitchValueResponse"
|
|
4341
4828
|
},
|
|
4342
4829
|
"example": {
|
|
4343
4830
|
"id": "ksw_01j7w9d8h2k4m6n8p0q2r4s6t8",
|
|
4344
4831
|
"env": "prod",
|
|
4345
|
-
"
|
|
4346
|
-
"
|
|
4832
|
+
"version": 6,
|
|
4833
|
+
"published": {
|
|
4834
|
+
"value": true,
|
|
4835
|
+
"switches": {
|
|
4836
|
+
"eu_region": true
|
|
4837
|
+
}
|
|
4838
|
+
}
|
|
4347
4839
|
}
|
|
4348
4840
|
}
|
|
4349
4841
|
}
|
|
@@ -4371,21 +4863,22 @@
|
|
|
4371
4863
|
"BAD_REQUEST",
|
|
4372
4864
|
"UNAUTHORIZED",
|
|
4373
4865
|
"FORBIDDEN",
|
|
4374
|
-
"NOT_FOUND"
|
|
4866
|
+
"NOT_FOUND",
|
|
4867
|
+
"VALIDATION"
|
|
4375
4868
|
],
|
|
4376
4869
|
"x-cli": {
|
|
4377
|
-
"name": "
|
|
4870
|
+
"name": "set-value",
|
|
4378
4871
|
"positional": [
|
|
4379
4872
|
"id"
|
|
4380
4873
|
]
|
|
4381
4874
|
}
|
|
4382
4875
|
}
|
|
4383
4876
|
},
|
|
4384
|
-
"/api/admin/killswitches/{id}/
|
|
4385
|
-
"
|
|
4386
|
-
"operationId": "
|
|
4387
|
-
"summary": "
|
|
4388
|
-
"description": "
|
|
4877
|
+
"/api/admin/killswitches/{id}/toggle": {
|
|
4878
|
+
"post": {
|
|
4879
|
+
"operationId": "toggleKillswitch",
|
|
4880
|
+
"summary": "Toggle a killswitch or one of its switches",
|
|
4881
|
+
"description": "Flips a killswitch on one environment and publishes a new version there. This is the one-call incident verb: it reads the current value, flips it, and publishes, so you don't have to fetch the killswitch first.\n\nEvery body field is optional, which is what makes the call widen cleanly:\n\n- **Flip the killswitch** — `{}`. Flips the flat `value` on `prod`.\n- **Flip one sub-switch** — `{ \"switchKey\": \"eu_region\" }`. Flips that entry on `prod`, creating it (from `false`) if it isn't in the map yet.\n- **Set it idempotently** — `{ \"switchKey\": \"eu_region\", \"value\": true }`. Publishes exactly that value, so a retried call can't undo the first one. A `null` `value` means \"flip\", not \"set to null\".\n- **Choose the environment** — add `\"env\": \"staging\"`. Omitted, `env` is `prod`.\n\nThe response reports both `previous` and `value`, so a caller that asked for a flip can see what it actually changed.\n\nPrefer this over `PUT /{id}/value` and `PUT /{id}/switch` unless you specifically need those endpoints' unconditional set semantics.",
|
|
4389
4882
|
"tags": [
|
|
4390
4883
|
"Killswitch"
|
|
4391
4884
|
],
|
|
@@ -4404,27 +4897,40 @@
|
|
|
4404
4897
|
}
|
|
4405
4898
|
],
|
|
4406
4899
|
"requestBody": {
|
|
4407
|
-
"required":
|
|
4900
|
+
"required": false,
|
|
4408
4901
|
"content": {
|
|
4409
4902
|
"application/json": {
|
|
4410
4903
|
"schema": {
|
|
4411
|
-
"$ref": "#/components/schemas/
|
|
4904
|
+
"$ref": "#/components/schemas/ToggleKillswitchRequest"
|
|
4412
4905
|
},
|
|
4413
4906
|
"examples": {
|
|
4414
|
-
"
|
|
4415
|
-
"summary": "
|
|
4416
|
-
"description": "
|
|
4907
|
+
"flipOnProd": {
|
|
4908
|
+
"summary": "Flip the killswitch on prod",
|
|
4909
|
+
"description": "No body fields — reads the flat `value` on prod and publishes its opposite.",
|
|
4910
|
+
"value": {}
|
|
4911
|
+
},
|
|
4912
|
+
"flipOneSwitch": {
|
|
4913
|
+
"summary": "Flip one sub-switch on prod",
|
|
4914
|
+
"description": "Flips `switches.eu_region` on prod, leaving the flat `value` and every other key alone.",
|
|
4417
4915
|
"value": {
|
|
4418
|
-
"
|
|
4916
|
+
"switchKey": "eu_region"
|
|
4917
|
+
}
|
|
4918
|
+
},
|
|
4919
|
+
"setOneSwitch": {
|
|
4920
|
+
"summary": "Trip one sub-switch idempotently",
|
|
4921
|
+
"description": "Publishes `switches.eu_region = true` on prod whatever it was before — safe to retry.",
|
|
4922
|
+
"value": {
|
|
4923
|
+
"switchKey": "eu_region",
|
|
4419
4924
|
"value": true
|
|
4420
4925
|
}
|
|
4421
4926
|
},
|
|
4422
|
-
"
|
|
4423
|
-
"summary": "
|
|
4424
|
-
"description": "
|
|
4927
|
+
"setOneSwitchOnStaging": {
|
|
4928
|
+
"summary": "Trip one sub-switch on a chosen env",
|
|
4929
|
+
"description": "The same idempotent set, published on staging instead of prod.",
|
|
4425
4930
|
"value": {
|
|
4426
|
-
"
|
|
4427
|
-
"value":
|
|
4931
|
+
"switchKey": "eu_region",
|
|
4932
|
+
"value": true,
|
|
4933
|
+
"env": "staging"
|
|
4428
4934
|
}
|
|
4429
4935
|
}
|
|
4430
4936
|
}
|
|
@@ -4433,18 +4939,21 @@
|
|
|
4433
4939
|
},
|
|
4434
4940
|
"responses": {
|
|
4435
4941
|
"200": {
|
|
4436
|
-
"description": "
|
|
4942
|
+
"description": "Toggle a killswitch or one of its switches",
|
|
4437
4943
|
"content": {
|
|
4438
4944
|
"application/json": {
|
|
4439
4945
|
"schema": {
|
|
4440
|
-
"$ref": "#/components/schemas/
|
|
4946
|
+
"$ref": "#/components/schemas/ToggleKillswitchResponse"
|
|
4441
4947
|
},
|
|
4442
4948
|
"example": {
|
|
4443
4949
|
"id": "ksw_01j7w9d8h2k4m6n8p0q2r4s6t8",
|
|
4444
4950
|
"env": "prod",
|
|
4445
|
-
"
|
|
4951
|
+
"switchKey": "eu_region",
|
|
4952
|
+
"previous": false,
|
|
4953
|
+
"value": true,
|
|
4954
|
+
"version": 7,
|
|
4446
4955
|
"published": {
|
|
4447
|
-
"value":
|
|
4956
|
+
"value": false,
|
|
4448
4957
|
"switches": {
|
|
4449
4958
|
"eu_region": true
|
|
4450
4959
|
}
|
|
@@ -4480,7 +4989,7 @@
|
|
|
4480
4989
|
"VALIDATION"
|
|
4481
4990
|
],
|
|
4482
4991
|
"x-cli": {
|
|
4483
|
-
"name": "
|
|
4992
|
+
"name": "toggle",
|
|
4484
4993
|
"positional": [
|
|
4485
4994
|
"id"
|
|
4486
4995
|
]
|
|
@@ -7016,35 +7525,273 @@
|
|
|
7016
7525
|
"403": {
|
|
7017
7526
|
"$ref": "#/components/responses/Forbidden"
|
|
7018
7527
|
},
|
|
7019
|
-
"404": {
|
|
7020
|
-
"$ref": "#/components/responses/NotFound"
|
|
7528
|
+
"404": {
|
|
7529
|
+
"$ref": "#/components/responses/NotFound"
|
|
7530
|
+
},
|
|
7531
|
+
"409": {
|
|
7532
|
+
"$ref": "#/components/responses/Conflict"
|
|
7533
|
+
},
|
|
7534
|
+
"422": {
|
|
7535
|
+
"$ref": "#/components/responses/UnprocessableEntity"
|
|
7536
|
+
}
|
|
7537
|
+
},
|
|
7538
|
+
"x-error-codes": [
|
|
7539
|
+
"BAD_REQUEST",
|
|
7540
|
+
"UNAUTHORIZED",
|
|
7541
|
+
"FORBIDDEN"
|
|
7542
|
+
],
|
|
7543
|
+
"x-cli": {
|
|
7544
|
+
"name": "list"
|
|
7545
|
+
}
|
|
7546
|
+
},
|
|
7547
|
+
"post": {
|
|
7548
|
+
"operationId": "createOpsItem",
|
|
7549
|
+
"summary": "File a queue item",
|
|
7550
|
+
"description": "Files one queue item — a bug report or a feature request — and fires the project's connectors (GitHub issue / Slack). `type` selects which; only the two user-fileable types are accepted (`error`/`alert` tickets are auto-filed by the platform). Returns the new id and per-project number.\n\n**Use cases**\n\n- **File a bug** — `{ \"type\": \"bug\", \"title\": \"Checkout 500s on Safari\", \"stepsToReproduce\": \"…\" }`.\n- **File a feature request** — `{ \"type\": \"feature_request\", \"title\": \"Dark mode\", \"priority\": \"nice_to_have\" }`.",
|
|
7551
|
+
"tags": [
|
|
7552
|
+
"Ops"
|
|
7553
|
+
],
|
|
7554
|
+
"parameters": [
|
|
7555
|
+
{
|
|
7556
|
+
"$ref": "#/components/parameters/ProjectId"
|
|
7557
|
+
}
|
|
7558
|
+
],
|
|
7559
|
+
"requestBody": {
|
|
7560
|
+
"required": true,
|
|
7561
|
+
"content": {
|
|
7562
|
+
"application/json": {
|
|
7563
|
+
"schema": {
|
|
7564
|
+
"$ref": "#/components/schemas/CreateOpsItemRequest"
|
|
7565
|
+
},
|
|
7566
|
+
"examples": {
|
|
7567
|
+
"bug": {
|
|
7568
|
+
"summary": "File a bug report",
|
|
7569
|
+
"value": {
|
|
7570
|
+
"type": "bug",
|
|
7571
|
+
"title": "Checkout button misaligned on mobile",
|
|
7572
|
+
"stepsToReproduce": "Open the cart on iOS Safari and tap the price row.",
|
|
7573
|
+
"actualResult": "The primary CTA overlaps the price.",
|
|
7574
|
+
"expectedResult": "The CTA sits below the price with clear spacing.",
|
|
7575
|
+
"priority": "high"
|
|
7576
|
+
}
|
|
7577
|
+
},
|
|
7578
|
+
"featureRequest": {
|
|
7579
|
+
"summary": "File a feature request",
|
|
7580
|
+
"value": {
|
|
7581
|
+
"type": "feature_request",
|
|
7582
|
+
"title": "Dark mode for the dashboard",
|
|
7583
|
+
"description": "Add a theme toggle that persists per user.",
|
|
7584
|
+
"useCase": "Reduce eye strain for users working at night.",
|
|
7585
|
+
"priority": "nice_to_have"
|
|
7586
|
+
}
|
|
7587
|
+
}
|
|
7588
|
+
}
|
|
7589
|
+
}
|
|
7590
|
+
}
|
|
7591
|
+
},
|
|
7592
|
+
"responses": {
|
|
7593
|
+
"201": {
|
|
7594
|
+
"description": "File a queue item",
|
|
7595
|
+
"content": {
|
|
7596
|
+
"application/json": {
|
|
7597
|
+
"schema": {
|
|
7598
|
+
"$ref": "#/components/schemas/CreateOpsItemResponse"
|
|
7599
|
+
},
|
|
7600
|
+
"example": {
|
|
7601
|
+
"id": "fb_01j7w8a1b2c3d4e5f6g7h8i9j0",
|
|
7602
|
+
"number": 7
|
|
7603
|
+
}
|
|
7604
|
+
}
|
|
7605
|
+
}
|
|
7606
|
+
},
|
|
7607
|
+
"400": {
|
|
7608
|
+
"$ref": "#/components/responses/BadRequest"
|
|
7609
|
+
},
|
|
7610
|
+
"401": {
|
|
7611
|
+
"$ref": "#/components/responses/Unauthorized"
|
|
7612
|
+
},
|
|
7613
|
+
"403": {
|
|
7614
|
+
"$ref": "#/components/responses/Forbidden"
|
|
7615
|
+
},
|
|
7616
|
+
"404": {
|
|
7617
|
+
"$ref": "#/components/responses/NotFound"
|
|
7618
|
+
},
|
|
7619
|
+
"409": {
|
|
7620
|
+
"$ref": "#/components/responses/Conflict"
|
|
7621
|
+
},
|
|
7622
|
+
"422": {
|
|
7623
|
+
"$ref": "#/components/responses/UnprocessableEntity"
|
|
7624
|
+
}
|
|
7625
|
+
},
|
|
7626
|
+
"x-error-codes": [
|
|
7627
|
+
"BAD_REQUEST",
|
|
7628
|
+
"UNAUTHORIZED",
|
|
7629
|
+
"FORBIDDEN",
|
|
7630
|
+
"ALREADY_EXISTS",
|
|
7631
|
+
"VALIDATION"
|
|
7632
|
+
],
|
|
7633
|
+
"x-cli": {
|
|
7634
|
+
"name": "create",
|
|
7635
|
+
"positional": [
|
|
7636
|
+
"title"
|
|
7637
|
+
],
|
|
7638
|
+
"commands": [
|
|
7639
|
+
{
|
|
7640
|
+
"name": "create",
|
|
7641
|
+
"summary": "File a queue item (bug or feature request) — pass --type."
|
|
7642
|
+
},
|
|
7643
|
+
{
|
|
7644
|
+
"name": "bug",
|
|
7645
|
+
"summary": "File a bug report.",
|
|
7646
|
+
"preset": {
|
|
7647
|
+
"type": "bug"
|
|
7648
|
+
}
|
|
7649
|
+
},
|
|
7650
|
+
{
|
|
7651
|
+
"name": "feature",
|
|
7652
|
+
"summary": "File a feature request.",
|
|
7653
|
+
"preset": {
|
|
7654
|
+
"type": "feature_request"
|
|
7655
|
+
}
|
|
7656
|
+
}
|
|
7657
|
+
]
|
|
7658
|
+
}
|
|
7659
|
+
}
|
|
7660
|
+
},
|
|
7661
|
+
"/ops/bug": {
|
|
7662
|
+
"post": {
|
|
7663
|
+
"operationId": "createPublicBug",
|
|
7664
|
+
"summary": "File a bug",
|
|
7665
|
+
"description": "Files one bug onto a project's queue, awaiting human approval. This is the **public** intake: it authenticates with a *client* SDK key rather than an admin key, so it can be called from a CLI, an installer script, a devtools overlay, or any shipped code — the same places a client key already lives.\n\nThree gates decide whether a ticket is filed, and nothing else the caller sends can widen them:\n\n1. the key is a `client` key carrying the `tickets:public_create` scope,\n2. the key's project has public ticket creation enabled, and\n3. the item is filed as `pending_approval` — parked out of the work queue until a human promotes it in the dashboard.\n\nThe project is the key's own project; there is no `X-Project-Id` to pass and no way to file into someone else's queue. Repeat submissions of the same title dedupe against the open ticket already tracking it, which returns `200` with `deduped: true` instead of filing again.\n\nThis endpoint is served by the Shipeasy **edge worker** (`api.shipeasy.ai`), not the admin API — see `servers` below.\n\n**Use case:** `shipeasy setup` fails on a customer's machine and self-reports the failure with the user's consent — `{ \"title\": \"Setup failed at Feature installs\", \"stepsToReproduce\": \"…\", \"actualResult\": \"…\" }`.",
|
|
7666
|
+
"tags": [
|
|
7667
|
+
"Ops"
|
|
7668
|
+
],
|
|
7669
|
+
"servers": [
|
|
7670
|
+
{
|
|
7671
|
+
"url": "https://api.shipeasy.ai",
|
|
7672
|
+
"description": "Shipeasy edge worker (production)"
|
|
7673
|
+
},
|
|
7674
|
+
{
|
|
7675
|
+
"url": "http://localhost:8787",
|
|
7676
|
+
"description": "Local `wrangler dev`"
|
|
7677
|
+
}
|
|
7678
|
+
],
|
|
7679
|
+
"security": [
|
|
7680
|
+
{
|
|
7681
|
+
"clientSdkKey": []
|
|
7682
|
+
}
|
|
7683
|
+
],
|
|
7684
|
+
"requestBody": {
|
|
7685
|
+
"required": true,
|
|
7686
|
+
"content": {
|
|
7687
|
+
"application/json": {
|
|
7688
|
+
"schema": {
|
|
7689
|
+
"$ref": "#/components/schemas/CreatePublicBugRequest"
|
|
7690
|
+
},
|
|
7691
|
+
"examples": {
|
|
7692
|
+
"minimal": {
|
|
7693
|
+
"summary": "Just a title",
|
|
7694
|
+
"description": "Every other field is optional — the queue triages from `open` at default priority.",
|
|
7695
|
+
"value": {
|
|
7696
|
+
"title": "Checkout button misaligned on mobile"
|
|
7697
|
+
}
|
|
7698
|
+
},
|
|
7699
|
+
"full": {
|
|
7700
|
+
"summary": "A full report",
|
|
7701
|
+
"value": {
|
|
7702
|
+
"title": "Checkout button misaligned on mobile",
|
|
7703
|
+
"stepsToReproduce": "Open the cart on iOS Safari and tap the price row.",
|
|
7704
|
+
"actualResult": "The primary CTA overlaps the price.",
|
|
7705
|
+
"expectedResult": "The CTA sits below the price with clear spacing.",
|
|
7706
|
+
"priority": "high",
|
|
7707
|
+
"reporterEmail": "alice@acme.dev",
|
|
7708
|
+
"pageUrl": "https://acme.dev/cart"
|
|
7709
|
+
}
|
|
7710
|
+
}
|
|
7711
|
+
}
|
|
7712
|
+
}
|
|
7713
|
+
}
|
|
7714
|
+
},
|
|
7715
|
+
"responses": {
|
|
7716
|
+
"200": {
|
|
7717
|
+
"description": "An open ticket already tracks this report; nothing was filed",
|
|
7718
|
+
"content": {
|
|
7719
|
+
"application/json": {
|
|
7720
|
+
"schema": {
|
|
7721
|
+
"$ref": "#/components/schemas/CreatePublicTicketResponse"
|
|
7722
|
+
},
|
|
7723
|
+
"example": {
|
|
7724
|
+
"number": 7,
|
|
7725
|
+
"deduped": true
|
|
7726
|
+
}
|
|
7727
|
+
}
|
|
7728
|
+
}
|
|
7729
|
+
},
|
|
7730
|
+
"201": {
|
|
7731
|
+
"description": "File a bug",
|
|
7732
|
+
"content": {
|
|
7733
|
+
"application/json": {
|
|
7734
|
+
"schema": {
|
|
7735
|
+
"$ref": "#/components/schemas/CreatePublicTicketResponse"
|
|
7736
|
+
},
|
|
7737
|
+
"example": {
|
|
7738
|
+
"id": "fb_01j7w8a1b2c3d4e5f6g7h8i9j0",
|
|
7739
|
+
"number": 7
|
|
7740
|
+
}
|
|
7741
|
+
}
|
|
7742
|
+
}
|
|
7743
|
+
},
|
|
7744
|
+
"400": {
|
|
7745
|
+
"$ref": "#/components/responses/BadRequest"
|
|
7746
|
+
},
|
|
7747
|
+
"401": {
|
|
7748
|
+
"$ref": "#/components/responses/Unauthorized"
|
|
7749
|
+
},
|
|
7750
|
+
"403": {
|
|
7751
|
+
"$ref": "#/components/responses/Forbidden"
|
|
7752
|
+
},
|
|
7753
|
+
"413": {
|
|
7754
|
+
"$ref": "#/components/responses/PayloadTooLarge"
|
|
7021
7755
|
},
|
|
7022
|
-
"
|
|
7023
|
-
"$ref": "#/components/responses/
|
|
7756
|
+
"429": {
|
|
7757
|
+
"$ref": "#/components/responses/TooManyRequests"
|
|
7024
7758
|
},
|
|
7025
|
-
"
|
|
7026
|
-
"$ref": "#/components/responses/
|
|
7759
|
+
"503": {
|
|
7760
|
+
"$ref": "#/components/responses/ServiceUnavailable"
|
|
7027
7761
|
}
|
|
7028
7762
|
},
|
|
7029
7763
|
"x-error-codes": [
|
|
7030
7764
|
"BAD_REQUEST",
|
|
7031
7765
|
"UNAUTHORIZED",
|
|
7032
|
-
"FORBIDDEN"
|
|
7766
|
+
"FORBIDDEN",
|
|
7767
|
+
"VALIDATION"
|
|
7033
7768
|
],
|
|
7034
7769
|
"x-cli": {
|
|
7035
|
-
"
|
|
7770
|
+
"hidden": true
|
|
7036
7771
|
}
|
|
7037
|
-
}
|
|
7772
|
+
}
|
|
7773
|
+
},
|
|
7774
|
+
"/ops/feature-request": {
|
|
7038
7775
|
"post": {
|
|
7039
|
-
"operationId": "
|
|
7040
|
-
"summary": "File a
|
|
7041
|
-
"description": "Files one
|
|
7776
|
+
"operationId": "createPublicFeatureRequest",
|
|
7777
|
+
"summary": "File a feature request",
|
|
7778
|
+
"description": "Files one feature request onto a project's queue, awaiting human approval. The feature-request counterpart to `POST /ops/bug`, with the same three gates: a `client` key carrying `tickets:public_create`, a project that has opted in, and a `pending_approval` state forced server-side.\n\nThe project is the key's own project; there is no `X-Project-Id` to pass. Repeat submissions of the same title dedupe against the open ticket already tracking it, which returns `200` with `deduped: true` instead of filing again.\n\nThis endpoint is served by the Shipeasy **edge worker** (`api.shipeasy.ai`), not the admin API — see `servers` below.\n\n**Use case:** An in-product \"request a feature\" form posts what the user asked for — `{ \"title\": \"Dark mode\", \"useCase\": \"Reduce eye strain at night\" }`.",
|
|
7042
7779
|
"tags": [
|
|
7043
7780
|
"Ops"
|
|
7044
7781
|
],
|
|
7045
|
-
"
|
|
7782
|
+
"servers": [
|
|
7046
7783
|
{
|
|
7047
|
-
"
|
|
7784
|
+
"url": "https://api.shipeasy.ai",
|
|
7785
|
+
"description": "Shipeasy edge worker (production)"
|
|
7786
|
+
},
|
|
7787
|
+
{
|
|
7788
|
+
"url": "http://localhost:8787",
|
|
7789
|
+
"description": "Local `wrangler dev`"
|
|
7790
|
+
}
|
|
7791
|
+
],
|
|
7792
|
+
"security": [
|
|
7793
|
+
{
|
|
7794
|
+
"clientSdkKey": []
|
|
7048
7795
|
}
|
|
7049
7796
|
],
|
|
7050
7797
|
"requestBody": {
|
|
@@ -7052,28 +7799,23 @@
|
|
|
7052
7799
|
"content": {
|
|
7053
7800
|
"application/json": {
|
|
7054
7801
|
"schema": {
|
|
7055
|
-
"$ref": "#/components/schemas/
|
|
7802
|
+
"$ref": "#/components/schemas/CreatePublicFeatureRequestRequest"
|
|
7056
7803
|
},
|
|
7057
7804
|
"examples": {
|
|
7058
|
-
"
|
|
7059
|
-
"summary": "
|
|
7805
|
+
"minimal": {
|
|
7806
|
+
"summary": "Just a title",
|
|
7060
7807
|
"value": {
|
|
7061
|
-
"
|
|
7062
|
-
"title": "Checkout button misaligned on mobile",
|
|
7063
|
-
"stepsToReproduce": "Open the cart on iOS Safari and tap the price row.",
|
|
7064
|
-
"actualResult": "The primary CTA overlaps the price.",
|
|
7065
|
-
"expectedResult": "The CTA sits below the price with clear spacing.",
|
|
7066
|
-
"priority": "high"
|
|
7808
|
+
"title": "Dark mode for the dashboard"
|
|
7067
7809
|
}
|
|
7068
7810
|
},
|
|
7069
|
-
"
|
|
7070
|
-
"summary": "
|
|
7811
|
+
"full": {
|
|
7812
|
+
"summary": "A full request",
|
|
7071
7813
|
"value": {
|
|
7072
|
-
"type": "feature_request",
|
|
7073
7814
|
"title": "Dark mode for the dashboard",
|
|
7074
7815
|
"description": "Add a theme toggle that persists per user.",
|
|
7075
7816
|
"useCase": "Reduce eye strain for users working at night.",
|
|
7076
|
-
"priority": "nice_to_have"
|
|
7817
|
+
"priority": "nice_to_have",
|
|
7818
|
+
"reporterEmail": "alice@acme.dev"
|
|
7077
7819
|
}
|
|
7078
7820
|
}
|
|
7079
7821
|
}
|
|
@@ -7081,16 +7823,30 @@
|
|
|
7081
7823
|
}
|
|
7082
7824
|
},
|
|
7083
7825
|
"responses": {
|
|
7826
|
+
"200": {
|
|
7827
|
+
"description": "An open ticket already tracks this report; nothing was filed",
|
|
7828
|
+
"content": {
|
|
7829
|
+
"application/json": {
|
|
7830
|
+
"schema": {
|
|
7831
|
+
"$ref": "#/components/schemas/CreatePublicTicketResponse"
|
|
7832
|
+
},
|
|
7833
|
+
"example": {
|
|
7834
|
+
"number": 7,
|
|
7835
|
+
"deduped": true
|
|
7836
|
+
}
|
|
7837
|
+
}
|
|
7838
|
+
}
|
|
7839
|
+
},
|
|
7084
7840
|
"201": {
|
|
7085
|
-
"description": "File a
|
|
7841
|
+
"description": "File a feature request",
|
|
7086
7842
|
"content": {
|
|
7087
7843
|
"application/json": {
|
|
7088
7844
|
"schema": {
|
|
7089
|
-
"$ref": "#/components/schemas/
|
|
7845
|
+
"$ref": "#/components/schemas/CreatePublicTicketResponse"
|
|
7090
7846
|
},
|
|
7091
7847
|
"example": {
|
|
7092
7848
|
"id": "fb_01j7w8a1b2c3d4e5f6g7h8i9j0",
|
|
7093
|
-
"number":
|
|
7849
|
+
"number": 8
|
|
7094
7850
|
}
|
|
7095
7851
|
}
|
|
7096
7852
|
}
|
|
@@ -7104,48 +7860,24 @@
|
|
|
7104
7860
|
"403": {
|
|
7105
7861
|
"$ref": "#/components/responses/Forbidden"
|
|
7106
7862
|
},
|
|
7107
|
-
"
|
|
7108
|
-
"$ref": "#/components/responses/
|
|
7863
|
+
"413": {
|
|
7864
|
+
"$ref": "#/components/responses/PayloadTooLarge"
|
|
7109
7865
|
},
|
|
7110
|
-
"
|
|
7111
|
-
"$ref": "#/components/responses/
|
|
7866
|
+
"429": {
|
|
7867
|
+
"$ref": "#/components/responses/TooManyRequests"
|
|
7112
7868
|
},
|
|
7113
|
-
"
|
|
7114
|
-
"$ref": "#/components/responses/
|
|
7869
|
+
"503": {
|
|
7870
|
+
"$ref": "#/components/responses/ServiceUnavailable"
|
|
7115
7871
|
}
|
|
7116
7872
|
},
|
|
7117
7873
|
"x-error-codes": [
|
|
7118
7874
|
"BAD_REQUEST",
|
|
7119
7875
|
"UNAUTHORIZED",
|
|
7120
7876
|
"FORBIDDEN",
|
|
7121
|
-
"ALREADY_EXISTS",
|
|
7122
7877
|
"VALIDATION"
|
|
7123
7878
|
],
|
|
7124
7879
|
"x-cli": {
|
|
7125
|
-
"
|
|
7126
|
-
"positional": [
|
|
7127
|
-
"title"
|
|
7128
|
-
],
|
|
7129
|
-
"commands": [
|
|
7130
|
-
{
|
|
7131
|
-
"name": "create",
|
|
7132
|
-
"summary": "File a queue item (bug or feature request) — pass --type."
|
|
7133
|
-
},
|
|
7134
|
-
{
|
|
7135
|
-
"name": "bug",
|
|
7136
|
-
"summary": "File a bug report.",
|
|
7137
|
-
"preset": {
|
|
7138
|
-
"type": "bug"
|
|
7139
|
-
}
|
|
7140
|
-
},
|
|
7141
|
-
{
|
|
7142
|
-
"name": "feature",
|
|
7143
|
-
"summary": "File a feature request.",
|
|
7144
|
-
"preset": {
|
|
7145
|
-
"type": "feature_request"
|
|
7146
|
-
}
|
|
7147
|
-
}
|
|
7148
|
-
]
|
|
7880
|
+
"hidden": true
|
|
7149
7881
|
}
|
|
7150
7882
|
}
|
|
7151
7883
|
},
|
|
@@ -12187,6 +12919,12 @@
|
|
|
12187
12919
|
"scheme": "bearer",
|
|
12188
12920
|
"bearerFormat": "sdk_admin_*",
|
|
12189
12921
|
"description": "Pass an admin SDK key as `Authorization: Bearer sdk_admin_…`. Mint via `POST /api/admin/keys` with `type: \"admin\"`."
|
|
12922
|
+
},
|
|
12923
|
+
"clientSdkKey": {
|
|
12924
|
+
"type": "apiKey",
|
|
12925
|
+
"in": "header",
|
|
12926
|
+
"name": "X-SDK-Key",
|
|
12927
|
+
"description": "Pass a **client** SDK key as `X-SDK-Key: sdk_client_…`. Used only by the public ticket intake (`POST /ops/bug`, `POST /ops/feature-request`), which is served by the edge worker rather than the admin API.\n\nClient keys are designed to be embedded in shipped code — a CLI, an installer, a browser bundle — so presenting one here is safe by construction: it can do nothing but file a rate-limited ticket into its own project, in a state a human must approve. It carries no read access and cannot reach any other operation in this contract.\n\nThe key must additionally carry the `tickets:public_create` scope, and its project must have public ticket creation enabled."
|
|
12190
12928
|
}
|
|
12191
12929
|
},
|
|
12192
12930
|
"parameters": {
|
|
@@ -13683,6 +14421,118 @@
|
|
|
13683
14421
|
},
|
|
13684
14422
|
"description": "Recent audit rows for one gate, newest first."
|
|
13685
14423
|
},
|
|
14424
|
+
"GateWhitelistAttr": {
|
|
14425
|
+
"type": "string",
|
|
14426
|
+
"enum": [
|
|
14427
|
+
"email",
|
|
14428
|
+
"user_id"
|
|
14429
|
+
],
|
|
14430
|
+
"description": "Which identity attribute the whitelist matches on. `email` compares the caller's `email`; `user_id` compares the caller's `userID`. A whitelist matches on exactly one of the two at a time."
|
|
14431
|
+
},
|
|
14432
|
+
"GateWhitelist": {
|
|
14433
|
+
"type": "object",
|
|
14434
|
+
"properties": {
|
|
14435
|
+
"id": {
|
|
14436
|
+
"type": "string",
|
|
14437
|
+
"description": "Resolved gate id."
|
|
14438
|
+
},
|
|
14439
|
+
"name": {
|
|
14440
|
+
"type": "string",
|
|
14441
|
+
"description": "The gate's stable `name`."
|
|
14442
|
+
},
|
|
14443
|
+
"attr": {
|
|
14444
|
+
"$ref": "#/components/schemas/GateWhitelistAttr"
|
|
14445
|
+
},
|
|
14446
|
+
"entries": {
|
|
14447
|
+
"type": "array",
|
|
14448
|
+
"items": {
|
|
14449
|
+
"type": "string",
|
|
14450
|
+
"minLength": 1
|
|
14451
|
+
},
|
|
14452
|
+
"description": "The whitelisted identities, in the order they are stored. Deduplicated case-sensitively; an empty array means the gate has no whitelist."
|
|
14453
|
+
}
|
|
14454
|
+
},
|
|
14455
|
+
"required": [
|
|
14456
|
+
"id",
|
|
14457
|
+
"name",
|
|
14458
|
+
"attr",
|
|
14459
|
+
"entries"
|
|
14460
|
+
],
|
|
14461
|
+
"additionalProperties": false,
|
|
14462
|
+
"description": "A gate's whitelist — the always-first allowlist that admits the listed identities before any targeting rule or rollout runs. Backed by the pinned `whitelist` entry at the head of the gate's `stack`, so it is the same list the dashboard's Whitelist block edits."
|
|
14463
|
+
},
|
|
14464
|
+
"SetGateWhitelistRequest": {
|
|
14465
|
+
"type": "object",
|
|
14466
|
+
"properties": {
|
|
14467
|
+
"attr": {
|
|
14468
|
+
"anyOf": [
|
|
14469
|
+
{
|
|
14470
|
+
"$ref": "#/components/schemas/GateWhitelistAttr"
|
|
14471
|
+
}
|
|
14472
|
+
],
|
|
14473
|
+
"description": "Identity attribute to match on. Defaults to the whitelist's current attribute, or `email` when the gate has no whitelist yet."
|
|
14474
|
+
},
|
|
14475
|
+
"entries": {
|
|
14476
|
+
"type": "array",
|
|
14477
|
+
"items": {
|
|
14478
|
+
"type": "string",
|
|
14479
|
+
"minLength": 1
|
|
14480
|
+
},
|
|
14481
|
+
"description": "The complete whitelist after the call. Pass `[]` to remove the whitelist from the gate entirely."
|
|
14482
|
+
}
|
|
14483
|
+
},
|
|
14484
|
+
"required": [
|
|
14485
|
+
"entries"
|
|
14486
|
+
],
|
|
14487
|
+
"additionalProperties": false,
|
|
14488
|
+
"description": "Body for `PUT /api/admin/gates/{id}/whitelist`. Replaces the whole list — idempotent, and the only call that can switch `attr` or clear the whitelist."
|
|
14489
|
+
},
|
|
14490
|
+
"AddToGateWhitelistRequest": {
|
|
14491
|
+
"type": "object",
|
|
14492
|
+
"properties": {
|
|
14493
|
+
"attr": {
|
|
14494
|
+
"anyOf": [
|
|
14495
|
+
{
|
|
14496
|
+
"$ref": "#/components/schemas/GateWhitelistAttr"
|
|
14497
|
+
}
|
|
14498
|
+
],
|
|
14499
|
+
"description": "Identity attribute to match on. Only honoured when the gate has no whitelist yet (this call creates it); passing an attribute that disagrees with an existing whitelist is a 409 rather than a silent re-key of the entries already there."
|
|
14500
|
+
},
|
|
14501
|
+
"entries": {
|
|
14502
|
+
"type": "array",
|
|
14503
|
+
"minItems": 1,
|
|
14504
|
+
"items": {
|
|
14505
|
+
"type": "string",
|
|
14506
|
+
"minLength": 1
|
|
14507
|
+
},
|
|
14508
|
+
"description": "Identities to admit. Already-listed entries are skipped, so the call is idempotent."
|
|
14509
|
+
}
|
|
14510
|
+
},
|
|
14511
|
+
"required": [
|
|
14512
|
+
"entries"
|
|
14513
|
+
],
|
|
14514
|
+
"additionalProperties": false,
|
|
14515
|
+
"description": "Body for `POST /api/admin/gates/{id}/whitelist`. Adds entries to the whitelist, creating it if the gate doesn't have one."
|
|
14516
|
+
},
|
|
14517
|
+
"RemoveFromGateWhitelistRequest": {
|
|
14518
|
+
"type": "object",
|
|
14519
|
+
"properties": {
|
|
14520
|
+
"entries": {
|
|
14521
|
+
"type": "array",
|
|
14522
|
+
"minItems": 1,
|
|
14523
|
+
"items": {
|
|
14524
|
+
"type": "string",
|
|
14525
|
+
"minLength": 1
|
|
14526
|
+
},
|
|
14527
|
+
"description": "Identities to stop admitting. Entries that aren't listed are skipped, so the call is idempotent."
|
|
14528
|
+
}
|
|
14529
|
+
},
|
|
14530
|
+
"required": [
|
|
14531
|
+
"entries"
|
|
14532
|
+
],
|
|
14533
|
+
"additionalProperties": false,
|
|
14534
|
+
"description": "Body for `DELETE /api/admin/gates/{id}/whitelist`. Removes entries from the whitelist. Removing the last entry leaves an empty whitelist in place — use `PUT` with `entries: []` to drop the block itself."
|
|
14535
|
+
},
|
|
13686
14536
|
"ExperimentApiRow": {
|
|
13687
14537
|
"type": "object",
|
|
13688
14538
|
"properties": {
|
|
@@ -16713,8 +17563,96 @@
|
|
|
16713
17563
|
"id": {
|
|
16714
17564
|
"type": "string"
|
|
16715
17565
|
},
|
|
16716
|
-
"env": {
|
|
16717
|
-
"$ref": "#/components/schemas/Env"
|
|
17566
|
+
"env": {
|
|
17567
|
+
"$ref": "#/components/schemas/Env"
|
|
17568
|
+
},
|
|
17569
|
+
"version": {
|
|
17570
|
+
"type": "integer",
|
|
17571
|
+
"minimum": -9007199254740991,
|
|
17572
|
+
"maximum": 9007199254740991,
|
|
17573
|
+
"description": "Newly published version on `env`."
|
|
17574
|
+
},
|
|
17575
|
+
"published": {
|
|
17576
|
+
"$ref": "#/components/schemas/KillswitchValue"
|
|
17577
|
+
}
|
|
17578
|
+
},
|
|
17579
|
+
"required": [
|
|
17580
|
+
"id",
|
|
17581
|
+
"env",
|
|
17582
|
+
"version",
|
|
17583
|
+
"published"
|
|
17584
|
+
],
|
|
17585
|
+
"additionalProperties": false
|
|
17586
|
+
},
|
|
17587
|
+
"ToggleKillswitchRequest": {
|
|
17588
|
+
"type": "object",
|
|
17589
|
+
"properties": {
|
|
17590
|
+
"switchKey": {
|
|
17591
|
+
"anyOf": [
|
|
17592
|
+
{
|
|
17593
|
+
"type": "string",
|
|
17594
|
+
"maxLength": 64,
|
|
17595
|
+
"pattern": "^[a-z0-9](?:[a-z0-9_-]*[a-z0-9])?$"
|
|
17596
|
+
},
|
|
17597
|
+
{
|
|
17598
|
+
"type": "null"
|
|
17599
|
+
}
|
|
17600
|
+
],
|
|
17601
|
+
"description": "Which target to flip. Omit (or `null`) to flip the killswitch's own flat `value`; name a switch key to flip that nested sub-switch instead, creating the entry if it doesn't exist yet."
|
|
17602
|
+
},
|
|
17603
|
+
"value": {
|
|
17604
|
+
"anyOf": [
|
|
17605
|
+
{
|
|
17606
|
+
"type": "boolean"
|
|
17607
|
+
},
|
|
17608
|
+
{
|
|
17609
|
+
"type": "null"
|
|
17610
|
+
}
|
|
17611
|
+
],
|
|
17612
|
+
"description": "The value to publish. Omit (or `null`) to flip whatever is stored now — read-modify-write in one call. Pass an explicit `true`/`false` to make the call idempotent, so a retry can't undo the first attempt."
|
|
17613
|
+
},
|
|
17614
|
+
"env": {
|
|
17615
|
+
"anyOf": [
|
|
17616
|
+
{
|
|
17617
|
+
"$ref": "#/components/schemas/Env"
|
|
17618
|
+
}
|
|
17619
|
+
],
|
|
17620
|
+
"description": "Environment to publish on. Defaults to `prod` — the environment an incident response means when it says \"kill it\"."
|
|
17621
|
+
}
|
|
17622
|
+
},
|
|
17623
|
+
"additionalProperties": false,
|
|
17624
|
+
"description": "Body for `POST /api/admin/killswitches/{id}/toggle`. Every field is optional, so the four useful calls read as one method with a widening argument list:\n\n- `{}` — flip the flat value on prod.\n- `{ \"switchKey\": \"eu_region\" }` — flip that sub-switch on prod.\n- `{ \"switchKey\": \"eu_region\", \"value\": true }` — set that sub-switch on prod, idempotently.\n- `{ \"switchKey\": \"eu_region\", \"value\": true, \"env\": \"staging\" }` — the same, on a chosen env."
|
|
17625
|
+
},
|
|
17626
|
+
"ToggleKillswitchResponse": {
|
|
17627
|
+
"type": "object",
|
|
17628
|
+
"properties": {
|
|
17629
|
+
"id": {
|
|
17630
|
+
"type": "string",
|
|
17631
|
+
"description": "Resolved killswitch id."
|
|
17632
|
+
},
|
|
17633
|
+
"env": {
|
|
17634
|
+
"$ref": "#/components/schemas/Env"
|
|
17635
|
+
},
|
|
17636
|
+
"switchKey": {
|
|
17637
|
+
"anyOf": [
|
|
17638
|
+
{
|
|
17639
|
+
"type": "string",
|
|
17640
|
+
"maxLength": 64,
|
|
17641
|
+
"pattern": "^[a-z0-9](?:[a-z0-9_-]*[a-z0-9])?$"
|
|
17642
|
+
},
|
|
17643
|
+
{
|
|
17644
|
+
"type": "null"
|
|
17645
|
+
}
|
|
17646
|
+
],
|
|
17647
|
+
"description": "The sub-switch that was flipped, or `null` when the flat `value` was."
|
|
17648
|
+
},
|
|
17649
|
+
"previous": {
|
|
17650
|
+
"type": "boolean",
|
|
17651
|
+
"description": "The value that was in effect before this call. Equal to `value` when an idempotent `value` matched what was already stored."
|
|
17652
|
+
},
|
|
17653
|
+
"value": {
|
|
17654
|
+
"type": "boolean",
|
|
17655
|
+
"description": "The value now in effect on `env`."
|
|
16718
17656
|
},
|
|
16719
17657
|
"version": {
|
|
16720
17658
|
"type": "integer",
|
|
@@ -16729,6 +17667,9 @@
|
|
|
16729
17667
|
"required": [
|
|
16730
17668
|
"id",
|
|
16731
17669
|
"env",
|
|
17670
|
+
"switchKey",
|
|
17671
|
+
"previous",
|
|
17672
|
+
"value",
|
|
16732
17673
|
"version",
|
|
16733
17674
|
"published"
|
|
16734
17675
|
],
|
|
@@ -20494,42 +21435,382 @@
|
|
|
20494
21435
|
},
|
|
20495
21436
|
"createdAt": {
|
|
20496
21437
|
"type": "string",
|
|
20497
|
-
"description": "ISO-8601 creation timestamp."
|
|
21438
|
+
"description": "ISO-8601 creation timestamp."
|
|
21439
|
+
},
|
|
21440
|
+
"updatedAt": {
|
|
21441
|
+
"type": "string",
|
|
21442
|
+
"description": "ISO-8601 last-update timestamp."
|
|
21443
|
+
}
|
|
21444
|
+
},
|
|
21445
|
+
"required": [
|
|
21446
|
+
"id",
|
|
21447
|
+
"number",
|
|
21448
|
+
"type",
|
|
21449
|
+
"title",
|
|
21450
|
+
"status",
|
|
21451
|
+
"priority",
|
|
21452
|
+
"source",
|
|
21453
|
+
"owner",
|
|
21454
|
+
"run",
|
|
21455
|
+
"lastRun",
|
|
21456
|
+
"createdAt",
|
|
21457
|
+
"updatedAt"
|
|
21458
|
+
],
|
|
21459
|
+
"additionalProperties": {},
|
|
21460
|
+
"description": "One queue item — any type — plus its resolved `owner` and run state."
|
|
21461
|
+
},
|
|
21462
|
+
"description": "A page of queue items, newest first."
|
|
21463
|
+
},
|
|
21464
|
+
"CreateBugRequest": {
|
|
21465
|
+
"type": "object",
|
|
21466
|
+
"properties": {
|
|
21467
|
+
"type": {
|
|
21468
|
+
"type": "string",
|
|
21469
|
+
"enum": [
|
|
21470
|
+
"bug"
|
|
21471
|
+
],
|
|
21472
|
+
"description": "Discriminator — files a bug."
|
|
21473
|
+
},
|
|
21474
|
+
"title": {
|
|
21475
|
+
"type": "string",
|
|
21476
|
+
"minLength": 1,
|
|
21477
|
+
"maxLength": 200,
|
|
21478
|
+
"pattern": "^\\S(.*\\S)?$",
|
|
21479
|
+
"description": "One-line bug title (no leading/trailing whitespace)."
|
|
21480
|
+
},
|
|
21481
|
+
"stepsToReproduce": {
|
|
21482
|
+
"type": "string",
|
|
21483
|
+
"maxLength": 8000,
|
|
21484
|
+
"default": "",
|
|
21485
|
+
"description": "How to reproduce the bug."
|
|
21486
|
+
},
|
|
21487
|
+
"actualResult": {
|
|
21488
|
+
"type": "string",
|
|
21489
|
+
"maxLength": 8000,
|
|
21490
|
+
"default": "",
|
|
21491
|
+
"description": "What actually happened."
|
|
21492
|
+
},
|
|
21493
|
+
"expectedResult": {
|
|
21494
|
+
"type": "string",
|
|
21495
|
+
"maxLength": 8000,
|
|
21496
|
+
"default": "",
|
|
21497
|
+
"description": "What was expected instead."
|
|
21498
|
+
},
|
|
21499
|
+
"priority": {
|
|
21500
|
+
"anyOf": [
|
|
21501
|
+
{
|
|
21502
|
+
"$ref": "#/components/schemas/OpsItemPriority"
|
|
21503
|
+
},
|
|
21504
|
+
{
|
|
21505
|
+
"type": "null"
|
|
21506
|
+
}
|
|
21507
|
+
],
|
|
21508
|
+
"description": "Initial triage priority, or `null`."
|
|
21509
|
+
},
|
|
21510
|
+
"status": {
|
|
21511
|
+
"anyOf": [
|
|
21512
|
+
{
|
|
21513
|
+
"$ref": "#/components/schemas/OpsItemStatus"
|
|
21514
|
+
}
|
|
21515
|
+
],
|
|
21516
|
+
"description": "Initial lifecycle status; defaults to `open` when omitted."
|
|
21517
|
+
},
|
|
21518
|
+
"assigneeId": {
|
|
21519
|
+
"anyOf": [
|
|
21520
|
+
{
|
|
21521
|
+
"type": "string"
|
|
21522
|
+
},
|
|
21523
|
+
{
|
|
21524
|
+
"type": "null"
|
|
21525
|
+
}
|
|
21526
|
+
],
|
|
21527
|
+
"description": "The `users.id` of the person to assign as owner at creation, or `null`."
|
|
21528
|
+
},
|
|
21529
|
+
"subscribers": {
|
|
21530
|
+
"type": "array",
|
|
21531
|
+
"items": {
|
|
21532
|
+
"type": "string",
|
|
21533
|
+
"format": "email"
|
|
21534
|
+
},
|
|
21535
|
+
"default": [],
|
|
21536
|
+
"description": "Emails of teammates to subscribe to this item's Slack pings at creation."
|
|
21537
|
+
},
|
|
21538
|
+
"tags": {
|
|
21539
|
+
"type": "array",
|
|
21540
|
+
"items": {
|
|
21541
|
+
"type": "string"
|
|
21542
|
+
},
|
|
21543
|
+
"default": [],
|
|
21544
|
+
"description": "Tag names to attach at creation (get-or-created by name, deduped case-insensitively)."
|
|
21545
|
+
},
|
|
21546
|
+
"reporterEmail": {
|
|
21547
|
+
"anyOf": [
|
|
21548
|
+
{
|
|
21549
|
+
"type": "string",
|
|
21550
|
+
"format": "email"
|
|
21551
|
+
},
|
|
21552
|
+
{
|
|
21553
|
+
"type": "null"
|
|
21554
|
+
}
|
|
21555
|
+
],
|
|
21556
|
+
"description": "Email of the reporter, or `null`."
|
|
21557
|
+
},
|
|
21558
|
+
"pageUrl": {
|
|
21559
|
+
"anyOf": [
|
|
21560
|
+
{
|
|
21561
|
+
"type": "string",
|
|
21562
|
+
"format": "uri"
|
|
21563
|
+
},
|
|
21564
|
+
{
|
|
21565
|
+
"type": "null"
|
|
21566
|
+
}
|
|
21567
|
+
],
|
|
21568
|
+
"description": "URL of the page the bug relates to, or `null`."
|
|
21569
|
+
},
|
|
21570
|
+
"userAgent": {
|
|
21571
|
+
"anyOf": [
|
|
21572
|
+
{
|
|
21573
|
+
"type": "string",
|
|
21574
|
+
"maxLength": 500
|
|
21575
|
+
},
|
|
21576
|
+
{
|
|
21577
|
+
"type": "null"
|
|
21578
|
+
}
|
|
21579
|
+
],
|
|
21580
|
+
"description": "Reporter's user-agent string, or `null`."
|
|
21581
|
+
},
|
|
21582
|
+
"viewport": {
|
|
21583
|
+
"anyOf": [
|
|
21584
|
+
{
|
|
21585
|
+
"type": "string",
|
|
21586
|
+
"maxLength": 40
|
|
21587
|
+
},
|
|
21588
|
+
{
|
|
21589
|
+
"type": "null"
|
|
21590
|
+
}
|
|
21591
|
+
],
|
|
21592
|
+
"description": "Reporter's viewport (e.g. `1280x720`), or `null`."
|
|
21593
|
+
},
|
|
21594
|
+
"context": {
|
|
21595
|
+
"anyOf": [
|
|
21596
|
+
{
|
|
21597
|
+
"type": "object",
|
|
21598
|
+
"additionalProperties": {}
|
|
21599
|
+
},
|
|
21600
|
+
{
|
|
21601
|
+
"type": "null"
|
|
21602
|
+
}
|
|
21603
|
+
],
|
|
21604
|
+
"description": "Arbitrary capture context, or `null`."
|
|
21605
|
+
},
|
|
21606
|
+
"notify": {
|
|
21607
|
+
"anyOf": [
|
|
21608
|
+
{
|
|
21609
|
+
"$ref": "#/components/schemas/NotificationTarget"
|
|
21610
|
+
},
|
|
21611
|
+
{
|
|
21612
|
+
"type": "null"
|
|
21613
|
+
}
|
|
21614
|
+
],
|
|
21615
|
+
"description": "Where this bug's completion notification lands."
|
|
21616
|
+
}
|
|
21617
|
+
},
|
|
21618
|
+
"required": [
|
|
21619
|
+
"type",
|
|
21620
|
+
"title"
|
|
21621
|
+
],
|
|
21622
|
+
"additionalProperties": false,
|
|
21623
|
+
"description": "Bug-kind fields for `POST /api/admin/ops` (sent with `type: bug`)."
|
|
21624
|
+
},
|
|
21625
|
+
"CreateFeatureRequestRequest": {
|
|
21626
|
+
"type": "object",
|
|
21627
|
+
"properties": {
|
|
21628
|
+
"type": {
|
|
21629
|
+
"type": "string",
|
|
21630
|
+
"enum": [
|
|
21631
|
+
"feature_request"
|
|
21632
|
+
],
|
|
21633
|
+
"description": "Discriminator — files a feature request."
|
|
21634
|
+
},
|
|
21635
|
+
"title": {
|
|
21636
|
+
"type": "string",
|
|
21637
|
+
"minLength": 1,
|
|
21638
|
+
"maxLength": 200,
|
|
21639
|
+
"pattern": "^\\S(.*\\S)?$",
|
|
21640
|
+
"description": "One-line feature-request title (no leading/trailing whitespace)."
|
|
21641
|
+
},
|
|
21642
|
+
"description": {
|
|
21643
|
+
"type": "string",
|
|
21644
|
+
"maxLength": 8000,
|
|
21645
|
+
"default": "",
|
|
21646
|
+
"description": "What the feature is."
|
|
21647
|
+
},
|
|
21648
|
+
"useCase": {
|
|
21649
|
+
"type": "string",
|
|
21650
|
+
"maxLength": 8000,
|
|
21651
|
+
"default": "",
|
|
21652
|
+
"description": "Why it's needed / the use case."
|
|
21653
|
+
},
|
|
21654
|
+
"priority": {
|
|
21655
|
+
"anyOf": [
|
|
21656
|
+
{
|
|
21657
|
+
"$ref": "#/components/schemas/OpsItemPriority"
|
|
21658
|
+
},
|
|
21659
|
+
{
|
|
21660
|
+
"type": "null"
|
|
21661
|
+
}
|
|
21662
|
+
],
|
|
21663
|
+
"description": "Initial triage priority, or `null`."
|
|
21664
|
+
},
|
|
21665
|
+
"status": {
|
|
21666
|
+
"anyOf": [
|
|
21667
|
+
{
|
|
21668
|
+
"$ref": "#/components/schemas/OpsItemStatus"
|
|
21669
|
+
}
|
|
21670
|
+
],
|
|
21671
|
+
"description": "Initial lifecycle status; defaults to `open` when omitted."
|
|
21672
|
+
},
|
|
21673
|
+
"assigneeId": {
|
|
21674
|
+
"anyOf": [
|
|
21675
|
+
{
|
|
21676
|
+
"type": "string"
|
|
21677
|
+
},
|
|
21678
|
+
{
|
|
21679
|
+
"type": "null"
|
|
21680
|
+
}
|
|
21681
|
+
],
|
|
21682
|
+
"description": "The `users.id` of the person to assign as owner at creation, or `null`."
|
|
21683
|
+
},
|
|
21684
|
+
"subscribers": {
|
|
21685
|
+
"type": "array",
|
|
21686
|
+
"items": {
|
|
21687
|
+
"type": "string",
|
|
21688
|
+
"format": "email"
|
|
20498
21689
|
},
|
|
20499
|
-
"
|
|
20500
|
-
|
|
20501
|
-
"description": "ISO-8601 last-update timestamp."
|
|
20502
|
-
}
|
|
21690
|
+
"default": [],
|
|
21691
|
+
"description": "Emails of teammates to subscribe to this item's Slack pings at creation."
|
|
20503
21692
|
},
|
|
20504
|
-
"
|
|
20505
|
-
"
|
|
20506
|
-
"
|
|
20507
|
-
|
|
20508
|
-
|
|
20509
|
-
"
|
|
20510
|
-
"
|
|
20511
|
-
|
|
20512
|
-
|
|
20513
|
-
"
|
|
20514
|
-
|
|
20515
|
-
|
|
20516
|
-
|
|
20517
|
-
|
|
20518
|
-
|
|
20519
|
-
|
|
21693
|
+
"tags": {
|
|
21694
|
+
"type": "array",
|
|
21695
|
+
"items": {
|
|
21696
|
+
"type": "string"
|
|
21697
|
+
},
|
|
21698
|
+
"default": [],
|
|
21699
|
+
"description": "Tag names to attach at creation (get-or-created by name, deduped case-insensitively)."
|
|
21700
|
+
},
|
|
21701
|
+
"reporterEmail": {
|
|
21702
|
+
"anyOf": [
|
|
21703
|
+
{
|
|
21704
|
+
"type": "string",
|
|
21705
|
+
"format": "email"
|
|
21706
|
+
},
|
|
21707
|
+
{
|
|
21708
|
+
"type": "null"
|
|
21709
|
+
}
|
|
21710
|
+
],
|
|
21711
|
+
"description": "Email of the reporter, or `null`."
|
|
21712
|
+
},
|
|
21713
|
+
"pageUrl": {
|
|
21714
|
+
"anyOf": [
|
|
21715
|
+
{
|
|
21716
|
+
"type": "string",
|
|
21717
|
+
"format": "uri"
|
|
21718
|
+
},
|
|
21719
|
+
{
|
|
21720
|
+
"type": "null"
|
|
21721
|
+
}
|
|
21722
|
+
],
|
|
21723
|
+
"description": "URL of the page the request relates to, or `null`."
|
|
21724
|
+
},
|
|
21725
|
+
"userAgent": {
|
|
21726
|
+
"anyOf": [
|
|
21727
|
+
{
|
|
21728
|
+
"type": "string",
|
|
21729
|
+
"maxLength": 500
|
|
21730
|
+
},
|
|
21731
|
+
{
|
|
21732
|
+
"type": "null"
|
|
21733
|
+
}
|
|
21734
|
+
],
|
|
21735
|
+
"description": "Reporter's user-agent string, or `null`."
|
|
21736
|
+
},
|
|
21737
|
+
"context": {
|
|
21738
|
+
"anyOf": [
|
|
21739
|
+
{
|
|
21740
|
+
"type": "object",
|
|
21741
|
+
"additionalProperties": {}
|
|
21742
|
+
},
|
|
21743
|
+
{
|
|
21744
|
+
"type": "null"
|
|
21745
|
+
}
|
|
21746
|
+
],
|
|
21747
|
+
"description": "Arbitrary capture context, or `null`."
|
|
21748
|
+
},
|
|
21749
|
+
"notify": {
|
|
21750
|
+
"anyOf": [
|
|
21751
|
+
{
|
|
21752
|
+
"$ref": "#/components/schemas/NotificationTarget"
|
|
21753
|
+
},
|
|
21754
|
+
{
|
|
21755
|
+
"type": "null"
|
|
21756
|
+
}
|
|
21757
|
+
],
|
|
21758
|
+
"description": "Where this request's completion notification lands."
|
|
21759
|
+
}
|
|
20520
21760
|
},
|
|
20521
|
-
"
|
|
21761
|
+
"required": [
|
|
21762
|
+
"type",
|
|
21763
|
+
"title"
|
|
21764
|
+
],
|
|
21765
|
+
"additionalProperties": false,
|
|
21766
|
+
"description": "Feature-request-kind fields for `POST /api/admin/ops` (sent with `type: feature_request`)."
|
|
20522
21767
|
},
|
|
20523
|
-
"
|
|
21768
|
+
"CreateOpsItemRequest": {
|
|
21769
|
+
"oneOf": [
|
|
21770
|
+
{
|
|
21771
|
+
"$ref": "#/components/schemas/CreateBugRequest"
|
|
21772
|
+
},
|
|
21773
|
+
{
|
|
21774
|
+
"$ref": "#/components/schemas/CreateFeatureRequestRequest"
|
|
21775
|
+
}
|
|
21776
|
+
],
|
|
21777
|
+
"discriminator": {
|
|
21778
|
+
"propertyName": "type",
|
|
21779
|
+
"mapping": {
|
|
21780
|
+
"bug": "#/components/schemas/CreateBugRequest",
|
|
21781
|
+
"feature_request": "#/components/schemas/CreateFeatureRequestRequest"
|
|
21782
|
+
}
|
|
21783
|
+
},
|
|
21784
|
+
"description": "Body for `POST /api/admin/ops`. A discriminated union on `type`: `bug` carries the bug fields, `feature_request` the feature fields. Only these two user-fileable types are accepted — `error`, `alert`, and `measure_plan` tickets are auto-filed by the platform and cannot be created over the API."
|
|
21785
|
+
},
|
|
21786
|
+
"CreateOpsItemResponse": {
|
|
20524
21787
|
"type": "object",
|
|
20525
21788
|
"properties": {
|
|
20526
|
-
"
|
|
21789
|
+
"id": {
|
|
20527
21790
|
"type": "string",
|
|
20528
|
-
"
|
|
20529
|
-
"bug"
|
|
20530
|
-
],
|
|
20531
|
-
"description": "Discriminator — files a bug."
|
|
21791
|
+
"description": "Newly created item id."
|
|
20532
21792
|
},
|
|
21793
|
+
"number": {
|
|
21794
|
+
"description": "Per-project item number assigned to the new item.",
|
|
21795
|
+
"anyOf": [
|
|
21796
|
+
{
|
|
21797
|
+
"type": "number"
|
|
21798
|
+
},
|
|
21799
|
+
{
|
|
21800
|
+
"type": "null"
|
|
21801
|
+
}
|
|
21802
|
+
]
|
|
21803
|
+
}
|
|
21804
|
+
},
|
|
21805
|
+
"required": [
|
|
21806
|
+
"id"
|
|
21807
|
+
],
|
|
21808
|
+
"additionalProperties": false,
|
|
21809
|
+
"description": "Response for `POST /api/admin/ops`."
|
|
21810
|
+
},
|
|
21811
|
+
"CreatePublicBugRequest": {
|
|
21812
|
+
"type": "object",
|
|
21813
|
+
"properties": {
|
|
20533
21814
|
"title": {
|
|
20534
21815
|
"type": "string",
|
|
20535
21816
|
"minLength": 1,
|
|
@@ -20675,22 +21956,36 @@
|
|
|
20675
21956
|
}
|
|
20676
21957
|
},
|
|
20677
21958
|
"required": [
|
|
20678
|
-
"type",
|
|
20679
21959
|
"title"
|
|
20680
21960
|
],
|
|
20681
21961
|
"additionalProperties": false,
|
|
20682
|
-
"description": "
|
|
21962
|
+
"description": "Body for `POST /ops/bug`. The same bug fields as `CreateBugRequest`, minus the `type` discriminator — the path already says what is being filed."
|
|
20683
21963
|
},
|
|
20684
|
-
"
|
|
21964
|
+
"CreatePublicTicketResponse": {
|
|
20685
21965
|
"type": "object",
|
|
20686
21966
|
"properties": {
|
|
20687
|
-
"
|
|
21967
|
+
"id": {
|
|
20688
21968
|
"type": "string",
|
|
20689
|
-
"
|
|
20690
|
-
|
|
20691
|
-
|
|
20692
|
-
"
|
|
21969
|
+
"description": "The new item's id. Absent on a deduped response — nothing was created, so there is no new id to report."
|
|
21970
|
+
},
|
|
21971
|
+
"number": {
|
|
21972
|
+
"type": "integer",
|
|
21973
|
+
"description": "Per-project item number. On a deduped response this is the number of the OPEN ticket already tracking the report, which is what a caller should show the user either way."
|
|
20693
21974
|
},
|
|
21975
|
+
"deduped": {
|
|
21976
|
+
"type": "boolean",
|
|
21977
|
+
"description": "`true` when an open ticket already tracked this report and nothing was filed (HTTP 200). Absent on a fresh file (HTTP 201)."
|
|
21978
|
+
}
|
|
21979
|
+
},
|
|
21980
|
+
"required": [
|
|
21981
|
+
"number"
|
|
21982
|
+
],
|
|
21983
|
+
"additionalProperties": false,
|
|
21984
|
+
"description": "Response for the public ticket intake. A fresh file returns `201` with `id` + `number`; a repeat of a report already tracked by an open ticket returns `200` with that ticket's `number` and `deduped: true`."
|
|
21985
|
+
},
|
|
21986
|
+
"CreatePublicFeatureRequestRequest": {
|
|
21987
|
+
"type": "object",
|
|
21988
|
+
"properties": {
|
|
20694
21989
|
"title": {
|
|
20695
21990
|
"type": "string",
|
|
20696
21991
|
"minLength": 1,
|
|
@@ -20818,54 +22113,10 @@
|
|
|
20818
22113
|
}
|
|
20819
22114
|
},
|
|
20820
22115
|
"required": [
|
|
20821
|
-
"type",
|
|
20822
22116
|
"title"
|
|
20823
22117
|
],
|
|
20824
22118
|
"additionalProperties": false,
|
|
20825
|
-
"description": "
|
|
20826
|
-
},
|
|
20827
|
-
"CreateOpsItemRequest": {
|
|
20828
|
-
"oneOf": [
|
|
20829
|
-
{
|
|
20830
|
-
"$ref": "#/components/schemas/CreateBugRequest"
|
|
20831
|
-
},
|
|
20832
|
-
{
|
|
20833
|
-
"$ref": "#/components/schemas/CreateFeatureRequestRequest"
|
|
20834
|
-
}
|
|
20835
|
-
],
|
|
20836
|
-
"discriminator": {
|
|
20837
|
-
"propertyName": "type",
|
|
20838
|
-
"mapping": {
|
|
20839
|
-
"bug": "#/components/schemas/CreateBugRequest",
|
|
20840
|
-
"feature_request": "#/components/schemas/CreateFeatureRequestRequest"
|
|
20841
|
-
}
|
|
20842
|
-
},
|
|
20843
|
-
"description": "Body for `POST /api/admin/ops`. A discriminated union on `type`: `bug` carries the bug fields, `feature_request` the feature fields. Only these two user-fileable types are accepted — `error`, `alert`, and `measure_plan` tickets are auto-filed by the platform and cannot be created over the API."
|
|
20844
|
-
},
|
|
20845
|
-
"CreateOpsItemResponse": {
|
|
20846
|
-
"type": "object",
|
|
20847
|
-
"properties": {
|
|
20848
|
-
"id": {
|
|
20849
|
-
"type": "string",
|
|
20850
|
-
"description": "Newly created item id."
|
|
20851
|
-
},
|
|
20852
|
-
"number": {
|
|
20853
|
-
"description": "Per-project item number assigned to the new item.",
|
|
20854
|
-
"anyOf": [
|
|
20855
|
-
{
|
|
20856
|
-
"type": "number"
|
|
20857
|
-
},
|
|
20858
|
-
{
|
|
20859
|
-
"type": "null"
|
|
20860
|
-
}
|
|
20861
|
-
]
|
|
20862
|
-
}
|
|
20863
|
-
},
|
|
20864
|
-
"required": [
|
|
20865
|
-
"id"
|
|
20866
|
-
],
|
|
20867
|
-
"additionalProperties": false,
|
|
20868
|
-
"description": "Response for `POST /api/admin/ops`."
|
|
22119
|
+
"description": "Body for `POST /ops/feature-request`. The same feature-request fields as `CreateFeatureRequestRequest`, minus the `type` discriminator — the path already says what is being filed."
|
|
20869
22120
|
},
|
|
20870
22121
|
"OpsItemAttachment": {
|
|
20871
22122
|
"type": "object",
|
|
@@ -25742,8 +26993,8 @@
|
|
|
25742
26993
|
"name": {
|
|
25743
26994
|
"type": "string",
|
|
25744
26995
|
"minLength": 1,
|
|
25745
|
-
"maxLength":
|
|
25746
|
-
"description": "Optional human label. Programmatic (API) mints that omit it get an auto-generated descriptive name; dashboard mints may leave it blank."
|
|
26996
|
+
"maxLength": 160,
|
|
26997
|
+
"description": "Optional human label. Programmatic (API) mints that omit it get an auto-generated descriptive name; dashboard mints may leave it blank. The cap is generous because provenance labels are composed, not typed — `shipeasy setup` mints keys named with the stack, package, date, and operator that produced them, which does not fit in a one-line-input budget."
|
|
25747
26998
|
},
|
|
25748
26999
|
"scopes": {
|
|
25749
27000
|
"type": "array",
|
|
@@ -26090,6 +27341,62 @@
|
|
|
26090
27341
|
}
|
|
26091
27342
|
}
|
|
26092
27343
|
},
|
|
27344
|
+
"PayloadTooLarge": {
|
|
27345
|
+
"description": "The request body exceeded the intake's fixed size cap (16 KB).",
|
|
27346
|
+
"content": {
|
|
27347
|
+
"application/json": {
|
|
27348
|
+
"schema": {
|
|
27349
|
+
"$ref": "#/components/schemas/Error"
|
|
27350
|
+
},
|
|
27351
|
+
"examples": {
|
|
27352
|
+
"BAD_REQUEST": {
|
|
27353
|
+
"summary": "Body too large",
|
|
27354
|
+
"value": {
|
|
27355
|
+
"error": "Body too large",
|
|
27356
|
+
"code": "BAD_REQUEST"
|
|
27357
|
+
}
|
|
27358
|
+
}
|
|
27359
|
+
}
|
|
27360
|
+
}
|
|
27361
|
+
}
|
|
27362
|
+
},
|
|
27363
|
+
"TooManyRequests": {
|
|
27364
|
+
"description": "The public intake is rate-limited per (project, client IP). Back off and retry;\nthe limit is a burst guard, not a quota, so a modest delay clears it.",
|
|
27365
|
+
"content": {
|
|
27366
|
+
"application/json": {
|
|
27367
|
+
"schema": {
|
|
27368
|
+
"$ref": "#/components/schemas/Error"
|
|
27369
|
+
},
|
|
27370
|
+
"examples": {
|
|
27371
|
+
"BAD_REQUEST": {
|
|
27372
|
+
"summary": "Rate limited",
|
|
27373
|
+
"value": {
|
|
27374
|
+
"error": "Too many requests",
|
|
27375
|
+
"code": "BAD_REQUEST"
|
|
27376
|
+
}
|
|
27377
|
+
}
|
|
27378
|
+
}
|
|
27379
|
+
}
|
|
27380
|
+
}
|
|
27381
|
+
},
|
|
27382
|
+
"ServiceUnavailable": {
|
|
27383
|
+
"description": "A backing store the endpoint needs is not bound in this environment. Transient\nand infrastructural — no stable `ErrorCode` is assigned, so `code` is omitted.",
|
|
27384
|
+
"content": {
|
|
27385
|
+
"application/json": {
|
|
27386
|
+
"schema": {
|
|
27387
|
+
"$ref": "#/components/schemas/Error"
|
|
27388
|
+
},
|
|
27389
|
+
"examples": {
|
|
27390
|
+
"UNAVAILABLE": {
|
|
27391
|
+
"summary": "Storage unbound",
|
|
27392
|
+
"value": {
|
|
27393
|
+
"error": "Unavailable"
|
|
27394
|
+
}
|
|
27395
|
+
}
|
|
27396
|
+
}
|
|
27397
|
+
}
|
|
27398
|
+
}
|
|
27399
|
+
},
|
|
26093
27400
|
"BadGateway": {
|
|
26094
27401
|
"description": "The request was well-formed and the resource exists, but a downstream provider\nthe endpoint delegates to (e.g. a connector's Slack/GitHub/cloud-agent dispatch)\nfailed. The failure is upstream of this API, not a client error. No stable\n`ErrorCode` is assigned to upstream failures, so `code` is omitted.",
|
|
26095
27402
|
"content": {
|