@shipeasy/openapi 2.1.0 → 2.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/dist/{chunk-EPLLHLZN.js → chunk-LYNWK55D.js} +23 -1
  2. package/dist/chunk-LYNWK55D.js.map +1 -0
  3. package/dist/{chunk-SPH5HWCD.js → chunk-PRN4AEAM.js} +396 -187
  4. package/dist/chunk-PRN4AEAM.js.map +1 -0
  5. package/dist/{client-pVkoo3Bf.d.ts → client-DO5HTb_L.d.ts} +886 -241
  6. package/dist/client.d.ts +1 -1
  7. package/dist/client.js +7 -1
  8. package/dist/custom.d.ts +23 -1
  9. package/dist/custom.js +12 -4
  10. package/dist/custom.js.map +1 -1
  11. package/dist/index.d.ts +2 -2
  12. package/dist/index.js +8 -2
  13. package/dist/index.js.map +1 -1
  14. package/dist/schemas.d.ts +770 -104
  15. package/dist/schemas.js +57 -5
  16. package/openapi.json +2177 -775
  17. package/openapi.yaml +1373 -409
  18. package/package.json +1 -1
  19. package/spec/components/errors.yaml +30 -1
  20. package/spec/components/parameters.yaml +8 -0
  21. package/spec/components/responses.yaml +15 -0
  22. package/spec/components/schemas/common.yaml +11 -0
  23. package/spec/components/schemas/connectors.yaml +5 -11
  24. package/spec/components/schemas/errors-api.yaml +66 -3
  25. package/spec/components/schemas/experiment-api-row.yaml +293 -0
  26. package/spec/components/schemas/experiments.yaml +54 -299
  27. package/spec/components/schemas/gate-templates.yaml +41 -2
  28. package/spec/components/schemas/gates.yaml +19 -0
  29. package/spec/components/schemas/keys.yaml +2 -1
  30. package/spec/components/schemas/metrics.yaml +17 -17
  31. package/spec/components/schemas/ops.yaml +105 -2
  32. package/spec/components/schemas/projects.yaml +119 -1
  33. package/spec/components/schemas/universes.yaml +83 -1
  34. package/spec/openapi.yaml +35 -2
  35. package/spec/paths/alert-rules.yaml +11 -2
  36. package/spec/paths/attributes.yaml +14 -3
  37. package/spec/paths/configs.yaml +24 -9
  38. package/spec/paths/connectors.yaml +42 -8
  39. package/spec/paths/errors.yaml +100 -6
  40. package/spec/paths/events.yaml +19 -4
  41. package/spec/paths/experiments.yaml +45 -11
  42. package/spec/paths/gate-templates.yaml +9 -5
  43. package/spec/paths/gates.yaml +21 -6
  44. package/spec/paths/i18n.yaml +18 -5
  45. package/spec/paths/keys.yaml +3 -1
  46. package/spec/paths/killswitches.yaml +24 -6
  47. package/spec/paths/metrics.yaml +15 -3
  48. package/spec/paths/ops.yaml +114 -3
  49. package/spec/paths/projects.yaml +3 -3
  50. package/spec/paths/universes.yaml +11 -2
  51. package/dist/chunk-EPLLHLZN.js.map +0 -1
  52. package/dist/chunk-SPH5HWCD.js.map +0 -1
package/openapi.yaml CHANGED
@@ -21,9 +21,26 @@ security:
21
21
  - bearerSdkKey: []
22
22
  tags:
23
23
  - name: Release
24
- summary: Feature delivery
24
+ summary: Feature delivery — flags, kill switches, dynamic configs, A/B experiments, and the universes they bucket in.
25
25
  kind: nav
26
- description: Flags, kill switches, dynamic configs, experiments, and the universes they bucket in — the feature-delivery surface.
26
+ description: |-
27
+ Release: the feature-delivery surface. One roof over the five resources
28
+ that decide which code path a user gets — feature gates (flags), kill
29
+ switches, dynamic configs, A/B/n experiments, and the universes that bucket
30
+ them — each exposed as its own subcommand group.
31
+
32
+ **Groups.**
33
+ - `flags` — boolean feature gates: AND-combined targeting rules + a percentage rollout (basis points), evaluated on the SDK hot path.
34
+ - `killswitch` (`ks`) — per-env boolean overrides for incident response; no rules, no rollout, versioned per environment.
35
+ - `configs` — JSON-Schema-validated structured values with a draft → publish workflow, delivered per environment.
36
+ - `experiments` — randomised A/B/n assignment plus the analysis pipeline (t-test, sequential testing, SRM detection); `universes` hold the shared bucketing space and holdouts the experiments draw from.
37
+
38
+ **Delivery model.** Every write rebuilds the project's KV blob and purges
39
+ the CDN — SDKs poll at the plan interval and never touch the database on
40
+ the hot path. The destructive verb is `archive` (soft-delete), never
41
+ `delete`.
42
+
43
+ Run `shipeasy release <group> --help` for the full contract of each group.
27
44
  - name: Flags
28
45
  parent: Release
29
46
  description: |-
@@ -161,6 +178,16 @@ tags:
161
178
  **Immutable metric.** The bound metric (and its aggregation) is fixed at create time — there is no update path for `metricId`. Tune `threshold`/`comparator`/`windowHours`/`severity`/`name`/`enabled` instead, or delete + recreate to repoint the rule at a different metric.
162
179
 
163
180
  **Delivery.** `notify` optionally targets a Slack channel and/or email for this rule; `null` falls back to the project's default notification settings. Slack targets require a connected Slack connector.
181
+ - name: Comments
182
+ parent: Ops
183
+ description: |-
184
+ Comments on a queue item — the discussion thread that hangs off any bug,
185
+ feature request, or auto-filed error/alert ticket. Append a comment or read
186
+ the thread; one level of threaded replies via `parentId`.
187
+
188
+ **Mentions.** `@teammate` in a body raises an in-app notification for that
189
+ person; `@shipeasy` asks Jarvis (the AI agent) to read the item and reply.
190
+ Comments authored by Jarvis carry `authorType: system`.
164
191
  - name: Projects
165
192
  description: |-
166
193
  Projects: the account-level container every other resource is scoped to.
@@ -229,6 +256,7 @@ paths:
229
256
  - $ref: '#/components/parameters/ProjectId'
230
257
  - $ref: '#/components/parameters/PaginationLimit'
231
258
  - $ref: '#/components/parameters/PaginationCursor'
259
+ - $ref: '#/components/parameters/Q'
232
260
  responses:
233
261
  '200':
234
262
  description: List feature gates
@@ -380,6 +408,23 @@ paths:
380
408
  example:
381
409
  id: gat_01j7w7m9q4hxbf6npe6s9zr3vc
382
410
  name: checkout_v2
411
+ links:
412
+ UpdateGate:
413
+ operationId: updateGate
414
+ parameters:
415
+ id: $response.body#/id
416
+ EnableGate:
417
+ operationId: enableGate
418
+ parameters:
419
+ id: $response.body#/id
420
+ DisableGate:
421
+ operationId: disableGate
422
+ parameters:
423
+ id: $response.body#/id
424
+ DeleteGate:
425
+ operationId: deleteGate
426
+ parameters:
427
+ id: $response.body#/id
383
428
  '400':
384
429
  $ref: '#/components/responses/BadRequest'
385
430
  '401':
@@ -398,6 +443,7 @@ paths:
398
443
  - FORBIDDEN
399
444
  - ALREADY_EXISTS
400
445
  - VALIDATION
446
+ - PLAN_LIMIT
401
447
  x-cli:
402
448
  name: create
403
449
  positional:
@@ -428,7 +474,7 @@ paths:
428
474
  required: true
429
475
  description: Stable opaque gate id (`gat_…`) or the gate's `name`.
430
476
  schema:
431
- type: string
477
+ $ref: '#/components/schemas/ResourceId'
432
478
  requestBody:
433
479
  required: true
434
480
  content:
@@ -534,7 +580,7 @@ paths:
534
580
  required: true
535
581
  description: Stable opaque gate id (`gat_…`) or the gate's `name`.
536
582
  schema:
537
- type: string
583
+ $ref: '#/components/schemas/ResourceId'
538
584
  responses:
539
585
  '200':
540
586
  description: Delete a feature gate
@@ -583,9 +629,9 @@ paths:
583
629
  required: true
584
630
  description: Stable opaque gate id (`gat_…`) or the gate's `name`.
585
631
  schema:
586
- type: string
632
+ $ref: '#/components/schemas/ResourceId'
587
633
  responses:
588
- '201':
634
+ '200':
589
635
  description: Enable a gate
590
636
  content:
591
637
  application/json:
@@ -633,9 +679,9 @@ paths:
633
679
  required: true
634
680
  description: Stable opaque gate id (`gat_…`) or the gate's `name`.
635
681
  schema:
636
- type: string
682
+ $ref: '#/components/schemas/ResourceId'
637
683
  responses:
638
- '201':
684
+ '200':
639
685
  description: Disable a gate
640
686
  content:
641
687
  application/json:
@@ -680,6 +726,13 @@ paths:
680
726
  - $ref: '#/components/parameters/ProjectId'
681
727
  - $ref: '#/components/parameters/PaginationLimit'
682
728
  - $ref: '#/components/parameters/PaginationCursor'
729
+ - name: status
730
+ in: query
731
+ required: false
732
+ description: Filter by lifecycle status. Pass `archived` to return the archive tab; any other value (or omitting it) returns the non-archived experiments.
733
+ schema:
734
+ type: string
735
+ - $ref: '#/components/parameters/Q'
683
736
  responses:
684
737
  '200':
685
738
  description: List experiments
@@ -827,6 +880,31 @@ paths:
827
880
  example:
828
881
  id: exp_01j7wb12c3d4e5f6g7h8j9k0l1
829
882
  name: checkout_button_color
883
+ links:
884
+ GetExperiment:
885
+ operationId: getExperiment
886
+ parameters:
887
+ id: $response.body#/id
888
+ UpdateExperiment:
889
+ operationId: updateExperiment
890
+ parameters:
891
+ id: $response.body#/id
892
+ SetExperimentMetrics:
893
+ operationId: setExperimentMetrics
894
+ parameters:
895
+ id: $response.body#/id
896
+ SetExperimentStatus:
897
+ operationId: setExperimentStatus
898
+ parameters:
899
+ id: $response.body#/id
900
+ ReanalyzeExperiment:
901
+ operationId: reanalyzeExperiment
902
+ parameters:
903
+ id: $response.body#/id
904
+ DeleteExperiment:
905
+ operationId: deleteExperiment
906
+ parameters:
907
+ id: $response.body#/id
830
908
  '400':
831
909
  $ref: '#/components/responses/BadRequest'
832
910
  '401':
@@ -869,14 +947,14 @@ paths:
869
947
  required: true
870
948
  description: Stable opaque experiment id (`exp_…`) or the experiment's `name`.
871
949
  schema:
872
- type: string
950
+ $ref: '#/components/schemas/ResourceId'
873
951
  responses:
874
952
  '200':
875
953
  description: Get one experiment
876
954
  content:
877
955
  application/json:
878
956
  schema:
879
- $ref: '#/components/schemas/GetExperimentResponse'
957
+ $ref: '#/components/schemas/ExperimentApiRow'
880
958
  example:
881
959
  id: exp_01j7wb12c3d4e5f6g7h8j9k0l1
882
960
  name: checkout_button_color
@@ -949,7 +1027,7 @@ paths:
949
1027
  required: true
950
1028
  description: Stable opaque experiment id (`exp_…`) or the experiment's `name`.
951
1029
  schema:
952
- type: string
1030
+ $ref: '#/components/schemas/ResourceId'
953
1031
  requestBody:
954
1032
  required: true
955
1033
  content:
@@ -1014,6 +1092,7 @@ paths:
1014
1092
  - GROUPS_WEIGHT_SUM
1015
1093
  - REFERENCED_NOT_FOUND
1016
1094
  - VALIDATION
1095
+ - EXPERIMENT_IMMUTABLE_FIELD
1017
1096
  x-cli:
1018
1097
  name: update
1019
1098
  positional:
@@ -1034,7 +1113,7 @@ paths:
1034
1113
  required: true
1035
1114
  description: Stable opaque experiment id (`exp_…`) or the experiment's `name`.
1036
1115
  schema:
1037
- type: string
1116
+ $ref: '#/components/schemas/ResourceId'
1038
1117
  responses:
1039
1118
  '200':
1040
1119
  description: Delete an experiment
@@ -1095,7 +1174,7 @@ paths:
1095
1174
  required: true
1096
1175
  description: Stable opaque experiment id (`exp_…`) or the experiment's `name`.
1097
1176
  schema:
1098
- type: string
1177
+ $ref: '#/components/schemas/ResourceId'
1099
1178
  requestBody:
1100
1179
  required: true
1101
1180
  content:
@@ -1124,7 +1203,7 @@ paths:
1124
1203
  value:
1125
1204
  status: draft
1126
1205
  responses:
1127
- '201':
1206
+ '200':
1128
1207
  description: Transition experiment status
1129
1208
  content:
1130
1209
  application/json:
@@ -1152,6 +1231,12 @@ paths:
1152
1231
  - NOT_FOUND
1153
1232
  - INVALID_TRANSITION
1154
1233
  - VALIDATION
1234
+ - PLAN_LIMIT
1235
+ - EXPERIMENT_NO_GOAL_METRIC
1236
+ - EXPERIMENT_ARCHIVED_RESTART
1237
+ - EXPERIMENT_NOT_RUNNING
1238
+ - EXPERIMENT_RUNNING_ARCHIVE
1239
+ - EXPERIMENT_RESTORE_INVALID
1155
1240
  x-cli:
1156
1241
  positional:
1157
1242
  - id
@@ -1190,7 +1275,7 @@ paths:
1190
1275
  required: true
1191
1276
  description: Stable opaque experiment id (`exp_…`) or the experiment's `name`.
1192
1277
  schema:
1193
- type: string
1278
+ $ref: '#/components/schemas/ResourceId'
1194
1279
  requestBody:
1195
1280
  required: true
1196
1281
  content:
@@ -1213,7 +1298,7 @@ paths:
1213
1298
  value:
1214
1299
  metrics: []
1215
1300
  responses:
1216
- '201':
1301
+ '200':
1217
1302
  description: Attach metrics
1218
1303
  content:
1219
1304
  application/json:
@@ -1246,6 +1331,7 @@ paths:
1246
1331
  - EVENT_PENDING
1247
1332
  - REFERENCED_NOT_FOUND
1248
1333
  - VALIDATION
1334
+ - METRIC_UNKNOWN_ID
1249
1335
  x-cli:
1250
1336
  name: set-metrics
1251
1337
  positional:
@@ -1267,7 +1353,7 @@ paths:
1267
1353
  required: true
1268
1354
  description: Stable opaque experiment id (`exp_…`) or the experiment's `name`.
1269
1355
  schema:
1270
- type: string
1356
+ $ref: '#/components/schemas/ResourceId'
1271
1357
  responses:
1272
1358
  '200':
1273
1359
  description: Get analysis results
@@ -1336,7 +1422,7 @@ paths:
1336
1422
  required: true
1337
1423
  description: Stable opaque experiment id (`exp_…`) or the experiment's `name`.
1338
1424
  schema:
1339
- type: string
1425
+ $ref: '#/components/schemas/ResourceId'
1340
1426
  - name: metric
1341
1427
  in: query
1342
1428
  required: false
@@ -1410,9 +1496,9 @@ paths:
1410
1496
  required: true
1411
1497
  description: Stable opaque experiment id (`exp_…`) or the experiment's `name`.
1412
1498
  schema:
1413
- type: string
1499
+ $ref: '#/components/schemas/ResourceId'
1414
1500
  responses:
1415
- '201':
1501
+ '200':
1416
1502
  description: Re-queue analysis
1417
1503
  content:
1418
1504
  application/json:
@@ -1457,6 +1543,7 @@ paths:
1457
1543
  - $ref: '#/components/parameters/ProjectId'
1458
1544
  - $ref: '#/components/parameters/PaginationLimit'
1459
1545
  - $ref: '#/components/parameters/PaginationCursor'
1546
+ - $ref: '#/components/parameters/Q'
1460
1547
  responses:
1461
1548
  '200':
1462
1549
  description: List dynamic configs
@@ -1605,6 +1692,23 @@ paths:
1605
1692
  example:
1606
1693
  id: cfg_01j7wae5h6j7k8l9m0n1p2q3r4
1607
1694
  name: pricing.tiers
1695
+ links:
1696
+ GetConfig:
1697
+ operationId: getConfig
1698
+ parameters:
1699
+ id: $response.body#/id
1700
+ UpdateConfig:
1701
+ operationId: updateConfig
1702
+ parameters:
1703
+ id: $response.body#/id
1704
+ PublishConfigDraft:
1705
+ operationId: publishConfigDraft
1706
+ parameters:
1707
+ id: $response.body#/id
1708
+ DeleteConfig:
1709
+ operationId: deleteConfig
1710
+ parameters:
1711
+ id: $response.body#/id
1608
1712
  '400':
1609
1713
  $ref: '#/components/responses/BadRequest'
1610
1714
  '401':
@@ -1623,6 +1727,7 @@ paths:
1623
1727
  - FORBIDDEN
1624
1728
  - ALREADY_EXISTS
1625
1729
  - VALIDATION
1730
+ - PLAN_LIMIT
1626
1731
  x-cli:
1627
1732
  name: create
1628
1733
  positional:
@@ -1644,7 +1749,7 @@ paths:
1644
1749
  required: true
1645
1750
  description: Stable opaque config id (`cfg_…`) or the config's `name`.
1646
1751
  schema:
1647
- type: string
1752
+ $ref: '#/components/schemas/ResourceId'
1648
1753
  responses:
1649
1754
  '200':
1650
1755
  description: Get one config
@@ -1746,7 +1851,7 @@ paths:
1746
1851
  required: true
1747
1852
  description: Stable opaque config id (`cfg_…`) or the config's `name`.
1748
1853
  schema:
1749
- type: string
1854
+ $ref: '#/components/schemas/ResourceId'
1750
1855
  requestBody:
1751
1856
  required: true
1752
1857
  content:
@@ -1822,7 +1927,7 @@ paths:
1822
1927
  required: true
1823
1928
  description: Stable opaque config id (`cfg_…`) or the config's `name`.
1824
1929
  schema:
1825
- type: string
1930
+ $ref: '#/components/schemas/ResourceId'
1826
1931
  responses:
1827
1932
  '200':
1828
1933
  description: Delete a dynamic config
@@ -1872,7 +1977,7 @@ paths:
1872
1977
  required: true
1873
1978
  description: Stable opaque config id (`cfg_…`) or the config's `name`.
1874
1979
  schema:
1875
- type: string
1980
+ $ref: '#/components/schemas/ResourceId'
1876
1981
  requestBody:
1877
1982
  required: true
1878
1983
  content:
@@ -1940,7 +2045,7 @@ paths:
1940
2045
  required: true
1941
2046
  description: Stable opaque config id (`cfg_…`) or the config's `name`.
1942
2047
  schema:
1943
- type: string
2048
+ $ref: '#/components/schemas/ResourceId'
1944
2049
  requestBody:
1945
2050
  required: true
1946
2051
  content:
@@ -2002,7 +2107,7 @@ paths:
2002
2107
  required: true
2003
2108
  description: Stable opaque config id (`cfg_…`) or the config's `name`.
2004
2109
  schema:
2005
- type: string
2110
+ $ref: '#/components/schemas/ResourceId'
2006
2111
  requestBody:
2007
2112
  required: true
2008
2113
  content:
@@ -2021,7 +2126,7 @@ paths:
2021
2126
  value:
2022
2127
  env: prod
2023
2128
  responses:
2024
- '201':
2129
+ '200':
2025
2130
  description: Publish a draft
2026
2131
  content:
2027
2132
  application/json:
@@ -2071,7 +2176,7 @@ paths:
2071
2176
  required: true
2072
2177
  description: Stable opaque config id (`cfg_…`) or the config's `name`.
2073
2178
  schema:
2074
- type: string
2179
+ $ref: '#/components/schemas/ResourceId'
2075
2180
  - name: limit
2076
2181
  in: query
2077
2182
  required: false
@@ -2135,7 +2240,7 @@ paths:
2135
2240
  required: true
2136
2241
  description: Stable opaque config id (`cfg_…`) or the config's `name`.
2137
2242
  schema:
2138
- type: string
2243
+ $ref: '#/components/schemas/ResourceId'
2139
2244
  requestBody:
2140
2245
  required: true
2141
2246
  content:
@@ -2201,6 +2306,7 @@ paths:
2201
2306
  - $ref: '#/components/parameters/ProjectId'
2202
2307
  - $ref: '#/components/parameters/PaginationLimit'
2203
2308
  - $ref: '#/components/parameters/PaginationCursor'
2309
+ - $ref: '#/components/parameters/Q'
2204
2310
  responses:
2205
2311
  '200':
2206
2312
  description: List killswitches
@@ -2303,6 +2409,27 @@ paths:
2303
2409
  example:
2304
2410
  id: ksw_01j7w9d8h2k4m6n8p0q2r4s6t8
2305
2411
  name: payments.checkout
2412
+ links:
2413
+ GetKillswitch:
2414
+ operationId: getKillswitch
2415
+ parameters:
2416
+ id: $response.body#/id
2417
+ UpdateKillswitch:
2418
+ operationId: updateKillswitch
2419
+ parameters:
2420
+ id: $response.body#/id
2421
+ SetKillswitchSwitch:
2422
+ operationId: setKillswitchSwitch
2423
+ parameters:
2424
+ id: $response.body#/id
2425
+ SetKillswitchValue:
2426
+ operationId: setKillswitchValue
2427
+ parameters:
2428
+ id: $response.body#/id
2429
+ DeleteKillswitch:
2430
+ operationId: deleteKillswitch
2431
+ parameters:
2432
+ id: $response.body#/id
2306
2433
  '400':
2307
2434
  $ref: '#/components/responses/BadRequest'
2308
2435
  '401':
@@ -2321,6 +2448,7 @@ paths:
2321
2448
  - FORBIDDEN
2322
2449
  - ALREADY_EXISTS
2323
2450
  - VALIDATION
2451
+ - PLAN_LIMIT
2324
2452
  x-cli:
2325
2453
  name: create
2326
2454
  positional:
@@ -2342,7 +2470,7 @@ paths:
2342
2470
  required: true
2343
2471
  description: Stable opaque killswitch id (`ksw_…`) or the killswitch's `name`.
2344
2472
  schema:
2345
- type: string
2473
+ $ref: '#/components/schemas/ResourceId'
2346
2474
  responses:
2347
2475
  '200':
2348
2476
  description: Get one killswitch
@@ -2414,7 +2542,7 @@ paths:
2414
2542
  required: true
2415
2543
  description: Stable opaque killswitch id (`ksw_…`) or the killswitch's `name`.
2416
2544
  schema:
2417
- type: string
2545
+ $ref: '#/components/schemas/ResourceId'
2418
2546
  requestBody:
2419
2547
  required: true
2420
2548
  content:
@@ -2491,7 +2619,7 @@ paths:
2491
2619
  required: true
2492
2620
  description: Stable opaque killswitch id (`ksw_…`) or the killswitch's `name`.
2493
2621
  schema:
2494
- type: string
2622
+ $ref: '#/components/schemas/ResourceId'
2495
2623
  responses:
2496
2624
  '200':
2497
2625
  description: Delete a killswitch
@@ -2544,7 +2672,7 @@ paths:
2544
2672
  required: true
2545
2673
  description: Stable opaque killswitch id (`ksw_…`) or the killswitch's `name`.
2546
2674
  schema:
2547
- type: string
2675
+ $ref: '#/components/schemas/ResourceId'
2548
2676
  requestBody:
2549
2677
  required: true
2550
2678
  content:
@@ -2618,7 +2746,7 @@ paths:
2618
2746
  required: true
2619
2747
  description: Stable opaque killswitch id (`ksw_…`) or the killswitch's `name`.
2620
2748
  schema:
2621
- type: string
2749
+ $ref: '#/components/schemas/ResourceId'
2622
2750
  requestBody:
2623
2751
  required: true
2624
2752
  content:
@@ -2687,7 +2815,7 @@ paths:
2687
2815
  required: true
2688
2816
  description: Stable opaque killswitch id (`ksw_…`) or the killswitch's `name`.
2689
2817
  schema:
2690
- type: string
2818
+ $ref: '#/components/schemas/ResourceId'
2691
2819
  requestBody:
2692
2820
  required: true
2693
2821
  content:
@@ -2758,6 +2886,7 @@ paths:
2758
2886
  - $ref: '#/components/parameters/ProjectId'
2759
2887
  - $ref: '#/components/parameters/PaginationLimit'
2760
2888
  - $ref: '#/components/parameters/PaginationCursor'
2889
+ - $ref: '#/components/parameters/Q'
2761
2890
  responses:
2762
2891
  '200':
2763
2892
  description: List universes
@@ -2846,6 +2975,15 @@ paths:
2846
2975
  example:
2847
2976
  id: uni_01j7w8a1b2c3d4e5f6g7h8i9j0
2848
2977
  name: primary_users
2978
+ links:
2979
+ UpdateUniverse:
2980
+ operationId: updateUniverse
2981
+ parameters:
2982
+ id: $response.body#/id
2983
+ DeleteUniverse:
2984
+ operationId: deleteUniverse
2985
+ parameters:
2986
+ id: $response.body#/id
2849
2987
  '400':
2850
2988
  $ref: '#/components/responses/BadRequest'
2851
2989
  '401':
@@ -2865,6 +3003,7 @@ paths:
2865
3003
  - PLAN_REQUIRED
2866
3004
  - ALREADY_EXISTS
2867
3005
  - VALIDATION
3006
+ - PLAN_LIMIT
2868
3007
  x-cli:
2869
3008
  name: create
2870
3009
  positional:
@@ -2891,7 +3030,7 @@ paths:
2891
3030
  required: true
2892
3031
  description: Stable opaque universe id (`uni_…`) or the universe's `name`.
2893
3032
  schema:
2894
- type: string
3033
+ $ref: '#/components/schemas/ResourceId'
2895
3034
  requestBody:
2896
3035
  required: true
2897
3036
  content:
@@ -2959,7 +3098,7 @@ paths:
2959
3098
  required: true
2960
3099
  description: Stable opaque universe id (`uni_…`) or the universe's `name`.
2961
3100
  schema:
2962
- type: string
3101
+ $ref: '#/components/schemas/ResourceId'
2963
3102
  responses:
2964
3103
  '200':
2965
3104
  description: Delete a universe
@@ -2998,17 +3137,19 @@ paths:
2998
3137
  description: |-
2999
3138
  Returns the merged targeting-rule template catalog — read-only **built-ins first** (the `@team`/`@owner` audience aliases, country, email-domain, region presets, …), then this project's **customer** templates. Each item carries a `rules: [{ attr, op, value }]` definition; the alias templates carry a symbol value (`email in ["@team"]`) that the SDK expands to the resolved email list at rebuild.
3000
3139
 
3001
- **Turn "launch X in country Y" into a gated flag:** list templates (optionally with `query`), take the matching template's `rules`, substitute the concrete value(s), and pass them as the `rules` arg of `release_flags_create`.
3140
+ **Turn "launch X in country Y" into a gated flag:** list templates (optionally with `q`), take the matching template's `rules`, substitute the concrete value(s), and pass them as the `rules` arg of `release_flags_create`.
3002
3141
 
3003
3142
  The catalog is small and bounded, so the full merged set is returned in one page — this endpoint is **not** paginated (`next_cursor` is always `null`; there are no `limit`/`cursor` params).
3004
3143
  tags:
3005
3144
  - Templates
3006
3145
  parameters:
3007
3146
  - $ref: '#/components/parameters/ProjectId'
3147
+ - $ref: '#/components/parameters/Q'
3008
3148
  - name: query
3009
3149
  in: query
3010
3150
  required: false
3011
- description: Case-insensitive substring filter over each template's `name` + `description`. Omit to return the whole catalog.
3151
+ deprecated: true
3152
+ description: Deprecated alias for `q`, kept working for one release. Prefer `q`.
3012
3153
  schema:
3013
3154
  type: string
3014
3155
  responses:
@@ -3120,7 +3261,7 @@ paths:
3120
3261
  required: true
3121
3262
  description: Built-in slug (`country`) or customer template id (`gtpl_…`).
3122
3263
  schema:
3123
- type: string
3264
+ $ref: '#/components/schemas/ResourceId'
3124
3265
  responses:
3125
3266
  '200':
3126
3267
  description: Get one gate template
@@ -3158,7 +3299,7 @@ paths:
3158
3299
  required: true
3159
3300
  description: Customer template id (`gtpl_…`) or its `name`.
3160
3301
  schema:
3161
- type: string
3302
+ $ref: '#/components/schemas/ResourceId'
3162
3303
  requestBody:
3163
3304
  required: true
3164
3305
  content:
@@ -3221,7 +3362,7 @@ paths:
3221
3362
  required: true
3222
3363
  description: Customer template id (`gtpl_…`) or its `name`.
3223
3364
  schema:
3224
- type: string
3365
+ $ref: '#/components/schemas/ResourceId'
3225
3366
  responses:
3226
3367
  '200':
3227
3368
  description: Delete a gate template
@@ -3241,6 +3382,8 @@ paths:
3241
3382
  $ref: '#/components/responses/NotFound'
3242
3383
  '409':
3243
3384
  $ref: '#/components/responses/Conflict'
3385
+ '422':
3386
+ $ref: '#/components/responses/UnprocessableEntity'
3244
3387
  x-error-codes:
3245
3388
  - BAD_REQUEST
3246
3389
  - UNAUTHORIZED
@@ -3263,6 +3406,7 @@ paths:
3263
3406
  - Attributes
3264
3407
  parameters:
3265
3408
  - $ref: '#/components/parameters/ProjectId'
3409
+ - $ref: '#/components/parameters/Q'
3266
3410
  responses:
3267
3411
  '200':
3268
3412
  description: List targeting attributes
@@ -3329,6 +3473,19 @@ paths:
3329
3473
  example:
3330
3474
  id: attr_01j7w8a1b2c3
3331
3475
  name: plan
3476
+ links:
3477
+ GetAttribute:
3478
+ operationId: getAttribute
3479
+ parameters:
3480
+ id: $response.body#/id
3481
+ UpdateAttribute:
3482
+ operationId: updateAttribute
3483
+ parameters:
3484
+ id: $response.body#/id
3485
+ DeleteAttribute:
3486
+ operationId: deleteAttribute
3487
+ parameters:
3488
+ id: $response.body#/id
3332
3489
  '400':
3333
3490
  $ref: '#/components/responses/BadRequest'
3334
3491
  '401':
@@ -3368,7 +3525,7 @@ paths:
3368
3525
  required: true
3369
3526
  description: The attribute id.
3370
3527
  schema:
3371
- type: string
3528
+ $ref: '#/components/schemas/ResourceId'
3372
3529
  responses:
3373
3530
  '200':
3374
3531
  description: Get a targeting attribute
@@ -3422,7 +3579,7 @@ paths:
3422
3579
  required: true
3423
3580
  description: The attribute id.
3424
3581
  schema:
3425
- type: string
3582
+ $ref: '#/components/schemas/ResourceId'
3426
3583
  requestBody:
3427
3584
  required: true
3428
3585
  content:
@@ -3481,7 +3638,7 @@ paths:
3481
3638
  required: true
3482
3639
  description: The attribute id.
3483
3640
  schema:
3484
- type: string
3641
+ $ref: '#/components/schemas/ResourceId'
3485
3642
  responses:
3486
3643
  '200':
3487
3644
  description: Archive a targeting attribute
@@ -3524,6 +3681,7 @@ paths:
3524
3681
  - Metrics
3525
3682
  parameters:
3526
3683
  - $ref: '#/components/parameters/ProjectId'
3684
+ - $ref: '#/components/parameters/Q'
3527
3685
  responses:
3528
3686
  '200':
3529
3687
  description: List metrics
@@ -3625,6 +3783,19 @@ paths:
3625
3783
  example:
3626
3784
  id: met_01j7w8a1b2c3d4e5f6g7h8i9j0
3627
3785
  name: checkouts
3786
+ links:
3787
+ GetMetric:
3788
+ operationId: getMetric
3789
+ parameters:
3790
+ id: $response.body#/id
3791
+ UpdateMetric:
3792
+ operationId: updateMetric
3793
+ parameters:
3794
+ id: $response.body#/id
3795
+ DeleteMetric:
3796
+ operationId: deleteMetric
3797
+ parameters:
3798
+ id: $response.body#/id
3628
3799
  '400':
3629
3800
  $ref: '#/components/responses/BadRequest'
3630
3801
  '401':
@@ -3645,6 +3816,7 @@ paths:
3645
3816
  - EVENT_PENDING
3646
3817
  - REFERENCED_NOT_FOUND
3647
3818
  - VALIDATION
3819
+ - PLAN_LIMIT
3648
3820
  x-cli:
3649
3821
  name: create
3650
3822
  positional:
@@ -3666,7 +3838,7 @@ paths:
3666
3838
  required: true
3667
3839
  description: Stable opaque metric id (`met_…`) or the metric's `name`.
3668
3840
  schema:
3669
- type: string
3841
+ $ref: '#/components/schemas/ResourceId'
3670
3842
  responses:
3671
3843
  '200':
3672
3844
  description: Get a metric
@@ -3725,7 +3897,7 @@ paths:
3725
3897
  required: true
3726
3898
  description: Stable opaque metric id (`met_…`) or the metric's `name`.
3727
3899
  schema:
3728
- type: string
3900
+ $ref: '#/components/schemas/ResourceId'
3729
3901
  requestBody:
3730
3902
  required: true
3731
3903
  content:
@@ -3784,7 +3956,7 @@ paths:
3784
3956
  required: true
3785
3957
  description: Stable opaque metric id (`met_…`) or the metric's `name`.
3786
3958
  schema:
3787
- type: string
3959
+ $ref: '#/components/schemas/ResourceId'
3788
3960
  responses:
3789
3961
  '200':
3790
3962
  description: Archive a metric
@@ -3828,6 +4000,7 @@ paths:
3828
4000
  - Events
3829
4001
  parameters:
3830
4002
  - $ref: '#/components/parameters/ProjectId'
4003
+ - $ref: '#/components/parameters/Q'
3831
4004
  responses:
3832
4005
  '200':
3833
4006
  description: List events
@@ -3913,6 +4086,23 @@ paths:
3913
4086
  example:
3914
4087
  id: evt_01j7w8a1b2c3d4e5f6g7h8i9j0
3915
4088
  name: checkout_completed
4089
+ links:
4090
+ GetEvent:
4091
+ operationId: getEvent
4092
+ parameters:
4093
+ id: $response.body#/id
4094
+ UpdateEvent:
4095
+ operationId: updateEvent
4096
+ parameters:
4097
+ id: $response.body#/id
4098
+ ApproveEvent:
4099
+ operationId: approveEvent
4100
+ parameters:
4101
+ id: $response.body#/id
4102
+ DeleteEvent:
4103
+ operationId: deleteEvent
4104
+ parameters:
4105
+ id: $response.body#/id
3916
4106
  '400':
3917
4107
  $ref: '#/components/responses/BadRequest'
3918
4108
  '401':
@@ -3931,6 +4121,7 @@ paths:
3931
4121
  - FORBIDDEN
3932
4122
  - ALREADY_EXISTS
3933
4123
  - VALIDATION
4124
+ - PLAN_LIMIT
3934
4125
  x-cli:
3935
4126
  name: create
3936
4127
  positional:
@@ -3952,7 +4143,7 @@ paths:
3952
4143
  required: true
3953
4144
  description: Stable opaque event id (`evt_…`) or the event's `name`.
3954
4145
  schema:
3955
- type: string
4146
+ $ref: '#/components/schemas/ResourceId'
3956
4147
  responses:
3957
4148
  '200':
3958
4149
  description: Get an event
@@ -4011,7 +4202,7 @@ paths:
4011
4202
  required: true
4012
4203
  description: Stable opaque event id (`evt_…`) or the event's `name`.
4013
4204
  schema:
4014
- type: string
4205
+ $ref: '#/components/schemas/ResourceId'
4015
4206
  requestBody:
4016
4207
  required: true
4017
4208
  content:
@@ -4079,7 +4270,7 @@ paths:
4079
4270
  required: true
4080
4271
  description: Stable opaque event id (`evt_…`) or the event's `name`.
4081
4272
  schema:
4082
- type: string
4273
+ $ref: '#/components/schemas/ResourceId'
4083
4274
  responses:
4084
4275
  '200':
4085
4276
  description: Archive an event
@@ -4130,7 +4321,7 @@ paths:
4130
4321
  required: true
4131
4322
  description: Stable opaque event id (`evt_…`) or the event's `name`.
4132
4323
  schema:
4133
- type: string
4324
+ $ref: '#/components/schemas/ResourceId'
4134
4325
  requestBody:
4135
4326
  required: true
4136
4327
  content:
@@ -4357,7 +4548,7 @@ paths:
4357
4548
  required: true
4358
4549
  description: Per-project item number (e.g. `7`) or the full ops item id.
4359
4550
  schema:
4360
- type: string
4551
+ $ref: '#/components/schemas/ResourceId'
4361
4552
  responses:
4362
4553
  '200':
4363
4554
  description: Get one queue item
@@ -4416,7 +4607,7 @@ paths:
4416
4607
  required: true
4417
4608
  description: Per-project item number (e.g. `7`) or the full ops item id.
4418
4609
  schema:
4419
- type: string
4610
+ $ref: '#/components/schemas/ResourceId'
4420
4611
  requestBody:
4421
4612
  required: true
4422
4613
  content:
@@ -4484,7 +4675,7 @@ paths:
4484
4675
  required: true
4485
4676
  description: Per-project item number (e.g. `7`) or the full ops item id.
4486
4677
  schema:
4487
- type: string
4678
+ $ref: '#/components/schemas/ResourceId'
4488
4679
  requestBody:
4489
4680
  required: true
4490
4681
  content:
@@ -4525,6 +4716,117 @@ paths:
4525
4716
  name: link-pr
4526
4717
  positional:
4527
4718
  - handle
4719
+ /api/admin/ops/{handle}/comments:
4720
+ get:
4721
+ operationId: listOpsComments
4722
+ summary: List an item's comments
4723
+ description: |-
4724
+ List the comment thread on a queue item, oldest first. Each comment
4725
+ carries its author (a teammate email, or `system` for a comment authored by
4726
+ Jarvis — the AI agent), its markdown body, and `parentId` for the single
4727
+ level of threaded replies. Removed comments are omitted.
4728
+
4729
+ **Use case:** Read the discussion on an item before replying or acting on it.
4730
+ tags:
4731
+ - Comments
4732
+ parameters:
4733
+ - $ref: '#/components/parameters/ProjectId'
4734
+ - name: handle
4735
+ in: path
4736
+ required: true
4737
+ description: Per-project item number (e.g. `7`) or the full ops item id.
4738
+ schema:
4739
+ $ref: '#/components/schemas/ResourceId'
4740
+ responses:
4741
+ '200':
4742
+ description: List an item's comments
4743
+ content:
4744
+ application/json:
4745
+ schema:
4746
+ $ref: '#/components/schemas/ListOpsCommentsResponse'
4747
+ '400':
4748
+ $ref: '#/components/responses/BadRequest'
4749
+ '401':
4750
+ $ref: '#/components/responses/Unauthorized'
4751
+ '403':
4752
+ $ref: '#/components/responses/Forbidden'
4753
+ '404':
4754
+ $ref: '#/components/responses/NotFound'
4755
+ x-error-codes:
4756
+ - BAD_REQUEST
4757
+ - UNAUTHORIZED
4758
+ - FORBIDDEN
4759
+ - NOT_FOUND
4760
+ x-cli:
4761
+ name: list
4762
+ positional:
4763
+ - handle
4764
+ post:
4765
+ operationId: createOpsComment
4766
+ summary: Comment on an item
4767
+ description: |-
4768
+ Append a comment to a queue item's thread. The body is markdown (mentions
4769
+ like `@teammate` notify that person; `@shipeasy` asks Jarvis, the AI agent,
4770
+ to reply). Pass `parentId` to reply under an existing top-level comment
4771
+ (one level of threading — a reply to a reply attaches to the same parent).
4772
+
4773
+ Create-only and append-only, so it is safe for restricted ops keys — the
4774
+ same channel the ops loop and Jarvis use to leave a note on an item.
4775
+ tags:
4776
+ - Comments
4777
+ parameters:
4778
+ - $ref: '#/components/parameters/ProjectId'
4779
+ - name: handle
4780
+ in: path
4781
+ required: true
4782
+ description: Per-project item number (e.g. `7`) or the full ops item id.
4783
+ schema:
4784
+ $ref: '#/components/schemas/ResourceId'
4785
+ requestBody:
4786
+ required: true
4787
+ content:
4788
+ application/json:
4789
+ schema:
4790
+ $ref: '#/components/schemas/CreateOpsCommentRequest'
4791
+ examples:
4792
+ comment:
4793
+ summary: Leave a comment
4794
+ value:
4795
+ body: Repro'd on iOS Safari 17 — the button overflows its container. cc @alice
4796
+ reply:
4797
+ summary: Reply under an existing comment
4798
+ value:
4799
+ body: Agreed, let's ship the fix behind @shipeasy's suggested flag.
4800
+ parentId: cm_01j7w8a1b2c3d4e5f6g7h8i9j0
4801
+ responses:
4802
+ '201':
4803
+ description: Comment on an item
4804
+ content:
4805
+ application/json:
4806
+ schema:
4807
+ $ref: '#/components/schemas/CreateOpsCommentResponse'
4808
+ example:
4809
+ id: cm_01j7w8a1b2c3d4e5f6g7h8i9j0
4810
+ '400':
4811
+ $ref: '#/components/responses/BadRequest'
4812
+ '401':
4813
+ $ref: '#/components/responses/Unauthorized'
4814
+ '403':
4815
+ $ref: '#/components/responses/Forbidden'
4816
+ '404':
4817
+ $ref: '#/components/responses/NotFound'
4818
+ '422':
4819
+ $ref: '#/components/responses/UnprocessableEntity'
4820
+ x-error-codes:
4821
+ - BAD_REQUEST
4822
+ - UNAUTHORIZED
4823
+ - FORBIDDEN
4824
+ - NOT_FOUND
4825
+ - VALIDATION
4826
+ x-cli:
4827
+ name: create
4828
+ positional:
4829
+ - handle
4528
4830
  /api/admin/notifications:
4529
4831
  post:
4530
4832
  operationId: notifyOps
@@ -4637,6 +4939,7 @@ paths:
4637
4939
  - Alerts
4638
4940
  parameters:
4639
4941
  - $ref: '#/components/parameters/ProjectId'
4942
+ - $ref: '#/components/parameters/Q'
4640
4943
  responses:
4641
4944
  '200':
4642
4945
  description: List alert rules
@@ -4730,6 +5033,15 @@ paths:
4730
5033
  $ref: '#/components/schemas/CreateAlertRuleResponse'
4731
5034
  example:
4732
5035
  id: ar_01j7w8a1b2c3d4e5f6g7h8i9j0
5036
+ links:
5037
+ UpdateAlertRule:
5038
+ operationId: updateAlertRule
5039
+ parameters:
5040
+ id: $response.body#/id
5041
+ DeleteAlertRule:
5042
+ operationId: deleteAlertRule
5043
+ parameters:
5044
+ id: $response.body#/id
4733
5045
  '400':
4734
5046
  $ref: '#/components/responses/BadRequest'
4735
5047
  '401':
@@ -4747,6 +5059,7 @@ paths:
4747
5059
  - UNAUTHORIZED
4748
5060
  - FORBIDDEN
4749
5061
  - ALREADY_EXISTS
5062
+ - METRIC_NOT_FOUND
4750
5063
  - REFERENCED_NOT_FOUND
4751
5064
  - VALIDATION
4752
5065
  x-cli:
@@ -4773,7 +5086,7 @@ paths:
4773
5086
  required: true
4774
5087
  description: Stable opaque alert-rule id (`ar_…`) or the rule's `name`.
4775
5088
  schema:
4776
- type: string
5089
+ $ref: '#/components/schemas/ResourceId'
4777
5090
  requestBody:
4778
5091
  required: true
4779
5092
  content:
@@ -4844,7 +5157,7 @@ paths:
4844
5157
  required: true
4845
5158
  description: Stable opaque alert-rule id (`ar_…`) or the rule's `name`.
4846
5159
  schema:
4847
- type: string
5160
+ $ref: '#/components/schemas/ResourceId'
4848
5161
  responses:
4849
5162
  '200':
4850
5163
  description: Delete an alert rule
@@ -4899,7 +5212,7 @@ paths:
4899
5212
  name: Acme
4900
5213
  domain: acme.com
4901
5214
  ownerEmail: owner@acme.com
4902
- plan: paid
5215
+ plan: pro
4903
5216
  status: active
4904
5217
  subscriptionStatus: active
4905
5218
  billingInterval: monthly
@@ -5018,7 +5331,7 @@ paths:
5018
5331
  required: true
5019
5332
  description: Stable opaque project id. Must match the caller's own project.
5020
5333
  schema:
5021
- type: string
5334
+ $ref: '#/components/schemas/ResourceId'
5022
5335
  requestBody:
5023
5336
  required: true
5024
5337
  content:
@@ -5046,7 +5359,7 @@ paths:
5046
5359
  name: Acme Rockets
5047
5360
  domain: acme.com
5048
5361
  ownerEmail: owner@acme.com
5049
- plan: paid
5362
+ plan: pro
5050
5363
  status: active
5051
5364
  subscriptionStatus: active
5052
5365
  billingInterval: monthly
@@ -5157,6 +5470,11 @@ paths:
5157
5470
  example:
5158
5471
  id: i18n_01j7w8a1b2c4
5159
5472
  name: fr:prod
5473
+ links:
5474
+ PublishI18nProfile:
5475
+ operationId: publishI18nProfile
5476
+ parameters:
5477
+ profileId: $response.body#/id
5160
5478
  '400':
5161
5479
  $ref: '#/components/responses/BadRequest'
5162
5480
  '401':
@@ -5204,9 +5522,10 @@ paths:
5204
5522
  - name: q
5205
5523
  in: query
5206
5524
  required: false
5207
- description: Free-text search — matches keys whose name OR value contains this substring (case-insensitive). Use it to find the key behind a piece of on-screen copy.
5525
+ description: Free-text search — matches keys whose name, value, OR description contains this substring (case-insensitive). Use it to find the key behind a piece of on-screen copy.
5208
5526
  schema:
5209
5527
  type: string
5528
+ maxLength: 100
5210
5529
  - name: limit
5211
5530
  in: query
5212
5531
  required: false
@@ -5215,6 +5534,14 @@ paths:
5215
5534
  type: integer
5216
5535
  minimum: 1
5217
5536
  maximum: 500
5537
+ - name: offset
5538
+ in: query
5539
+ required: false
5540
+ description: Number of keys to skip before returning `limit` rows (offset pagination).
5541
+ schema:
5542
+ type: integer
5543
+ minimum: 0
5544
+ default: 0
5218
5545
  responses:
5219
5546
  '200':
5220
5547
  description: List i18n keys
@@ -5319,7 +5646,7 @@ paths:
5319
5646
  required: true
5320
5647
  description: The key's id.
5321
5648
  schema:
5322
- type: string
5649
+ $ref: '#/components/schemas/ResourceId'
5323
5650
  requestBody:
5324
5651
  required: true
5325
5652
  content:
@@ -5467,7 +5794,7 @@ paths:
5467
5794
  required: true
5468
5795
  description: The draft id to update.
5469
5796
  schema:
5470
- type: string
5797
+ $ref: '#/components/schemas/ResourceId'
5471
5798
  requestBody:
5472
5799
  required: true
5473
5800
  content:
@@ -5525,7 +5852,7 @@ paths:
5525
5852
  required: true
5526
5853
  description: The profile id to publish.
5527
5854
  schema:
5528
- type: string
5855
+ $ref: '#/components/schemas/ResourceId'
5529
5856
  requestBody:
5530
5857
  required: true
5531
5858
  content:
@@ -5535,7 +5862,7 @@ paths:
5535
5862
  example:
5536
5863
  chunk: default
5537
5864
  responses:
5538
- '201':
5865
+ '200':
5539
5866
  description: Publish a profile chunk
5540
5867
  content:
5541
5868
  application/json:
@@ -5724,9 +6051,9 @@ paths:
5724
6051
  operationId: getError
5725
6052
  summary: Get a tracked error
5726
6053
  description: |-
5727
- Returns a single tracked error by its id, including the latest occurrence's stack, extras, and consequence. Returns `404` if no such error exists in the project.
6054
+ Returns a single tracked error by its id, including the latest occurrence's stack, extras, and consequence, plus `occurrences` — the sampled per-instance detail rows behind the issue (newest first; exhaustive while the issue is small, thinned at volume, capped at 100). Returns `404` if no such error exists in the project.
5728
6055
 
5729
- **Use case:** Drill into one issue — fetch its full stack and `seenUrls` to investigate, or to render the detail panel behind a row in the errors list.
6056
+ **Use case:** Drill into one issue — fetch its full stack and `seenUrls` to investigate, or walk `occurrences` to see how the failing message/stack varies across instances.
5730
6057
  tags:
5731
6058
  - Errors
5732
6059
  parameters:
@@ -5736,7 +6063,7 @@ paths:
5736
6063
  required: true
5737
6064
  description: Stable opaque error id (`err_…`).
5738
6065
  schema:
5739
- type: string
6066
+ $ref: '#/components/schemas/ResourceId'
5740
6067
  responses:
5741
6068
  '200':
5742
6069
  description: Get a tracked error
@@ -5770,6 +6097,31 @@ paths:
5770
6097
  lastSeenAt: '2026-06-28T14:55:31.000Z'
5771
6098
  createdAt: '2026-06-20T08:11:02.000Z'
5772
6099
  updatedAt: '2026-06-28T14:55:31.000Z'
6100
+ occurrences:
6101
+ - id: occ_01j7w7qq0v3k9d2m5rc8t1xh4n
6102
+ message: Cannot read properties of undefined (reading 'id')
6103
+ stack: |-
6104
+ TypeError: Cannot read properties of undefined (reading 'id')
6105
+ at GateEditor (/app/gates/[id]/page.tsx:42:18)
6106
+ url: https://app.example.com/dashboard/gat_01j7/edit
6107
+ env: prod
6108
+ side: client
6109
+ sdkVersion: 6.0.0
6110
+ extrasJson: '{"gateId":"gat_01j7"}'
6111
+ sampleRate: 100
6112
+ seenAt: '2026-06-28T14:55:31.000Z'
6113
+ - id: occ_01j7w7pk8s6e3f9b0zad7y5w2q
6114
+ message: Cannot read properties of undefined (reading 'id')
6115
+ stack: |-
6116
+ TypeError: Cannot read properties of undefined (reading 'id')
6117
+ at GateEditor (/app/gates/[id]/page.tsx:42:18)
6118
+ url: https://app.example.com/dashboard/gat_02c4/edit
6119
+ env: prod
6120
+ side: client
6121
+ sdkVersion: 6.0.0
6122
+ extrasJson: '{"gateId":"gat_02c4"}'
6123
+ sampleRate: 10
6124
+ seenAt: '2026-06-24T09:12:05.000Z'
5773
6125
  '400':
5774
6126
  $ref: '#/components/responses/BadRequest'
5775
6127
  '401':
@@ -5811,7 +6163,7 @@ paths:
5811
6163
  required: true
5812
6164
  description: Stable opaque error id (`err_…`).
5813
6165
  schema:
5814
- type: string
6166
+ $ref: '#/components/schemas/ResourceId'
5815
6167
  requestBody:
5816
6168
  required: true
5817
6169
  content:
@@ -5900,7 +6252,7 @@ paths:
5900
6252
  required: true
5901
6253
  description: Stable opaque error id (`err_…`).
5902
6254
  schema:
5903
- type: string
6255
+ $ref: '#/components/schemas/ResourceId'
5904
6256
  responses:
5905
6257
  '201':
5906
6258
  description: File a feedback ticket for an error
@@ -5931,6 +6283,75 @@ paths:
5931
6283
  name: file
5932
6284
  positional:
5933
6285
  - id
6286
+ /api/admin/errors/{id}/resolve:
6287
+ post:
6288
+ operationId: resolveError
6289
+ summary: Resolve a tracked error
6290
+ description: |-
6291
+ Marks one tracked error `resolved` — the single-purpose "close out" action. Takes no body; it is `PATCH /api/admin/errors/{id}` pinned to `{ "status": "resolved" }`, exposed so tooling can close an error without being handed the full open/resolved/ignored status machine. A resolved error reopens automatically (ingestion-side) if it recurs, so resolving is always safe: a premature resolve un-does itself on the next occurrence. Returns the updated row; `404` if the error does not exist.
6292
+
6293
+ **Use case:** Close out a tracked error from an agent or script once its fix has shipped — e.g. after a deploy, resolve every open issue the change addressed and let recurrence reopen anything that wasn't actually fixed.
6294
+ tags:
6295
+ - Errors
6296
+ parameters:
6297
+ - $ref: '#/components/parameters/ProjectId'
6298
+ - name: id
6299
+ in: path
6300
+ required: true
6301
+ description: Stable opaque error id (`err_…`).
6302
+ schema:
6303
+ $ref: '#/components/schemas/ResourceId'
6304
+ responses:
6305
+ '200':
6306
+ description: Resolve a tracked error
6307
+ content:
6308
+ application/json:
6309
+ schema:
6310
+ $ref: '#/components/schemas/ErrorRecord'
6311
+ example:
6312
+ id: err_01j7w7m9q4hxbf6npe6s9zr3vc
6313
+ projectId: e976b15e-2f0d-4c6e-9b1a-3a7c1f2d8e90
6314
+ fingerprint: 9c1f4f1f2c0c4a5fa1c2b6d3e7c8e3a1
6315
+ causedByFingerprint: null
6316
+ message: Cannot read properties of undefined (reading 'id')
6317
+ errorType: TypeError
6318
+ stack: null
6319
+ source: shipeasy
6320
+ url: https://app.example.com/dashboard/gat_01j7/edit
6321
+ seenUrls: null
6322
+ subject: gate editor
6323
+ outcome: crash on load
6324
+ side: client
6325
+ env: prod
6326
+ kind: uncaught
6327
+ lastExtrasJson: null
6328
+ sdkVersion: 6.0.0
6329
+ count: 137
6330
+ status: resolved
6331
+ firstSeenAt: '2026-06-20T08:11:02.000Z'
6332
+ lastSeenAt: '2026-06-28T14:55:31.000Z'
6333
+ createdAt: '2026-06-20T08:11:02.000Z'
6334
+ updatedAt: '2026-06-28T15:02:10.000Z'
6335
+ '400':
6336
+ $ref: '#/components/responses/BadRequest'
6337
+ '401':
6338
+ $ref: '#/components/responses/Unauthorized'
6339
+ '403':
6340
+ $ref: '#/components/responses/Forbidden'
6341
+ '404':
6342
+ $ref: '#/components/responses/NotFound'
6343
+ '409':
6344
+ $ref: '#/components/responses/Conflict'
6345
+ '422':
6346
+ $ref: '#/components/responses/UnprocessableEntity'
6347
+ x-error-codes:
6348
+ - UNAUTHORIZED
6349
+ - FORBIDDEN
6350
+ - NOT_FOUND
6351
+ x-cli:
6352
+ name: resolve
6353
+ positional:
6354
+ - id
5934
6355
  /api/admin/errors/{id}/series:
5935
6356
  post:
5936
6357
  operationId: getErrorSeries
@@ -5948,7 +6369,7 @@ paths:
5948
6369
  required: true
5949
6370
  description: Stable opaque error id (`err_…`).
5950
6371
  schema:
5951
- type: string
6372
+ $ref: '#/components/schemas/ResourceId'
5952
6373
  requestBody:
5953
6374
  required: true
5954
6375
  content:
@@ -6022,6 +6443,7 @@ paths:
6022
6443
  - Connectors
6023
6444
  parameters:
6024
6445
  - $ref: '#/components/parameters/ProjectId'
6446
+ - $ref: '#/components/parameters/Q'
6025
6447
  responses:
6026
6448
  '200':
6027
6449
  description: List connectors
@@ -6140,6 +6562,23 @@ paths:
6140
6562
  $ref: '#/components/schemas/CreateConnectorResponse'
6141
6563
  example:
6142
6564
  id: 9b0e7c2a-1f3d-4a8e-bf21-0c6a2e5d7f10
6565
+ links:
6566
+ GetConnector:
6567
+ operationId: getConnector
6568
+ parameters:
6569
+ id: $response.body#/id
6570
+ UpdateConnector:
6571
+ operationId: updateConnector
6572
+ parameters:
6573
+ id: $response.body#/id
6574
+ TestConnector:
6575
+ operationId: testConnector
6576
+ parameters:
6577
+ id: $response.body#/id
6578
+ DeleteConnector:
6579
+ operationId: deleteConnector
6580
+ parameters:
6581
+ id: $response.body#/id
6143
6582
  '400':
6144
6583
  $ref: '#/components/responses/BadRequest'
6145
6584
  '401':
@@ -6175,7 +6614,7 @@ paths:
6175
6614
  required: true
6176
6615
  description: Stable opaque connector id.
6177
6616
  schema:
6178
- type: string
6617
+ $ref: '#/components/schemas/ResourceId'
6179
6618
  responses:
6180
6619
  '200':
6181
6620
  description: Get a connector
@@ -6243,7 +6682,7 @@ paths:
6243
6682
  required: true
6244
6683
  description: Stable opaque connector id.
6245
6684
  schema:
6246
- type: string
6685
+ $ref: '#/components/schemas/ResourceId'
6247
6686
  requestBody:
6248
6687
  required: true
6249
6688
  content:
@@ -6316,7 +6755,7 @@ paths:
6316
6755
  required: true
6317
6756
  description: Stable opaque connector id.
6318
6757
  schema:
6319
- type: string
6758
+ $ref: '#/components/schemas/ResourceId'
6320
6759
  responses:
6321
6760
  '200':
6322
6761
  description: Delete a connector
@@ -6365,7 +6804,7 @@ paths:
6365
6804
  required: true
6366
6805
  description: Stable opaque connector id.
6367
6806
  schema:
6368
- type: string
6807
+ $ref: '#/components/schemas/ResourceId'
6369
6808
  requestBody:
6370
6809
  required: false
6371
6810
  content:
@@ -6383,7 +6822,7 @@ paths:
6383
6822
  text: Triage only the P0 bugs reported in the last 24h.
6384
6823
  responses:
6385
6824
  '200':
6386
- description: Fire a trigger connector
6825
+ description: The trigger run was successfully kicked off.
6387
6826
  content:
6388
6827
  application/json:
6389
6828
  schema:
@@ -6402,6 +6841,8 @@ paths:
6402
6841
  $ref: '#/components/responses/Conflict'
6403
6842
  '422':
6404
6843
  $ref: '#/components/responses/UnprocessableEntity'
6844
+ '502':
6845
+ $ref: '#/components/responses/BadGateway'
6405
6846
  x-error-codes:
6406
6847
  - UNAUTHORIZED
6407
6848
  - FORBIDDEN
@@ -6429,10 +6870,10 @@ paths:
6429
6870
  required: true
6430
6871
  description: Stable opaque connector id.
6431
6872
  schema:
6432
- type: string
6873
+ $ref: '#/components/schemas/ResourceId'
6433
6874
  responses:
6434
6875
  '200':
6435
- description: Test a connector
6876
+ description: The test dispatch reached the destination.
6436
6877
  content:
6437
6878
  application/json:
6438
6879
  schema:
@@ -6452,6 +6893,8 @@ paths:
6452
6893
  $ref: '#/components/responses/Conflict'
6453
6894
  '422':
6454
6895
  $ref: '#/components/responses/UnprocessableEntity'
6896
+ '502':
6897
+ $ref: '#/components/responses/BadGateway'
6455
6898
  x-error-codes:
6456
6899
  - UNAUTHORIZED
6457
6900
  - FORBIDDEN
@@ -6481,7 +6924,7 @@ paths:
6481
6924
  required: true
6482
6925
  description: Stable opaque connector id. Its stored `provider` must match the body's `provider`.
6483
6926
  schema:
6484
- type: string
6927
+ $ref: '#/components/schemas/ResourceId'
6485
6928
  requestBody:
6486
6929
  required: true
6487
6930
  content:
@@ -6587,6 +7030,27 @@ paths:
6587
7030
  $ref: '#/components/schemas/CreateConnectorResponse'
6588
7031
  example:
6589
7032
  id: 9b0e7c2a-1f3d-4a8e-bf21-0c6a2e5d7f10
7033
+ links:
7034
+ FireConnector:
7035
+ operationId: fireConnector
7036
+ parameters:
7037
+ id: $response.body#/id
7038
+ TestConnector:
7039
+ operationId: testConnector
7040
+ parameters:
7041
+ id: $response.body#/id
7042
+ GetConnector:
7043
+ operationId: getConnector
7044
+ parameters:
7045
+ id: $response.body#/id
7046
+ UpdateTriggerConnector:
7047
+ operationId: updateTriggerConnector
7048
+ parameters:
7049
+ id: $response.body#/id
7050
+ DeleteConnector:
7051
+ operationId: deleteConnector
7052
+ parameters:
7053
+ id: $response.body#/id
6590
7054
  '400':
6591
7055
  $ref: '#/components/responses/BadRequest'
6592
7056
  '401':
@@ -6783,6 +7247,7 @@ paths:
6783
7247
  - $ref: '#/components/parameters/ProjectId'
6784
7248
  - $ref: '#/components/parameters/PaginationLimit'
6785
7249
  - $ref: '#/components/parameters/PaginationCursor'
7250
+ - $ref: '#/components/parameters/Q'
6786
7251
  responses:
6787
7252
  '200':
6788
7253
  description: List API keys
@@ -6896,6 +7361,7 @@ paths:
6896
7361
  - UNAUTHORIZED
6897
7362
  - FORBIDDEN
6898
7363
  - VALIDATION
7364
+ - PLAN_LIMIT
6899
7365
  x-cli:
6900
7366
  name: create
6901
7367
  /api/admin/keys/{id}/revoke:
@@ -6917,7 +7383,7 @@ paths:
6917
7383
  required: true
6918
7384
  description: Stable opaque key id (UUID) returned by `create` / `list`.
6919
7385
  schema:
6920
- type: string
7386
+ $ref: '#/components/schemas/ResourceId'
6921
7387
  responses:
6922
7388
  '200':
6923
7389
  description: Revoke an API key
@@ -7042,6 +7508,14 @@ components:
7042
7508
  description: Opaque cursor returned in the previous page's `next_cursor`. Omit for the first page.
7043
7509
  schema:
7044
7510
  type: string
7511
+ Q:
7512
+ name: q
7513
+ in: query
7514
+ required: false
7515
+ description: Case-insensitive substring filter across the resource's human-readable text columns (e.g. `name`, `title`, `description`). OR-matched across those columns; omit to return everything.
7516
+ schema:
7517
+ type: string
7518
+ maxLength: 100
7045
7519
  schemas:
7046
7520
  ListGatesResponse:
7047
7521
  type: object
@@ -7066,6 +7540,12 @@ components:
7066
7540
  minimum: 0
7067
7541
  maximum: 1
7068
7542
  description: Master switch. Returned as `1`/`0` from D1; treat truthy as enabled.
7543
+ type:
7544
+ type: string
7545
+ enum:
7546
+ - targeting
7547
+ - holdout
7548
+ description: Gate kind — `targeting` (normal flag) or `holdout` (restricted public % + whitelist).
7069
7549
  rolloutPct:
7070
7550
  type: integer
7071
7551
  minimum: 0
@@ -7357,6 +7837,15 @@ components:
7357
7837
  - GROUPS_WEIGHT_SUM
7358
7838
  - EVENT_PENDING
7359
7839
  - INTERNAL
7840
+ - PLAN_LIMIT
7841
+ - EXPERIMENT_NO_GOAL_METRIC
7842
+ - EXPERIMENT_ARCHIVED_RESTART
7843
+ - EXPERIMENT_RESTORE_INVALID
7844
+ - EXPERIMENT_NOT_RUNNING
7845
+ - EXPERIMENT_RUNNING_ARCHIVE
7846
+ - EXPERIMENT_IMMUTABLE_FIELD
7847
+ - METRIC_NOT_FOUND
7848
+ - METRIC_UNKNOWN_ID
7360
7849
  x-enum-descriptions:
7361
7850
  BAD_REQUEST: Malformed request (bad JSON, missing project scope).
7362
7851
  UNAUTHORIZED: Missing or invalid admin SDK key.
@@ -7373,9 +7862,18 @@ components:
7373
7862
  GROUPS_WEIGHT_SUM: Experiment group weights must sum to 10000 (basis points).
7374
7863
  EVENT_PENDING: The referenced event is still pending review and cannot back a metric yet.
7375
7864
  INTERNAL: Unexpected server error.
7865
+ PLAN_LIMIT: The action would exceed a plan quota (e.g. the tier's maximum experiments, metrics, or configs). Upgrade the plan or archive an existing resource.
7866
+ EXPERIMENT_NO_GOAL_METRIC: The experiment has no goal metric set, which the requested action requires.
7867
+ EXPERIMENT_ARCHIVED_RESTART: An archived experiment cannot be restarted.
7868
+ EXPERIMENT_RESTORE_INVALID: The experiment cannot be restored from its current state.
7869
+ EXPERIMENT_NOT_RUNNING: The action requires the experiment to be running.
7870
+ EXPERIMENT_RUNNING_ARCHIVE: A running experiment cannot be archived — stop it first.
7871
+ EXPERIMENT_IMMUTABLE_FIELD: A field that is immutable while the experiment is running was modified.
7872
+ METRIC_NOT_FOUND: The referenced metric does not exist or is not visible to the caller.
7873
+ METRIC_UNKNOWN_ID: The supplied metric id is malformed or not recognised.
7376
7874
  Error:
7377
7875
  type: object
7378
- description: Uniform error envelope returned by every admin endpoint on a non-2xx status. `error` is the human-readable message; `code` is the stable machine code (present whenever the failure maps to a catalogued `ErrorCode`); `detail` carries extra context (validation paths, conflicting field); `instructions` is optional actionable guidance for resolving the error.
7876
+ description: Uniform error envelope returned by every admin endpoint on a non-2xx status. `error` is the human-readable message; `code` is the stable machine code (present whenever the failure maps to a catalogued `ErrorCode`); `detail` carries extra context (validation paths, conflicting field); `fields` maps each failing input field to its own message; `instructions` is optional actionable guidance for resolving the error; `schema` echoes back the expected JSON Schema when a value failed schema validation.
7379
7877
  required:
7380
7878
  - error
7381
7879
  additionalProperties: false
@@ -7388,9 +7886,18 @@ components:
7388
7886
  detail:
7389
7887
  type: string
7390
7888
  description: Extra context (validation details, conflicting field, referenced row).
7889
+ fields:
7890
+ type: object
7891
+ additionalProperties:
7892
+ type: string
7893
+ description: Per-field validation messages, keyed by the submitted field's path (dot-notation for nested fields, e.g. `groups.0.weight`). Present when structural validation failed on one or more specific input fields, so a client form can attach each message to its own input instead of collapsing everything into one banner.
7391
7894
  instructions:
7392
7895
  type: string
7393
7896
  description: Actionable guidance for resolving this error — what to check or change next.
7897
+ schema:
7898
+ type: object
7899
+ additionalProperties: true
7900
+ description: The expected JSON Schema (draft 2020-12) the submitted value must satisfy. Returned only when a value failed schema validation (e.g. a config `value` that does not match its config's saved schema), so the caller can correct the value and retry without re-fetching.
7394
7901
  Folder:
7395
7902
  anyOf:
7396
7903
  - type: string
@@ -7406,6 +7913,13 @@ components:
7406
7913
  maxLength: 128
7407
7914
  pattern: ^[a-z0-9](?:[a-z0-9_-]*[a-z0-9])?(?:\.[a-z0-9](?:[a-z0-9_-]*[a-z0-9])?)?$
7408
7915
  description: Stable gate key used by SDKs (`Shipeasy.checkGate(user, '<name>')`). Single segment or `folder.name`. Lowercase letters, digits, `_` or `-`; max 128 chars. Immutable after create — rename = delete + recreate.
7916
+ type:
7917
+ default: targeting
7918
+ type: string
7919
+ enum:
7920
+ - targeting
7921
+ - holdout
7922
+ description: Gate kind. `targeting` (default) is a normal flag with the full builder. `holdout` is a **restricted** flag — only a public rollout % and a whitelist are allowed; attribute rules and a gatekeeper stack are rejected. Used as an experiment's `holdout_gate`.
7409
7923
  enabled:
7410
7924
  default: true
7411
7925
  description: Master switch. Defaults to `true`. Set `false` to create the gate disabled (evaluates to `false` regardless of rules/rollout); flip on via `POST /{id}/enable` or PATCH.
@@ -7690,6 +8204,11 @@ components:
7690
8204
  - id
7691
8205
  - name
7692
8206
  additionalProperties: false
8207
+ ResourceId:
8208
+ type: string
8209
+ minLength: 1
8210
+ maxLength: 128
8211
+ description: A resource path identifier — an opaque `xxx_<ULID>` id (~30 chars) or the resource's `name`/`key`. 1–128 characters; the upper bound matches the longest name/key any resource accepts, so an over-long value can never name a real row.
7693
8212
  DeleteGateResponse:
7694
8213
  type: object
7695
8214
  properties:
@@ -7702,6 +8221,12 @@ components:
7702
8221
  UpdateGateRequest:
7703
8222
  type: object
7704
8223
  properties:
8224
+ type:
8225
+ type: string
8226
+ enum:
8227
+ - targeting
8228
+ - holdout
8229
+ description: Gate kind. Switching to `holdout` requires the gate carry only a public rollout % + whitelist (attribute rules / stack are rejected).
7705
8230
  rollout_pct:
7706
8231
  description: New rollout in **basis points** (0–10000 = 0%–100%) — `100` here means **1%**. Use `rollout_percent` (0–100) below for percent. Omit both to leave unchanged.
7707
8232
  type: integer
@@ -7993,155 +8518,292 @@ components:
7993
8518
  - id
7994
8519
  - enabled
7995
8520
  additionalProperties: false
7996
- ListExperimentsResponse:
8521
+ ExperimentApiRow:
7997
8522
  type: object
7998
8523
  properties:
7999
- data:
8000
- type: array
8524
+ id:
8525
+ type: string
8526
+ description: Stable opaque experiment id (`exp_…`).
8527
+ name:
8528
+ type: string
8529
+ maxLength: 128
8530
+ pattern: ^[a-z0-9](?:[a-z0-9_-]*[a-z0-9])?(?:\.[a-z0-9](?:[a-z0-9_-]*[a-z0-9])?)?$
8531
+ description: Stable experiment key. Single segment or `folder.name` (a-z, 0-9, `_`/`-`; max 128 chars). Used by SDKs as `Shipeasy.getExperiment(user, '<name>')`. Immutable after create.
8532
+ description:
8533
+ anyOf:
8534
+ - type: string
8535
+ - type: 'null'
8536
+ hypothesis:
8537
+ anyOf:
8538
+ - type: string
8539
+ - type: 'null'
8540
+ tag:
8541
+ anyOf:
8542
+ - type: string
8543
+ - type: 'null'
8544
+ ownerEmail:
8545
+ anyOf:
8546
+ - type: string
8547
+ - type: 'null'
8548
+ audience:
8549
+ anyOf:
8550
+ - type: string
8551
+ - type: 'null'
8552
+ bucketBy:
8553
+ anyOf:
8554
+ - type: string
8555
+ minLength: 1
8556
+ maxLength: 128
8557
+ pattern: ^[a-zA-Z_][a-zA-Z0-9_]*$
8558
+ description: User-attribute name used as the bucketing key — e.g. `user_id` (default), `session_id`, `device_id`, or a custom attribute like `company_id` to keep a whole org on one variant. Defaults to `user_id` when omitted.
8559
+ - type: 'null'
8560
+ folder:
8561
+ anyOf:
8562
+ - type: string
8563
+ - type: 'null'
8564
+ status:
8565
+ type: string
8566
+ enum:
8567
+ - draft
8568
+ - running
8569
+ - stopped
8570
+ - archived
8571
+ universe:
8572
+ type: string
8573
+ description: Universe name this experiment draws from.
8574
+ targetingGate:
8575
+ anyOf:
8576
+ - type: string
8577
+ - type: 'null'
8578
+ holdoutGate:
8579
+ anyOf:
8580
+ - type: string
8581
+ - type: 'null'
8582
+ description: Per-experiment holdout gate name (a `holdout`-type flag), or `null`.
8583
+ allocationPct:
8584
+ type: integer
8585
+ minimum: -9007199254740991
8586
+ maximum: 9007199254740991
8587
+ description: Allocation in basis points (0–10000).
8588
+ reservedHeadroom:
8589
+ type: integer
8590
+ minimum: 0
8591
+ maximum: 10000
8592
+ description: Basis points of the split reserved for appended variants (group weights sum to 10000 − this).
8593
+ hashVersion:
8594
+ type: integer
8595
+ minimum: 1
8596
+ description: Bucketing hash algorithm version for the experiment's pool slice (§B4). Defaults to 1.
8597
+ poolOffsetBp:
8598
+ anyOf:
8599
+ - type: integer
8600
+ minimum: 0
8601
+ maximum: 10000
8602
+ - type: 'null'
8603
+ description: Basis-point offset of the experiment's contiguous slice in the universe pool, or `null` before a slice is allocated.
8604
+ poolSizeBp:
8605
+ anyOf:
8606
+ - type: integer
8607
+ minimum: 0
8608
+ maximum: 10000
8609
+ - type: 'null'
8610
+ description: Basis-point width of the experiment's pool slice (equal to its allocation), or `null` before a slice is allocated.
8611
+ salt:
8612
+ type: string
8613
+ params:
8614
+ type: object
8615
+ additionalProperties:
8616
+ type: string
8617
+ enum:
8618
+ - string
8619
+ - bool
8620
+ - number
8621
+ groups:
8622
+ type: array
8001
8623
  items:
8002
8624
  type: object
8003
8625
  properties:
8004
- id:
8005
- type: string
8006
- description: Stable opaque experiment id (`exp_…`).
8007
8626
  name:
8008
8627
  type: string
8009
- maxLength: 128
8010
- pattern: ^[a-z0-9](?:[a-z0-9_-]*[a-z0-9])?(?:\.[a-z0-9](?:[a-z0-9_-]*[a-z0-9])?)?$
8011
- description: Stable experiment key. Single segment or `folder.name` (a-z, 0-9, `_`/`-`; max 128 chars). Used by SDKs as `Shipeasy.getExperiment(user, '<name>')`. Immutable after create.
8012
- description:
8013
- anyOf:
8014
- - type: string
8015
- - type: 'null'
8016
- hypothesis:
8017
- anyOf:
8018
- - type: string
8019
- - type: 'null'
8020
- tag:
8021
- anyOf:
8022
- - type: string
8023
- - type: 'null'
8024
- ownerEmail:
8025
- anyOf:
8026
- - type: string
8027
- - type: 'null'
8028
- audience:
8029
- anyOf:
8030
- - type: string
8031
- - type: 'null'
8032
- bucketBy:
8033
- anyOf:
8034
- - type: string
8035
- minLength: 1
8036
- maxLength: 128
8037
- pattern: ^[a-zA-Z_][a-zA-Z0-9_]*$
8038
- description: User-attribute name used as the bucketing key — e.g. `user_id` (default), `session_id`, `device_id`, or a custom attribute like `company_id` to keep a whole org on one variant. Defaults to `user_id` when omitted.
8039
- - type: 'null'
8040
- folder:
8041
- anyOf:
8042
- - type: string
8043
- - type: 'null'
8044
- status:
8045
- type: string
8046
- enum:
8047
- - draft
8048
- - running
8049
- - stopped
8050
- - archived
8051
- universe:
8052
- type: string
8053
- description: Universe name this experiment draws from.
8054
- targetingGate:
8055
- anyOf:
8056
- - type: string
8057
- - type: 'null'
8058
- allocationPct:
8628
+ minLength: 1
8629
+ maxLength: 64
8630
+ description: Group label (e.g. `control`, `treatment_a`). Max 64 chars.
8631
+ weight:
8059
8632
  type: integer
8060
- minimum: -9007199254740991
8061
- maximum: 9007199254740991
8062
- description: Allocation in basis points (0–10000).
8063
- salt:
8064
- type: string
8633
+ minimum: 0
8634
+ maximum: 10000
8635
+ description: Allocation weight in basis points (0–10000). The sum across all groups must equal exactly **10000** (100%).
8065
8636
  params:
8637
+ default: {}
8638
+ description: Per-group parameter values delivered to the SDK when a caller is hashed into this group. Keys must match the experiment's `params` schema.
8066
8639
  type: object
8067
- additionalProperties:
8640
+ additionalProperties: {}
8641
+ required:
8642
+ - name
8643
+ - weight
8644
+ additionalProperties: false
8645
+ description: One variant in the experiment. Weights across all groups must sum to 10000.
8646
+ significanceThreshold:
8647
+ type: number
8648
+ minRuntimeDays:
8649
+ type: integer
8650
+ minimum: -9007199254740991
8651
+ maximum: 9007199254740991
8652
+ minSampleSize:
8653
+ type: integer
8654
+ minimum: -9007199254740991
8655
+ maximum: 9007199254740991
8656
+ sequentialTesting:
8657
+ type: boolean
8658
+ startedAt:
8659
+ anyOf:
8660
+ - type: string
8661
+ - type: 'null'
8662
+ description: ISO-8601 timestamp the experiment last transitioned to `running`, or `null`.
8663
+ stoppedAt:
8664
+ anyOf:
8665
+ - type: string
8666
+ - type: 'null'
8667
+ updatedAt:
8668
+ type: string
8669
+ version:
8670
+ anyOf:
8671
+ - type: integer
8672
+ - type: 'null'
8673
+ description: Save counter (number of published edits), or `null` on pre-versioning rows.
8674
+ creatorEmail:
8675
+ anyOf:
8676
+ - type: string
8677
+ - type: 'null'
8678
+ description: 'Resolved creator email (`created_by` → users). Enriched field: present on list rows, omitted from the by-id detail.'
8679
+ updaterEmail:
8680
+ anyOf:
8681
+ - type: string
8682
+ - type: 'null'
8683
+ description: 'Resolved last-editor email (`updated_by` → users). Enriched field: present on list rows, omitted from the by-id detail.'
8684
+ verdict:
8685
+ type: string
8686
+ enum:
8687
+ - ship
8688
+ - hold
8689
+ - wait
8690
+ - invalid
8691
+ - draft
8692
+ description: 'Answer-first decision from the goal metric + guardrails + SRM vs. the significance threshold and min runtime: `ship`, `hold`, `wait`, `invalid`, `draft`. Enriched field: list rows only.'
8693
+ verdictTitle:
8694
+ type: string
8695
+ description: 'Expanded verdict headline the results hero renders. Enriched field: list rows only.'
8696
+ verdictWhy:
8697
+ type: string
8698
+ description: 'Verdict rationale the results hero renders. Enriched field: list rows only.'
8699
+ goalMetric:
8700
+ anyOf:
8701
+ - type: object
8702
+ properties:
8703
+ id:
8068
8704
  type: string
8069
- enum:
8070
- - string
8071
- - bool
8072
- - number
8073
- groups:
8074
- type: array
8075
- items:
8076
- type: object
8077
- properties:
8078
- name:
8079
- type: string
8080
- minLength: 1
8081
- maxLength: 64
8082
- description: Group label (e.g. `control`, `treatment_a`). Max 64 chars.
8083
- weight:
8084
- type: integer
8085
- minimum: 0
8086
- maximum: 10000
8087
- description: Allocation weight in basis points (0–10000). The sum across all groups must equal exactly **10000** (100%).
8088
- params:
8089
- default: {}
8090
- description: Per-group parameter values delivered to the SDK when a caller is hashed into this group. Keys must match the experiment's `params` schema.
8091
- type: object
8092
- additionalProperties: {}
8093
- required:
8094
- - name
8095
- - weight
8096
- additionalProperties: false
8097
- description: One variant in the experiment. Weights across all groups must sum to 10000.
8098
- significanceThreshold:
8099
- type: number
8100
- minRuntimeDays:
8101
- type: integer
8102
- minimum: -9007199254740991
8103
- maximum: 9007199254740991
8104
- minSampleSize:
8105
- type: integer
8106
- minimum: -9007199254740991
8107
- maximum: 9007199254740991
8108
- sequentialTesting:
8109
- type: boolean
8110
- startedAt:
8111
- anyOf:
8112
- - type: string
8113
- - type: 'null'
8114
- description: ISO-8601 timestamp the experiment last transitioned to `running`, or `null`.
8115
- stoppedAt:
8705
+ name:
8706
+ type: string
8707
+ required:
8708
+ - id
8709
+ - name
8710
+ additionalProperties: false
8711
+ - type: 'null'
8712
+ description: 'The experiment''s goal metric `{ id, name }`, or `null` when none is set. Enriched field: list rows only.'
8713
+ guardrails:
8714
+ type: array
8715
+ items:
8716
+ type: object
8717
+ properties:
8718
+ id:
8719
+ type: string
8720
+ name:
8721
+ type: string
8722
+ eventName:
8116
8723
  anyOf:
8117
8724
  - type: string
8118
8725
  - type: 'null'
8119
- updatedAt:
8120
- type: string
8121
8726
  required:
8122
8727
  - id
8123
8728
  - name
8124
- - description
8125
- - hypothesis
8126
- - tag
8127
- - ownerEmail
8128
- - audience
8129
- - bucketBy
8130
- - folder
8131
- - status
8132
- - universe
8133
- - targetingGate
8134
- - allocationPct
8135
- - salt
8136
- - params
8137
- - groups
8138
- - significanceThreshold
8139
- - minRuntimeDays
8140
- - minSampleSize
8141
- - sequentialTesting
8142
- - startedAt
8143
- - updatedAt
8144
8729
  additionalProperties: false
8730
+ description: 'Guardrail metrics `{ id, name, eventName? }`. Enriched field: list rows only.'
8731
+ guardrailCount:
8732
+ type: integer
8733
+ minimum: 0
8734
+ description: 'Number of guardrail metrics. Enriched field: list rows only.'
8735
+ primaryLiftPct:
8736
+ anyOf:
8737
+ - type: number
8738
+ - type: 'null'
8739
+ description: 'Goal-metric lift of the leading variant vs. control, in percent, or `null` when not yet computed. Enriched field: list rows only.'
8740
+ significancePct:
8741
+ anyOf:
8742
+ - type: number
8743
+ - type: 'null'
8744
+ description: 'Statistical significance (1 − p) as a percent, or `null` when not yet computed. Enriched field: list rows only.'
8745
+ sampleSize:
8746
+ anyOf:
8747
+ - type: integer
8748
+ - type: 'null'
8749
+ description: 'Total distinct units analysed, or `null` when not yet computed. Enriched field: list rows only.'
8750
+ exposure:
8751
+ type: array
8752
+ items:
8753
+ type: object
8754
+ properties:
8755
+ ds:
8756
+ type: string
8757
+ value:
8758
+ type: number
8759
+ required:
8760
+ - ds
8761
+ - value
8762
+ additionalProperties: false
8763
+ description: 'Cumulative unique-users-enrolled series for the exposure sparkline. Enriched field: list rows only.'
8764
+ exposureTotal:
8765
+ anyOf:
8766
+ - type: integer
8767
+ - type: 'null'
8768
+ description: 'Total distinct units ever exposed (sparkline headline), or `null`. Enriched field: list rows only.'
8769
+ required:
8770
+ - id
8771
+ - name
8772
+ - description
8773
+ - hypothesis
8774
+ - tag
8775
+ - ownerEmail
8776
+ - audience
8777
+ - bucketBy
8778
+ - folder
8779
+ - status
8780
+ - universe
8781
+ - targetingGate
8782
+ - holdoutGate
8783
+ - allocationPct
8784
+ - reservedHeadroom
8785
+ - hashVersion
8786
+ - poolOffsetBp
8787
+ - poolSizeBp
8788
+ - salt
8789
+ - params
8790
+ - groups
8791
+ - significanceThreshold
8792
+ - minRuntimeDays
8793
+ - minSampleSize
8794
+ - sequentialTesting
8795
+ - startedAt
8796
+ - stoppedAt
8797
+ - updatedAt
8798
+ - version
8799
+ additionalProperties: false
8800
+ ListExperimentsResponse:
8801
+ type: object
8802
+ properties:
8803
+ data:
8804
+ type: array
8805
+ items:
8806
+ $ref: '#/components/schemas/ExperimentApiRow'
8145
8807
  next_cursor:
8146
8808
  anyOf:
8147
8809
  - type: string
@@ -8185,6 +8847,12 @@ components:
8185
8847
  minLength: 1
8186
8848
  maxLength: 256
8187
8849
  description: Numeric event property for `sum`/`avg` (with `event`).
8850
+ min_effect_of_interest:
8851
+ description: Per-experiment override of the metric's default minimum effect of interest (relative, 0–1) — the smallest change worth acting on for this experiment's decision. `null`/omitted inherits the metric default. For a guardrail, the non-inferiority margin.
8852
+ default: null
8853
+ anyOf:
8854
+ - type: number
8855
+ - type: 'null'
8188
8856
  additionalProperties: false
8189
8857
  description: Inline metric — a DSL `query`, or an `event` (+ `aggregation`/`value`) the server compiles into one.
8190
8858
  CreateExperimentRequest:
@@ -8247,13 +8915,19 @@ components:
8247
8915
  description: Name of an existing universe in the project. Returns `422` if the universe doesn't exist.
8248
8916
  targeting_gate:
8249
8917
  default: null
8250
- description: Optional gate name. Only callers that pass the gate are enrolled in the experiment.
8918
+ description: Optional gate name (a `targeting`-type flag). Only callers that pass the gate are enrolled in the experiment.
8919
+ anyOf:
8920
+ - type: string
8921
+ - type: 'null'
8922
+ holdout_gate:
8923
+ default: null
8924
+ description: Optional per-experiment holdout gate — the name of a `holdout`-type flag (public % + whitelist). A caller the flag passes is *held out* (never assigned, sees the universe defaults). Distinct from the universe-level holdout.
8251
8925
  anyOf:
8252
8926
  - type: string
8253
8927
  - type: 'null'
8254
8928
  allocation_pct:
8255
8929
  default: 0
8256
- description: Share of the (gated) audience allocated to the experiment, in basis points (0–10000 = 0%–100%). `0` = unallocated. Use `allocation_percent` (0–100) below to think in percent. Immutable while the experiment is running.
8930
+ description: Share of the (gated) audience allocated to the experiment, in basis points (0–10000 = 0%–100%). `0` = unallocated. Under pooled assignment this is the size of the universe-pool slice claimed. Use `allocation_percent` (0–100) below to think in percent. Immutable while the experiment is running.
8257
8931
  type: integer
8258
8932
  minimum: 0
8259
8933
  maximum: 10000
@@ -8262,6 +8936,11 @@ components:
8262
8936
  type: number
8263
8937
  minimum: 0
8264
8938
  maximum: 100
8939
+ reserved_headroom:
8940
+ description: Basis points of this experiment's split kept empty (0–10000) so a new variant can be appended into it while running without reshuffling. Group weights must sum to `10000 − reserved_headroom`. Defaults to the universe's `recommended_headroom` when omitted.
8941
+ type: integer
8942
+ minimum: 0
8943
+ maximum: 10000
8265
8944
  salt:
8266
8945
  description: Hash salt for bucketing. Auto-generated if omitted. Immutable while running.
8267
8946
  type: string
@@ -8276,7 +8955,7 @@ components:
8276
8955
  - string
8277
8956
  - bool
8278
8957
  - number
8279
- description: 'Map of param-name → scalar type. Defines the shape of `groups[].params`. Example: `{ headline: ''string'', show_cta: ''bool'' }`.'
8958
+ description: '**Deprecated** — the universe now owns the config schema (`param_schema`). Retained for back-compat; new experiments should leave this empty and declare params on the universe. Map of param-name → scalar type.'
8280
8959
  groups:
8281
8960
  minItems: 2
8282
8961
  type: array
@@ -8292,18 +8971,18 @@ components:
8292
8971
  type: integer
8293
8972
  minimum: 0
8294
8973
  maximum: 10000
8295
- description: Allocation weight in basis points (0–10000). The sum across all groups must equal exactly **10000** (100%).
8974
+ description: Allocation weight in basis points (0–10000). Weights across all groups must sum to `10000 − reserved_headroom` (100% minus any reserved tail).
8296
8975
  params:
8297
8976
  default: {}
8298
- description: Per-group parameter values delivered to the SDK when a caller is hashed into this group. Keys must match the experiment's `params` schema.
8977
+ description: Per-variant **override map** — a subset of the universe's `param_schema` keys. A key the variant doesn't set inherits the universe default. Unknown keys (not in the universe schema) are rejected.
8299
8978
  type: object
8300
8979
  additionalProperties: {}
8301
8980
  required:
8302
8981
  - name
8303
8982
  - weight
8304
8983
  additionalProperties: false
8305
- description: One variant in the experiment. Weights across all groups must sum to 10000.
8306
- description: Two or more variants. Weights must sum to exactly 10000 (100%). Immutable while running.
8984
+ description: One variant. Its `params` may only override keys declared on the universe schema.
8985
+ description: Two or more variants. Weights must sum to `10000 − reserved_headroom`. Existing weights are immutable while running, but a new variant may be appended into the reserved tail.
8307
8986
  significance_threshold:
8308
8987
  default: 0.05
8309
8988
  description: p-value cutoff used by the analysis pass. Defaults to `0.05`. Values other than 0.05 require Pro plan or higher.
@@ -8357,150 +9036,6 @@ components:
8357
9036
  - id
8358
9037
  - name
8359
9038
  additionalProperties: false
8360
- GetExperimentResponse:
8361
- type: object
8362
- properties:
8363
- id:
8364
- type: string
8365
- description: Stable opaque experiment id (`exp_…`).
8366
- name:
8367
- type: string
8368
- maxLength: 128
8369
- pattern: ^[a-z0-9](?:[a-z0-9_-]*[a-z0-9])?(?:\.[a-z0-9](?:[a-z0-9_-]*[a-z0-9])?)?$
8370
- description: Stable experiment key. Single segment or `folder.name` (a-z, 0-9, `_`/`-`; max 128 chars). Used by SDKs as `Shipeasy.getExperiment(user, '<name>')`. Immutable after create.
8371
- description:
8372
- anyOf:
8373
- - type: string
8374
- - type: 'null'
8375
- hypothesis:
8376
- anyOf:
8377
- - type: string
8378
- - type: 'null'
8379
- tag:
8380
- anyOf:
8381
- - type: string
8382
- - type: 'null'
8383
- ownerEmail:
8384
- anyOf:
8385
- - type: string
8386
- - type: 'null'
8387
- audience:
8388
- anyOf:
8389
- - type: string
8390
- - type: 'null'
8391
- bucketBy:
8392
- anyOf:
8393
- - type: string
8394
- minLength: 1
8395
- maxLength: 128
8396
- pattern: ^[a-zA-Z_][a-zA-Z0-9_]*$
8397
- description: User-attribute name used as the bucketing key — e.g. `user_id` (default), `session_id`, `device_id`, or a custom attribute like `company_id` to keep a whole org on one variant. Defaults to `user_id` when omitted.
8398
- - type: 'null'
8399
- folder:
8400
- anyOf:
8401
- - type: string
8402
- - type: 'null'
8403
- status:
8404
- type: string
8405
- enum:
8406
- - draft
8407
- - running
8408
- - stopped
8409
- - archived
8410
- universe:
8411
- type: string
8412
- description: Universe name this experiment draws from.
8413
- targetingGate:
8414
- anyOf:
8415
- - type: string
8416
- - type: 'null'
8417
- allocationPct:
8418
- type: integer
8419
- minimum: -9007199254740991
8420
- maximum: 9007199254740991
8421
- description: Allocation in basis points (0–10000).
8422
- salt:
8423
- type: string
8424
- params:
8425
- type: object
8426
- additionalProperties:
8427
- type: string
8428
- enum:
8429
- - string
8430
- - bool
8431
- - number
8432
- groups:
8433
- type: array
8434
- items:
8435
- type: object
8436
- properties:
8437
- name:
8438
- type: string
8439
- minLength: 1
8440
- maxLength: 64
8441
- description: Group label (e.g. `control`, `treatment_a`). Max 64 chars.
8442
- weight:
8443
- type: integer
8444
- minimum: 0
8445
- maximum: 10000
8446
- description: Allocation weight in basis points (0–10000). The sum across all groups must equal exactly **10000** (100%).
8447
- params:
8448
- default: {}
8449
- description: Per-group parameter values delivered to the SDK when a caller is hashed into this group. Keys must match the experiment's `params` schema.
8450
- type: object
8451
- additionalProperties: {}
8452
- required:
8453
- - name
8454
- - weight
8455
- additionalProperties: false
8456
- description: One variant in the experiment. Weights across all groups must sum to 10000.
8457
- significanceThreshold:
8458
- type: number
8459
- minRuntimeDays:
8460
- type: integer
8461
- minimum: -9007199254740991
8462
- maximum: 9007199254740991
8463
- minSampleSize:
8464
- type: integer
8465
- minimum: -9007199254740991
8466
- maximum: 9007199254740991
8467
- sequentialTesting:
8468
- type: boolean
8469
- startedAt:
8470
- anyOf:
8471
- - type: string
8472
- - type: 'null'
8473
- description: ISO-8601 timestamp the experiment last transitioned to `running`, or `null`.
8474
- stoppedAt:
8475
- anyOf:
8476
- - type: string
8477
- - type: 'null'
8478
- updatedAt:
8479
- type: string
8480
- required:
8481
- - id
8482
- - name
8483
- - description
8484
- - hypothesis
8485
- - tag
8486
- - ownerEmail
8487
- - audience
8488
- - bucketBy
8489
- - folder
8490
- - status
8491
- - universe
8492
- - targetingGate
8493
- - allocationPct
8494
- - salt
8495
- - params
8496
- - groups
8497
- - significanceThreshold
8498
- - minRuntimeDays
8499
- - minSampleSize
8500
- - sequentialTesting
8501
- - startedAt
8502
- - updatedAt
8503
- additionalProperties: false
8504
9039
  DeleteExperimentResponse:
8505
9040
  type: object
8506
9041
  properties:
@@ -8557,11 +9092,21 @@ components:
8557
9092
  anyOf:
8558
9093
  - type: string
8559
9094
  - type: 'null'
9095
+ holdout_gate:
9096
+ description: Per-experiment holdout gate — the name of a `holdout`-type flag, or `null` to clear. A caller the flag passes is held out.
9097
+ anyOf:
9098
+ - type: string
9099
+ - type: 'null'
8560
9100
  allocation_pct:
8561
9101
  description: Basis-points allocation (0–10000). Use `allocation_percent` (0–100) for percent. Immutable while the experiment is running.
8562
9102
  type: integer
8563
9103
  minimum: 0
8564
9104
  maximum: 10000
9105
+ reserved_headroom:
9106
+ description: Basis points of the split kept empty for appended variants. Group weights must sum to `10000 − reserved_headroom`. May be shrunk (never grown into existing weights) while running when appending a variant.
9107
+ type: integer
9108
+ minimum: 0
9109
+ maximum: 10000
8565
9110
  allocation_percent:
8566
9111
  description: Allocation as a **percentage** (0–100). Friendlier alias for `allocation_pct`; converted to basis points server-side. Wins over `allocation_pct` if both are supplied. Immutable while running.
8567
9112
  type: number
@@ -8583,9 +9128,9 @@ components:
8583
9128
  - string
8584
9129
  - bool
8585
9130
  - number
8586
- description: 'Map of param-name → scalar type. Defines the shape of `groups[].params`. Example: `{ headline: ''string'', show_cta: ''bool'' }`.'
9131
+ description: '**Deprecated** — the universe owns the config schema (`param_schema`). Retained for back-compat. Map of param-name → scalar type.'
8587
9132
  groups:
8588
- description: Replacement groups. Weights must sum to 10000. Immutable while running.
9133
+ description: Replacement groups. Weights must sum to `10000 − reserved_headroom`. Existing weights/values are immutable while running; a new variant may be appended into the reserved tail.
8589
9134
  minItems: 2
8590
9135
  type: array
8591
9136
  items:
@@ -8600,17 +9145,17 @@ components:
8600
9145
  type: integer
8601
9146
  minimum: 0
8602
9147
  maximum: 10000
8603
- description: Allocation weight in basis points (0–10000). The sum across all groups must equal exactly **10000** (100%).
9148
+ description: Allocation weight in basis points (0–10000). Weights across all groups must sum to `10000 − reserved_headroom`.
8604
9149
  params:
8605
9150
  default: {}
8606
- description: Per-group parameter values delivered to the SDK when a caller is hashed into this group. Keys must match the experiment's `params` schema.
9151
+ description: Per-variant **override map** — a subset of the universe's `param_schema` keys. Unknown keys are rejected; unset keys inherit the universe default.
8607
9152
  type: object
8608
9153
  additionalProperties: {}
8609
9154
  required:
8610
9155
  - name
8611
9156
  - weight
8612
9157
  additionalProperties: false
8613
- description: One variant in the experiment. Weights across all groups must sum to 10000.
9158
+ description: One variant. Its `params` may only override keys declared on the universe schema.
8614
9159
  significance_threshold:
8615
9160
  type: number
8616
9161
  minimum: 0.0001
@@ -8635,7 +9180,7 @@ components:
8635
9180
  items:
8636
9181
  $ref: '#/components/schemas/ExperimentInlineMetric'
8637
9182
  additionalProperties: false
8638
- description: Body for `PATCH /api/admin/experiments/{id}`. Partial — only supplied fields change. `allocation_pct`, `groups`, `salt`, `universe`, `params` are immutable while the experiment is running (stop first).
9183
+ description: Body for `PATCH /api/admin/experiments/{id}`. Partial — only supplied fields change. `allocation_pct`, `salt`, `universe` and existing group weights/values are immutable while running (stop first); a new variant may still be appended into the reserved tail.
8639
9184
  UpdateExperimentResponse:
8640
9185
  type: object
8641
9186
  properties:
@@ -8694,6 +9239,12 @@ components:
8694
9239
  - guardrail
8695
9240
  - secondary
8696
9241
  description: Metric role. `goal` drives the decision, `guardrail` blocks ship if degraded, `secondary` is informational.
9242
+ min_effect_of_interest:
9243
+ description: Per-experiment override of the metric's `default_min_effect_of_interest` (relative, 0–1) — the smallest change worth acting on for THIS experiment's decision, which depends on the intervention's cost/risk. `null`/omitted falls back to the metric default. For a `guardrail` this is the non-inferiority margin (how large a regression is tolerated); for a `goal` it is the superiority threshold.
9244
+ default: null
9245
+ anyOf:
9246
+ - type: number
9247
+ - type: 'null'
8697
9248
  required:
8698
9249
  - metric_id
8699
9250
  - role
@@ -8721,9 +9272,15 @@ components:
8721
9272
  - goal
8722
9273
  - guardrail
8723
9274
  - secondary
9275
+ min_effect_of_interest:
9276
+ description: The resolved per-experiment override, or `null` when this attachment inherits the metric's `default_min_effect_of_interest`.
9277
+ anyOf:
9278
+ - type: number
9279
+ - type: 'null'
8724
9280
  required:
8725
9281
  - metric_id
8726
9282
  - role
9283
+ - min_effect_of_interest
8727
9284
  additionalProperties: false
8728
9285
  required:
8729
9286
  - id
@@ -8787,6 +9344,11 @@ components:
8787
9344
  - type: number
8788
9345
  - type: 'null'
8789
9346
  description: '`1` if sample-ratio mismatch detected, else `0`.'
9347
+ realized_mde:
9348
+ anyOf:
9349
+ - type: number
9350
+ - type: 'null'
9351
+ description: Realized minimum detectable effect (absolute, in metric units) — the smallest true effect this run could detect at the experiment's significance level and target power GIVEN the observed variance and per-arm sample sizes. A derived OUTPUT, not an input knob; compare it against the metric's minimum effect of interest to judge whether the run is adequately powered. `null` on the control row and on empty arms.
8790
9352
  required:
8791
9353
  - metric
8792
9354
  - group_name
@@ -8796,6 +9358,7 @@ components:
8796
9358
  - delta_pct
8797
9359
  - p_value
8798
9360
  - srm_detected
9361
+ - realized_mde
8799
9362
  additionalProperties: false
8800
9363
  verdict:
8801
9364
  type: string
@@ -9602,6 +10165,35 @@ components:
9602
10165
  - version
9603
10166
  - published
9604
10167
  additionalProperties: false
10168
+ UniverseParam:
10169
+ type: object
10170
+ properties:
10171
+ name:
10172
+ type: string
10173
+ minLength: 1
10174
+ maxLength: 128
10175
+ pattern: ^[a-zA-Z_][a-zA-Z0-9_]*$
10176
+ description: Param key, referenced by `groups[].params` on every experiment in the universe.
10177
+ type:
10178
+ type: string
10179
+ enum:
10180
+ - bool
10181
+ - string
10182
+ - int
10183
+ - number
10184
+ description: Scalar type of the param's value.
10185
+ default:
10186
+ description: 'Universe-wide default. Returned when the assigned variant doesn''t override this param (resolution: variant override → universe default).'
10187
+ required:
10188
+ - name
10189
+ - type
10190
+ - default
10191
+ additionalProperties: false
10192
+ UniverseParamSchema:
10193
+ type: array
10194
+ items:
10195
+ $ref: '#/components/schemas/UniverseParam'
10196
+ description: The universe's ordered config schema — the single source of truth for its params.
9605
10197
  ListUniversesResponse:
9606
10198
  type: object
9607
10199
  properties:
@@ -9618,6 +10210,11 @@ components:
9618
10210
  maxLength: 128
9619
10211
  pattern: ^[a-z0-9](?:[a-z0-9_-]*[a-z0-9])?(?:\.[a-z0-9](?:[a-z0-9_-]*[a-z0-9])?)?$
9620
10212
  description: Stable universe key. Single segment or `folder.name`. Lowercase letters, digits, `_` or `-`; max 128 chars. Immutable after create.
10213
+ description:
10214
+ anyOf:
10215
+ - type: string
10216
+ - type: 'null'
10217
+ description: Human-readable blurb shown in the universe picker/hovercard.
9621
10218
  unitType:
9622
10219
  type: string
9623
10220
  description: Unit of randomisation. Snake-case in request (`unit_type`), camelCase in response.
@@ -9630,6 +10227,16 @@ components:
9630
10227
  maxItems: 2
9631
10228
  - type: 'null'
9632
10229
  description: Reserved holdout bucket range, or `null` if none.
10230
+ recommendedHeadroom:
10231
+ type: integer
10232
+ minimum: 0
10233
+ maximum: 10000
10234
+ description: Basis points of headroom seeded into each new experiment's reserved tail (0 = none).
10235
+ paramSchema:
10236
+ anyOf:
10237
+ - $ref: '#/components/schemas/UniverseParamSchema'
10238
+ - type: 'null'
10239
+ description: The universe-owned config schema, or `null` on pre-rework universes not yet seeded.
9633
10240
  createdAt:
9634
10241
  type: string
9635
10242
  description: ISO-8601 timestamp of creation.
@@ -9658,6 +10265,13 @@ components:
9658
10265
  description: Stable universe key. Single segment or `folder.name`. Lowercase letters, digits, `_` or `-`; max 128 chars. Immutable after create.
9659
10266
  folder:
9660
10267
  $ref: '#/components/schemas/Folder'
10268
+ description:
10269
+ default: null
10270
+ anyOf:
10271
+ - type: string
10272
+ maxLength: 2000
10273
+ - type: 'null'
10274
+ description: Human-readable blurb shown in the universe picker/hovercard.
9661
10275
  unit_type:
9662
10276
  default: user_id
9663
10277
  description: Unit of randomisation. Typically `user_id`. Use `account_id` to keep whole accounts in the same group across an experiment.
@@ -9674,6 +10288,18 @@ components:
9674
10288
  maxItems: 2
9675
10289
  - type: 'null'
9676
10290
  description: Inclusive `[lo, hi]` bucket range (0–9999) reserved as the **holdout** — callers hashed into this slice are excluded from every experiment in the universe. `null` disables the holdout. Pro plan or higher required.
10291
+ recommended_headroom:
10292
+ default: 0
10293
+ type: integer
10294
+ minimum: 0
10295
+ maximum: 10000
10296
+ description: Basis points of reserved headroom seeded into each new experiment created in this universe (0 = none). Lets variants be appended into a running experiment without reshuffling.
10297
+ param_schema:
10298
+ default: null
10299
+ anyOf:
10300
+ - $ref: '#/components/schemas/UniverseParamSchema'
10301
+ - type: 'null'
10302
+ description: The universe-owned config schema — an ordered `{ name, type, default }[]`. Experiments may only override values per variant, never add fields. `null` starts an empty schema.
9677
10303
  required:
9678
10304
  - name
9679
10305
  additionalProperties: false
@@ -9707,6 +10333,12 @@ components:
9707
10333
  properties:
9708
10334
  folder:
9709
10335
  $ref: '#/components/schemas/Folder'
10336
+ description:
10337
+ anyOf:
10338
+ - type: string
10339
+ maxLength: 2000
10340
+ - type: 'null'
10341
+ description: Human-readable blurb shown in the universe picker/hovercard.
9710
10342
  holdout_range:
9711
10343
  anyOf:
9712
10344
  - type: array
@@ -9718,8 +10350,18 @@ components:
9718
10350
  maxItems: 2
9719
10351
  - type: 'null'
9720
10352
  description: Inclusive `[lo, hi]` bucket range (0–9999) reserved as the **holdout** — callers hashed into this slice are excluded from every experiment in the universe. `null` disables the holdout. Pro plan or higher required.
10353
+ recommended_headroom:
10354
+ type: integer
10355
+ minimum: 0
10356
+ maximum: 10000
10357
+ description: Basis points of reserved headroom seeded into new experiments in this universe.
10358
+ param_schema:
10359
+ anyOf:
10360
+ - $ref: '#/components/schemas/UniverseParamSchema'
10361
+ - type: 'null'
10362
+ description: Replace the universe config schema. Additive changes + default edits are always allowed; removing a param a running experiment overrides is rejected (deprecate-only).
9721
10363
  additionalProperties: false
9722
- description: Body for `PATCH /api/admin/universes/{id}`. Only `holdout_range` is mutable — name and unit_type are immutable after create.
10364
+ description: Body for `PATCH /api/admin/universes/{id}`. `name` and `unit_type` are immutable after create; `holdout_range`, `description`, `recommended_headroom` and `param_schema` are mutable.
9723
10365
  UpdateUniverseResponse:
9724
10366
  type: object
9725
10367
  properties:
@@ -9729,7 +10371,7 @@ components:
9729
10371
  required:
9730
10372
  - id
9731
10373
  additionalProperties: false
9732
- GateTemplateRule:
10374
+ GateTemplateRuleResponse:
9733
10375
  type: object
9734
10376
  properties:
9735
10377
  attr:
@@ -9760,9 +10402,8 @@ components:
9760
10402
  required:
9761
10403
  - attr
9762
10404
  - op
9763
- - value
9764
10405
  additionalProperties: false
9765
- description: Single targeting predicate. Copy a template's rules, substitute the concrete `value`(s), and pass them as the `rules` arg of `release_flags_create`.
10406
+ description: Single targeting predicate as returned by the API. `value` is optional here — read-only built-in templates (e.g. `@owner`, or presets whose operand is supplied at substitution time) legitimately omit it. Request bodies still require `value`.
9766
10407
  GateTemplate:
9767
10408
  type: object
9768
10409
  description: 'A reusable targeting-rule template. Built-ins are read-only (`builtin: true`, stable slug `id`); per-project customer templates are `builtin: false` with a `gtpl_…` id.'
@@ -9796,7 +10437,7 @@ components:
9796
10437
  rules:
9797
10438
  type: array
9798
10439
  items:
9799
- $ref: '#/components/schemas/GateTemplateRule'
10440
+ $ref: '#/components/schemas/GateTemplateRuleResponse'
9800
10441
  description: The rule definition — copy, substitute the value(s), pass as `rules`.
9801
10442
  createdAt:
9802
10443
  anyOf:
@@ -9832,6 +10473,46 @@ components:
9832
10473
  - data
9833
10474
  - next_cursor
9834
10475
  additionalProperties: false
10476
+ GateTemplateRule:
10477
+ type: object
10478
+ properties:
10479
+ attr:
10480
+ type: string
10481
+ description: Attribute key on the evaluation context (e.g. `country`, `plan`, `email`). Matched case-sensitively against `Shipeasy.checkGate(user, name)` input. May be empty on a built-in template whose attribute is supplied at substitution time (e.g. the `exp_in` "in any experiment" preset — you fill in the experiment name).
10482
+ op:
10483
+ type: string
10484
+ enum:
10485
+ - eq
10486
+ - neq
10487
+ - in
10488
+ - not_in
10489
+ - gt
10490
+ - gte
10491
+ - lt
10492
+ - lte
10493
+ - contains
10494
+ - regex
10495
+ - semver_gt
10496
+ - semver_gte
10497
+ - semver_lt
10498
+ - semver_lte
10499
+ - gate_pass
10500
+ - exp_in
10501
+ description: 'Comparison operator. Equality: `eq`/`neq`. Set membership: `in`/`not_in` (value is an array). Numeric order: `gt`/`gte`/`lt`/`lte`. Semver order: `semver_gt`/`semver_gte`/`semver_lt`/`semver_lte`. String: `contains` (substring), `regex` (JS-flavour pattern). Cross-resource: `gate_pass` (value is another gate''s name), `exp_in` (attr is an experiment name, value is a variant / `"$holdout"` / `"$any"`).'
10502
+ value:
10503
+ description: 'Operand. Shape depends on `op`: scalar for `eq/neq/gt/gte/lt/lte/contains/semver_*`; array of scalars for `in/not_in`; string pattern for `regex`; referenced gate name for `gate_pass`; variant selector for `exp_in`. Required on every request rule (value-ops need it; the valueless built-in presets are read-only and never created via the API).'
10504
+ anyOf:
10505
+ - type: string
10506
+ - type: number
10507
+ - type: boolean
10508
+ - type: array
10509
+ items: {}
10510
+ required:
10511
+ - attr
10512
+ - op
10513
+ - value
10514
+ additionalProperties: false
10515
+ description: Single targeting predicate. Copy a template's rules, substitute the concrete `value`(s), and pass them as the `rules` arg of `release_flags_create`.
9835
10516
  CreateGateTemplateRequest:
9836
10517
  type: object
9837
10518
  description: Body for `POST /api/admin/gate-templates`. Creates a per-project (customer) template. At minimum supply `name` + `rules`.
@@ -10393,8 +11074,8 @@ components:
10393
11074
  winsorizePct:
10394
11075
  description: Winsorise percentile applied to the metric.
10395
11076
  type: number
10396
- minDetectableEffect:
10397
- description: Configured MDE, or `null`.
11077
+ defaultMinEffectOfInterest:
11078
+ description: Metric-level default minimum effect of interest (relative, 0–1), or `null`.
10398
11079
  anyOf:
10399
11080
  - type: number
10400
11081
  - type: 'null'
@@ -10429,9 +11110,9 @@ components:
10429
11110
  type: integer
10430
11111
  minimum: 1
10431
11112
  maximum: 99
10432
- MetricMinDetectableEffect:
11113
+ MetricDefaultMinEffectOfInterest:
10433
11114
  default: null
10434
- description: Minimum detectable effect (relative, 0–1) for power planning. `null` to omit.
11115
+ description: Default minimum effect of interest (relative, 0–1) — the smallest change in this metric worth acting on, used as the power-planning baseline. Intrinsic to the metric; an experiment overrides it per-attachment with `min_effect_of_interest` when a specific decision has a different cost/risk bar. `null` to omit.
10435
11116
  anyOf:
10436
11117
  - type: number
10437
11118
  - type: 'null'
@@ -10456,8 +11137,8 @@ components:
10456
11137
  $ref: '#/components/schemas/MetricQueryDsl'
10457
11138
  winsorize_pct:
10458
11139
  $ref: '#/components/schemas/MetricWinsorizePct'
10459
- min_detectable_effect:
10460
- $ref: '#/components/schemas/MetricMinDetectableEffect'
11140
+ default_min_effect_of_interest:
11141
+ $ref: '#/components/schemas/MetricDefaultMinEffectOfInterest'
10461
11142
  direction:
10462
11143
  $ref: '#/components/schemas/MetricDirection'
10463
11144
  required:
@@ -10479,8 +11160,8 @@ components:
10479
11160
  $ref: '#/components/schemas/QueryIr'
10480
11161
  winsorize_pct:
10481
11162
  $ref: '#/components/schemas/MetricWinsorizePct'
10482
- min_detectable_effect:
10483
- $ref: '#/components/schemas/MetricMinDetectableEffect'
11163
+ default_min_effect_of_interest:
11164
+ $ref: '#/components/schemas/MetricDefaultMinEffectOfInterest'
10484
11165
  direction:
10485
11166
  $ref: '#/components/schemas/MetricDirection'
10486
11167
  required:
@@ -10542,8 +11223,8 @@ components:
10542
11223
  winsorizePct:
10543
11224
  description: Winsorise percentile applied to the metric.
10544
11225
  type: number
10545
- minDetectableEffect:
10546
- description: Configured MDE, or `null`.
11226
+ defaultMinEffectOfInterest:
11227
+ description: Metric-level default minimum effect of interest (relative, 0–1), or `null`.
10547
11228
  anyOf:
10548
11229
  - type: number
10549
11230
  - type: 'null'
@@ -10583,8 +11264,8 @@ components:
10583
11264
  $ref: '#/components/schemas/MetricQueryDsl'
10584
11265
  winsorize_pct:
10585
11266
  $ref: '#/components/schemas/MetricWinsorizePct'
10586
- min_detectable_effect:
10587
- $ref: '#/components/schemas/MetricMinDetectableEffect'
11267
+ default_min_effect_of_interest:
11268
+ $ref: '#/components/schemas/MetricDefaultMinEffectOfInterest'
10588
11269
  direction:
10589
11270
  $ref: '#/components/schemas/MetricDirection'
10590
11271
  required:
@@ -10602,8 +11283,8 @@ components:
10602
11283
  $ref: '#/components/schemas/QueryIr'
10603
11284
  winsorize_pct:
10604
11285
  $ref: '#/components/schemas/MetricWinsorizePct'
10605
- min_detectable_effect:
10606
- $ref: '#/components/schemas/MetricMinDetectableEffect'
11286
+ default_min_effect_of_interest:
11287
+ $ref: '#/components/schemas/MetricDefaultMinEffectOfInterest'
10607
11288
  direction:
10608
11289
  $ref: '#/components/schemas/MetricDirection'
10609
11290
  required:
@@ -10619,12 +11300,12 @@ components:
10619
11300
  $ref: '#/components/schemas/MetricEventName'
10620
11301
  winsorize_pct:
10621
11302
  $ref: '#/components/schemas/MetricWinsorizePct'
10622
- min_detectable_effect:
10623
- $ref: '#/components/schemas/MetricMinDetectableEffect'
11303
+ default_min_effect_of_interest:
11304
+ $ref: '#/components/schemas/MetricDefaultMinEffectOfInterest'
10624
11305
  direction:
10625
11306
  $ref: '#/components/schemas/MetricDirection'
10626
11307
  additionalProperties: false
10627
- description: Update-metric variant that leaves the query untouched (metadata-only edit — folder, event, winsorisation, MDE, direction).
11308
+ description: Update-metric variant that leaves the query untouched (metadata-only edit — folder, event, winsorisation, default minimum effect of interest, direction).
10628
11309
  UpdateMetricRequest:
10629
11310
  oneOf:
10630
11311
  - $ref: '#/components/schemas/UpdateMetricWithQuery'
@@ -11293,6 +11974,11 @@ components:
11293
11974
  items:
11294
11975
  type: string
11295
11976
  description: Distinct, id-normalized routes this error has surfaced on (UUIDs/ids collapsed to `#`).
11977
+ stackTail:
11978
+ anyOf:
11979
+ - type: string
11980
+ - type: 'null'
11981
+ description: The last 3 non-empty lines of the latest occurrence's stack — the innermost frames closest to where the throw bubbled out, baked in as an at-a-glance locator. `null` when the source supplied no stack. Fetch the full stack via `related.error`.
11296
11982
  sdkVersion:
11297
11983
  anyOf:
11298
11984
  - type: string
@@ -11482,6 +12168,11 @@ components:
11482
12168
  OpsMeasurePlanContext:
11483
12169
  type: object
11484
12170
  properties:
12171
+ goal:
12172
+ anyOf:
12173
+ - type: string
12174
+ - type: 'null'
12175
+ description: The customer's underlying intent in their own words — WHAT they want to learn or achieve and WHY, independent of the specific steps. The plan below is a best-effort proposal made without repo access; when the code reveals a better path, this goal is the target you optimize for, not literal step-adherence.
11485
12176
  created:
11486
12177
  type: array
11487
12178
  items:
@@ -11888,6 +12579,91 @@ components:
11888
12579
  - id
11889
12580
  additionalProperties: false
11890
12581
  description: Response for the update / link-pr endpoints.
12582
+ OpsCommentAuthorType:
12583
+ type: string
12584
+ enum:
12585
+ - user
12586
+ - system
12587
+ description: Who wrote a comment. `user` is a teammate (their email is in `authorEmail`); `system` is Jarvis — the AI agent — which authors comments when an ops-notify escalation lands on the item and when it is `@shipeasy`-mentioned in a thread.
12588
+ OpsComment:
12589
+ type: object
12590
+ properties:
12591
+ id:
12592
+ type: string
12593
+ description: Comment id.
12594
+ feedbackId:
12595
+ type: string
12596
+ description: The queue item this comment hangs off.
12597
+ parentId:
12598
+ anyOf:
12599
+ - type: string
12600
+ - type: 'null'
12601
+ description: The top-level comment this is a reply to, or `null` for a top-level comment.
12602
+ authorType:
12603
+ $ref: '#/components/schemas/OpsCommentAuthorType'
12604
+ authorEmail:
12605
+ anyOf:
12606
+ - type: string
12607
+ - type: 'null'
12608
+ description: The teammate who wrote it, or `null` for a `system` (Jarvis) comment.
12609
+ body:
12610
+ type: string
12611
+ description: Comment body as markdown (may contain sanitised inline image/video embeds).
12612
+ createdAt:
12613
+ type: string
12614
+ description: ISO-8601 creation timestamp.
12615
+ updatedAt:
12616
+ type: string
12617
+ description: ISO-8601 last-edit timestamp.
12618
+ required:
12619
+ - id
12620
+ - feedbackId
12621
+ - parentId
12622
+ - authorType
12623
+ - authorEmail
12624
+ - body
12625
+ - createdAt
12626
+ - updatedAt
12627
+ additionalProperties: false
12628
+ description: A single comment on a queue item.
12629
+ ListOpsCommentsResponse:
12630
+ type: object
12631
+ properties:
12632
+ comments:
12633
+ type: array
12634
+ items:
12635
+ $ref: '#/components/schemas/OpsComment'
12636
+ required:
12637
+ - comments
12638
+ additionalProperties: false
12639
+ description: Response for `GET /api/admin/ops/{handle}/comments` — the thread, oldest first.
12640
+ CreateOpsCommentRequest:
12641
+ type: object
12642
+ properties:
12643
+ body:
12644
+ type: string
12645
+ minLength: 1
12646
+ maxLength: 10000
12647
+ description: The comment body as markdown. Mentions (`@teammate`, `@shipeasy`) are parsed from it.
12648
+ parentId:
12649
+ anyOf:
12650
+ - type: string
12651
+ - type: 'null'
12652
+ description: Reply under this top-level comment. Omit / `null` for a top-level comment. Replying to a reply attaches to the same top-level parent (threading is one level deep).
12653
+ required:
12654
+ - body
12655
+ additionalProperties: false
12656
+ description: Body for `POST /api/admin/ops/{handle}/comments`.
12657
+ CreateOpsCommentResponse:
12658
+ type: object
12659
+ properties:
12660
+ id:
12661
+ type: string
12662
+ description: Newly created comment id.
12663
+ required:
12664
+ - id
12665
+ additionalProperties: false
12666
+ description: Response for `POST /api/admin/ops/{handle}/comments`.
11891
12667
  NotifyOpsRequest:
11892
12668
  type: object
11893
12669
  properties:
@@ -11907,8 +12683,10 @@ components:
11907
12683
  items:
11908
12684
  type: string
11909
12685
  href:
11910
- description: Dashboard-relative deep link to the related item.
11911
- type: string
12686
+ description: Dashboard-relative deep link to the related item. `null` is accepted and treated as "no link".
12687
+ anyOf:
12688
+ - type: string
12689
+ - type: 'null'
11912
12690
  dedupeKey:
11913
12691
  description: Stable per-escalation key (e.g. `feedback:7`) so re-runs dedupe to one row.
11914
12692
  type: string
@@ -12158,7 +12936,9 @@ components:
12158
12936
  type: string
12159
12937
  enum:
12160
12938
  - free
12161
- - paid
12939
+ - pro
12940
+ - business
12941
+ - enterprise
12162
12942
  description: Billing plan tier.
12163
12943
  status:
12164
12944
  type: string
@@ -12213,6 +12993,50 @@ components:
12213
12993
  - type: boolean
12214
12994
  - type: number
12215
12995
  description: Whether the feedback/ops module is enabled.
12996
+ minSampleSize:
12997
+ type: integer
12998
+ description: Verdict power guard — minimum users per arm before a ship/hold verdict.
12999
+ minRuntimeDays:
13000
+ type: integer
13001
+ description: Minimum days an experiment must run before a verdict (peeking guard).
13002
+ defaultPower:
13003
+ type: number
13004
+ description: Target statistical power (1−β) feeding the realized-MDE calculation.
13005
+ ciConfidence:
13006
+ type: number
13007
+ description: Confidence level for the interval surfaced on results.
13008
+ defaultAllocationPct:
13009
+ type: integer
13010
+ description: Default traffic allocation (basis points) new experiments start with.
13011
+ defaultHoldout:
13012
+ type: integer
13013
+ description: Default holdout carve-out (basis points) that seeds each new universe's holdout.
13014
+ defaultWinsorizePct:
13015
+ type: integer
13016
+ description: Default winsorization percentile new metrics start with.
13017
+ defaultMei:
13018
+ anyOf:
13019
+ - type: number
13020
+ - type: 'null'
13021
+ description: Default minimum effect of interest (relative, 0–1) new metrics start with, or null.
13022
+ cupedBaselineDays:
13023
+ type: integer
13024
+ description: CUPED baseline window — days of pre-experiment history, frozen at start.
13025
+ cupedMinOverlap:
13026
+ type: number
13027
+ description: CUPED selection-bias guard — min share of users with a baseline, else skip.
13028
+ cupedMinBaselineUsers:
13029
+ type: integer
13030
+ description: CUPED — minimum users with a baseline before it runs at all.
13031
+ msprtTauMeiFactor:
13032
+ type: number
13033
+ description: mSPRT prior width — τ = minimum effect of interest × this factor.
13034
+ msprtTauSdFactor:
13035
+ type: number
13036
+ description: mSPRT fallback prior width — τ = this × control SD when no MEI is set.
13037
+ srmThreshold:
13038
+ type: number
13039
+ description: SRM chi-square p-value below which the run is called invalid.
12216
13040
  createdAt:
12217
13041
  type: string
12218
13042
  description: ISO-8601 timestamp of project creation.
@@ -12363,6 +13187,78 @@ components:
12363
13187
  moduleEvents:
12364
13188
  type: boolean
12365
13189
  description: Enable/disable the events module.
13190
+ minSampleSize:
13191
+ type: integer
13192
+ minimum: 2
13193
+ maximum: 1000000
13194
+ description: Verdict power guard — minimum users per arm before a ship/hold verdict.
13195
+ minRuntimeDays:
13196
+ type: integer
13197
+ minimum: 0
13198
+ maximum: 365
13199
+ description: Minimum days an experiment must run before a verdict (peeking guard).
13200
+ defaultPower:
13201
+ type: number
13202
+ minimum: 0.5
13203
+ maximum: 0.99
13204
+ description: Target statistical power (1−β) feeding the realized-MDE calculation.
13205
+ ciConfidence:
13206
+ type: number
13207
+ minimum: 0.5
13208
+ maximum: 0.999
13209
+ description: Confidence level for the interval surfaced on results (any value in [0.5, 0.999], e.g. 0.90, 0.95, 0.975, 0.99).
13210
+ defaultAllocationPct:
13211
+ type: integer
13212
+ minimum: 1
13213
+ maximum: 10000
13214
+ description: Default traffic allocation (basis points, 1000 = 10%) new experiments start with; overridable per experiment.
13215
+ defaultHoldout:
13216
+ type: integer
13217
+ minimum: 0
13218
+ maximum: 10000
13219
+ description: Default holdout carve-out (basis points) that seeds each new universe's holdout (0 = none).
13220
+ defaultWinsorizePct:
13221
+ type: integer
13222
+ minimum: 1
13223
+ maximum: 99
13224
+ description: Default winsorization percentile new metrics start with; overridable per metric.
13225
+ defaultMei:
13226
+ anyOf:
13227
+ - type: number
13228
+ minimum: 0
13229
+ maximum: 1
13230
+ - type: 'null'
13231
+ description: Default minimum effect of interest (relative, 0–1) new metrics start with; overridable per metric and per experiment. Null clears it.
13232
+ cupedBaselineDays:
13233
+ type: integer
13234
+ minimum: 1
13235
+ maximum: 365
13236
+ description: CUPED baseline window — days of pre-experiment history, frozen at start.
13237
+ cupedMinOverlap:
13238
+ type: number
13239
+ minimum: 0.01
13240
+ maximum: 0.99
13241
+ description: CUPED selection-bias guard — min share of users with a baseline, else skip.
13242
+ cupedMinBaselineUsers:
13243
+ type: integer
13244
+ minimum: 10
13245
+ maximum: 100000
13246
+ description: CUPED — minimum users with a baseline before it runs at all.
13247
+ msprtTauMeiFactor:
13248
+ type: number
13249
+ minimum: 0.1
13250
+ maximum: 2
13251
+ description: mSPRT prior width — τ = minimum effect of interest × this factor.
13252
+ msprtTauSdFactor:
13253
+ type: number
13254
+ minimum: 0.05
13255
+ maximum: 1
13256
+ description: mSPRT fallback prior width — τ = this × control SD when no MEI is set.
13257
+ srmThreshold:
13258
+ type: number
13259
+ minimum: 0.0001
13260
+ maximum: 0.05
13261
+ description: SRM chi-square p-value below which the run is called invalid.
12366
13262
  additionalProperties: false
12367
13263
  description: Body for `PATCH /api/admin/projects/{id}`. Partial — every field is optional; only supplied fields change. The project id in the path must match the caller's own project.
12368
13264
  ListI18nProfilesResponse:
@@ -12839,6 +13735,59 @@ components:
12839
13735
  - kv_verified
12840
13736
  additionalProperties: false
12841
13737
  description: Result of a one-shot set-and-publish.
13738
+ ErrorOccurrence:
13739
+ type: object
13740
+ description: One sampled instance behind a tracked error — the minimal per-occurrence payload (what varies between instances; everything else lives on the parent `ErrorRecord`).
13741
+ properties:
13742
+ id:
13743
+ type: string
13744
+ description: Stable opaque occurrence id.
13745
+ message:
13746
+ type: string
13747
+ description: This instance's raw message (unlike the fingerprint, never normalized).
13748
+ stack:
13749
+ description: This instance's stack trace, or `null` if none was captured.
13750
+ anyOf:
13751
+ - type: string
13752
+ - type: 'null'
13753
+ url:
13754
+ description: This instance's raw URL (ids intact), or `null`.
13755
+ anyOf:
13756
+ - type: string
13757
+ - type: 'null'
13758
+ env:
13759
+ description: Published env this instance ran against, or `null`.
13760
+ anyOf:
13761
+ - type: string
13762
+ - type: 'null'
13763
+ side:
13764
+ description: Which SDK side reported it — `client` or `server`. `null` if unknown.
13765
+ anyOf:
13766
+ - type: string
13767
+ - type: 'null'
13768
+ sdkVersion:
13769
+ description: '`@shipeasy/sdk` version of this instance, or `null`.'
13770
+ anyOf:
13771
+ - type: string
13772
+ - type: 'null'
13773
+ extrasJson:
13774
+ description: This instance's sanitized extras, JSON-encoded. `null` if none.
13775
+ anyOf:
13776
+ - type: string
13777
+ - type: 'null'
13778
+ sampleRate:
13779
+ type: integer
13780
+ minimum: 1
13781
+ description: 1-in-N sampling rate in force when this instance was kept (`1` = the issue was still being recorded exhaustively).
13782
+ seenAt:
13783
+ type: string
13784
+ description: ISO-8601 timestamp this instance was ingested.
13785
+ required:
13786
+ - id
13787
+ - message
13788
+ - sampleRate
13789
+ - seenAt
13790
+ additionalProperties: false
12842
13791
  ErrorRecord:
12843
13792
  type: object
12844
13793
  description: |-
@@ -12854,7 +13803,7 @@ components:
12854
13803
  description: Project this issue belongs to.
12855
13804
  fingerprint:
12856
13805
  type: string
12857
- description: Stable dedupe key (hash of `errorType` + normalized `message` + consequence). Unique per project — every occurrence with the same fingerprint folds into this one row.
13806
+ description: Stable dedupe key — the issue TITLE (hash of `errorType` + `subject` + `outcome`, plus the violation name for violations). Unique per project — every occurrence with the same title folds into this one row; per-instance variety (message/stack/extras) lives in `occurrences`.
12858
13807
  causedByFingerprint:
12859
13808
  description: Fingerprint of the issue this one descends from — set when `see()` reported the same error (re-throw) or its `{ cause }` (wrap) at an inner boundary first. Points at another error's `fingerprint` in the same project (soft reference, no FK). `null` for a root issue.
12860
13809
  anyOf:
@@ -12932,7 +13881,12 @@ components:
12932
13881
  count:
12933
13882
  type: integer
12934
13883
  minimum: 1
12935
- description: Number of folded occurrences for this fingerprint.
13884
+ description: EXACT number of folded occurrences for this fingerprint — every occurrence increments it, regardless of detail-row sampling.
13885
+ occurrences:
13886
+ type: array
13887
+ description: Sampled per-instance detail rows behind this issue, newest first. Returned only by `GET /api/admin/errors/{id}` (never in list responses). The parent row's `count` / `firstSeenAt` / `lastSeenAt` are exact; these rows are a *sampled sketch* of the instances — exhaustive while the issue is small, thinning to roughly 1-in-10 past 10 occurrences, 1-in-100 past 100, and 1-in-1000 past 1000 (each row's `sampleRate` records the rate in force when it was kept), capped at the newest 100 rows.
13888
+ items:
13889
+ $ref: '#/components/schemas/ErrorOccurrence'
12936
13890
  status:
12937
13891
  type: string
12938
13892
  enum:
@@ -12966,7 +13920,7 @@ components:
12966
13920
  additionalProperties: false
12967
13921
  ListErrorsResponse:
12968
13922
  type: array
12969
- description: A bare JSON array of tracked errors, ordered by `lastSeenAt` descending. There is no pagination envelope — `limit` caps the page size.
13923
+ description: A bare JSON array of tracked errors, ordered by `lastSeenAt` descending. There is no pagination envelope — `limit` caps the page size. List rows never include `occurrences` — read one error to get them.
12970
13924
  items:
12971
13925
  $ref: '#/components/schemas/ErrorRecord'
12972
13926
  UpdateErrorStatusRequest:
@@ -13478,33 +14432,27 @@ components:
13478
14432
  properties:
13479
14433
  ok:
13480
14434
  type: boolean
13481
- description: '`true` when the trigger run was successfully kicked off; `false` when the dispatch itself failed (still HTTP 200, with `error` set).'
13482
- error:
13483
- type: string
13484
- description: Failure reason. Present only when `ok` is `false` — the fire reached the handler but the underlying dispatch to the provider failed.
14435
+ description: Always `true` — a successful fire returns HTTP 200. A dispatch failure returns HTTP 502 with the `Error` envelope, not this body.
13485
14436
  required:
13486
14437
  - ok
13487
14438
  additionalProperties: false
13488
- description: 'Result of firing a trigger connector. `{ ok: true }` on success; `{ ok: false, error }` when the dispatch fails (the request still returns HTTP 200).'
14439
+ description: 'Result of a successful trigger fire: `{ ok: true }`. When the downstream dispatch fails the endpoint returns HTTP 502 (see the `Error` response), never `ok: false`.'
13489
14440
  TestConnectorResponse:
13490
14441
  type: object
13491
14442
  properties:
13492
14443
  ok:
13493
14444
  type: boolean
13494
- description: '`true` when the test dispatch reached the destination; `false` when the dispatch itself failed (still HTTP 200, with `error` set).'
14445
+ description: Always `true` — a successful test returns HTTP 200. A dispatch failure returns HTTP 502 with the `Error` envelope, not this body.
13495
14446
  issueUrl:
13496
14447
  anyOf:
13497
14448
  - type: string
13498
14449
  - type: 'null'
13499
- description: URL of the artifact the test produced (e.g. the GitHub Issue created by a `github` connector), or `null` when the provider produces no linkable artifact (or the dispatch failed).
13500
- error:
13501
- type: string
13502
- description: Failure reason. Present only when `ok` is `false` — the test reached the handler but the underlying dispatch to the provider failed.
14450
+ description: URL of the artifact the test produced (e.g. the GitHub Issue created by a `github` connector), or `null` when the provider produces no linkable artifact.
13503
14451
  required:
13504
14452
  - ok
13505
14453
  - issueUrl
13506
14454
  additionalProperties: false
13507
- description: 'Result of testing a connector. `{ ok: true, issueUrl }` on success; `{ ok: false, issueUrl: null, error }` when the dispatch fails (the request still returns HTTP 200).'
14455
+ description: 'Result of a successful connector test: `{ ok: true, issueUrl }`. When the downstream dispatch fails the endpoint returns HTTP 502 (see the `Error` response), never `ok: false`.'
13508
14456
  UpdateClaudeTriggerRequest:
13509
14457
  type: object
13510
14458
  properties:
@@ -13753,7 +14701,8 @@ components:
13753
14701
  - events:write
13754
14702
  - configs:write
13755
14703
  - experiments:write
13756
- description: Optional permission strings recorded on the key for audit/display.
14704
+ - tickets:public_create
14705
+ description: 'Optional permission strings recorded on the key. `tickets:public_create` is enforced: a client key needs it (plus the project''s `allowPublicTickets` setting) to file a `pending_approval` bug via the public `POST /cli/report` endpoint. The rest are audit/display only.'
13757
14706
  expiresInDays:
13758
14707
  anyOf:
13759
14708
  - type: integer
@@ -13977,3 +14926,18 @@ components:
13977
14926
  value:
13978
14927
  error: The referenced event is still pending review and cannot back a metric yet.
13979
14928
  code: EVENT_PENDING
14929
+ BadGateway:
14930
+ description: |-
14931
+ The request was well-formed and the resource exists, but a downstream provider
14932
+ the endpoint delegates to (e.g. a connector's Slack/GitHub/cloud-agent dispatch)
14933
+ failed. The failure is upstream of this API, not a client error. No stable
14934
+ `ErrorCode` is assigned to upstream failures, so `code` is omitted.
14935
+ content:
14936
+ application/json:
14937
+ schema:
14938
+ $ref: '#/components/schemas/Error'
14939
+ examples:
14940
+ UPSTREAM_FAILURE:
14941
+ summary: Downstream provider dispatch failed
14942
+ value:
14943
+ error: 'GitHub API responded 403: resource not accessible by integration.'