@shipeasy/openapi 2.2.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 (43) hide show
  1. package/dist/{chunk-643YSMLU.js → chunk-LYNWK55D.js} +23 -1
  2. package/dist/chunk-LYNWK55D.js.map +1 -0
  3. package/dist/{chunk-MRW3DJ57.js → chunk-PRN4AEAM.js} +250 -100
  4. package/dist/chunk-PRN4AEAM.js.map +1 -0
  5. package/dist/{client-K0ShmsC7.d.ts → client-DO5HTb_L.d.ts} +690 -143
  6. package/dist/client.d.ts +1 -1
  7. package/dist/client.js +7 -1
  8. package/dist/custom.js +4 -4
  9. package/dist/custom.js.map +1 -1
  10. package/dist/index.d.ts +2 -2
  11. package/dist/index.js +8 -2
  12. package/dist/index.js.map +1 -1
  13. package/dist/schemas.d.ts +686 -73
  14. package/dist/schemas.js +41 -5
  15. package/openapi.json +1486 -521
  16. package/openapi.yaml +1016 -350
  17. package/package.json +1 -1
  18. package/spec/components/errors.yaml +30 -1
  19. package/spec/components/schemas/common.yaml +6 -0
  20. package/spec/components/schemas/errors-api.yaml +66 -3
  21. package/spec/components/schemas/experiment-api-row.yaml +293 -0
  22. package/spec/components/schemas/experiments.yaml +54 -299
  23. package/spec/components/schemas/gates.yaml +19 -0
  24. package/spec/components/schemas/keys.yaml +2 -1
  25. package/spec/components/schemas/metrics.yaml +17 -17
  26. package/spec/components/schemas/ops.yaml +101 -0
  27. package/spec/components/schemas/projects.yaml +119 -1
  28. package/spec/components/schemas/universes.yaml +83 -1
  29. package/spec/openapi.yaml +14 -0
  30. package/spec/paths/alert-rules.yaml +1 -0
  31. package/spec/paths/configs.yaml +1 -0
  32. package/spec/paths/errors.yaml +96 -2
  33. package/spec/paths/events.yaml +1 -0
  34. package/spec/paths/experiments.yaml +8 -0
  35. package/spec/paths/gates.yaml +1 -0
  36. package/spec/paths/keys.yaml +1 -0
  37. package/spec/paths/killswitches.yaml +1 -0
  38. package/spec/paths/metrics.yaml +1 -0
  39. package/spec/paths/ops.yaml +111 -0
  40. package/spec/paths/projects.yaml +2 -2
  41. package/spec/paths/universes.yaml +1 -0
  42. package/dist/chunk-643YSMLU.js.map +0 -1
  43. package/dist/chunk-MRW3DJ57.js.map +0 -1
package/openapi.yaml CHANGED
@@ -178,6 +178,16 @@ tags:
178
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.
179
179
 
180
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`.
181
191
  - name: Projects
182
192
  description: |-
183
193
  Projects: the account-level container every other resource is scoped to.
@@ -433,6 +443,7 @@ paths:
433
443
  - FORBIDDEN
434
444
  - ALREADY_EXISTS
435
445
  - VALIDATION
446
+ - PLAN_LIMIT
436
447
  x-cli:
437
448
  name: create
438
449
  positional:
@@ -943,7 +954,7 @@ paths:
943
954
  content:
944
955
  application/json:
945
956
  schema:
946
- $ref: '#/components/schemas/GetExperimentResponse'
957
+ $ref: '#/components/schemas/ExperimentApiRow'
947
958
  example:
948
959
  id: exp_01j7wb12c3d4e5f6g7h8j9k0l1
949
960
  name: checkout_button_color
@@ -1081,6 +1092,7 @@ paths:
1081
1092
  - GROUPS_WEIGHT_SUM
1082
1093
  - REFERENCED_NOT_FOUND
1083
1094
  - VALIDATION
1095
+ - EXPERIMENT_IMMUTABLE_FIELD
1084
1096
  x-cli:
1085
1097
  name: update
1086
1098
  positional:
@@ -1219,6 +1231,12 @@ paths:
1219
1231
  - NOT_FOUND
1220
1232
  - INVALID_TRANSITION
1221
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
1222
1240
  x-cli:
1223
1241
  positional:
1224
1242
  - id
@@ -1313,6 +1331,7 @@ paths:
1313
1331
  - EVENT_PENDING
1314
1332
  - REFERENCED_NOT_FOUND
1315
1333
  - VALIDATION
1334
+ - METRIC_UNKNOWN_ID
1316
1335
  x-cli:
1317
1336
  name: set-metrics
1318
1337
  positional:
@@ -1708,6 +1727,7 @@ paths:
1708
1727
  - FORBIDDEN
1709
1728
  - ALREADY_EXISTS
1710
1729
  - VALIDATION
1730
+ - PLAN_LIMIT
1711
1731
  x-cli:
1712
1732
  name: create
1713
1733
  positional:
@@ -2428,6 +2448,7 @@ paths:
2428
2448
  - FORBIDDEN
2429
2449
  - ALREADY_EXISTS
2430
2450
  - VALIDATION
2451
+ - PLAN_LIMIT
2431
2452
  x-cli:
2432
2453
  name: create
2433
2454
  positional:
@@ -2982,6 +3003,7 @@ paths:
2982
3003
  - PLAN_REQUIRED
2983
3004
  - ALREADY_EXISTS
2984
3005
  - VALIDATION
3006
+ - PLAN_LIMIT
2985
3007
  x-cli:
2986
3008
  name: create
2987
3009
  positional:
@@ -3794,6 +3816,7 @@ paths:
3794
3816
  - EVENT_PENDING
3795
3817
  - REFERENCED_NOT_FOUND
3796
3818
  - VALIDATION
3819
+ - PLAN_LIMIT
3797
3820
  x-cli:
3798
3821
  name: create
3799
3822
  positional:
@@ -4098,6 +4121,7 @@ paths:
4098
4121
  - FORBIDDEN
4099
4122
  - ALREADY_EXISTS
4100
4123
  - VALIDATION
4124
+ - PLAN_LIMIT
4101
4125
  x-cli:
4102
4126
  name: create
4103
4127
  positional:
@@ -4692,6 +4716,117 @@ paths:
4692
4716
  name: link-pr
4693
4717
  positional:
4694
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
4695
4830
  /api/admin/notifications:
4696
4831
  post:
4697
4832
  operationId: notifyOps
@@ -4924,6 +5059,7 @@ paths:
4924
5059
  - UNAUTHORIZED
4925
5060
  - FORBIDDEN
4926
5061
  - ALREADY_EXISTS
5062
+ - METRIC_NOT_FOUND
4927
5063
  - REFERENCED_NOT_FOUND
4928
5064
  - VALIDATION
4929
5065
  x-cli:
@@ -5076,7 +5212,7 @@ paths:
5076
5212
  name: Acme
5077
5213
  domain: acme.com
5078
5214
  ownerEmail: owner@acme.com
5079
- plan: paid
5215
+ plan: pro
5080
5216
  status: active
5081
5217
  subscriptionStatus: active
5082
5218
  billingInterval: monthly
@@ -5223,7 +5359,7 @@ paths:
5223
5359
  name: Acme Rockets
5224
5360
  domain: acme.com
5225
5361
  ownerEmail: owner@acme.com
5226
- plan: paid
5362
+ plan: pro
5227
5363
  status: active
5228
5364
  subscriptionStatus: active
5229
5365
  billingInterval: monthly
@@ -5915,9 +6051,9 @@ paths:
5915
6051
  operationId: getError
5916
6052
  summary: Get a tracked error
5917
6053
  description: |-
5918
- 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.
5919
6055
 
5920
- **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.
5921
6057
  tags:
5922
6058
  - Errors
5923
6059
  parameters:
@@ -5961,6 +6097,31 @@ paths:
5961
6097
  lastSeenAt: '2026-06-28T14:55:31.000Z'
5962
6098
  createdAt: '2026-06-20T08:11:02.000Z'
5963
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'
5964
6125
  '400':
5965
6126
  $ref: '#/components/responses/BadRequest'
5966
6127
  '401':
@@ -6122,6 +6283,75 @@ paths:
6122
6283
  name: file
6123
6284
  positional:
6124
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
6125
6355
  /api/admin/errors/{id}/series:
6126
6356
  post:
6127
6357
  operationId: getErrorSeries
@@ -7131,6 +7361,7 @@ paths:
7131
7361
  - UNAUTHORIZED
7132
7362
  - FORBIDDEN
7133
7363
  - VALIDATION
7364
+ - PLAN_LIMIT
7134
7365
  x-cli:
7135
7366
  name: create
7136
7367
  /api/admin/keys/{id}/revoke:
@@ -7309,6 +7540,12 @@ components:
7309
7540
  minimum: 0
7310
7541
  maximum: 1
7311
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).
7312
7549
  rolloutPct:
7313
7550
  type: integer
7314
7551
  minimum: 0
@@ -7600,6 +7837,15 @@ components:
7600
7837
  - GROUPS_WEIGHT_SUM
7601
7838
  - EVENT_PENDING
7602
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
7603
7849
  x-enum-descriptions:
7604
7850
  BAD_REQUEST: Malformed request (bad JSON, missing project scope).
7605
7851
  UNAUTHORIZED: Missing or invalid admin SDK key.
@@ -7616,9 +7862,18 @@ components:
7616
7862
  GROUPS_WEIGHT_SUM: Experiment group weights must sum to 10000 (basis points).
7617
7863
  EVENT_PENDING: The referenced event is still pending review and cannot back a metric yet.
7618
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.
7619
7874
  Error:
7620
7875
  type: object
7621
- 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.
7622
7877
  required:
7623
7878
  - error
7624
7879
  additionalProperties: false
@@ -7631,9 +7886,18 @@ components:
7631
7886
  detail:
7632
7887
  type: string
7633
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.
7634
7894
  instructions:
7635
7895
  type: string
7636
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.
7637
7901
  Folder:
7638
7902
  anyOf:
7639
7903
  - type: string
@@ -7649,6 +7913,13 @@ components:
7649
7913
  maxLength: 128
7650
7914
  pattern: ^[a-z0-9](?:[a-z0-9_-]*[a-z0-9])?(?:\.[a-z0-9](?:[a-z0-9_-]*[a-z0-9])?)?$
7651
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`.
7652
7923
  enabled:
7653
7924
  default: true
7654
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.
@@ -7950,6 +8221,12 @@ components:
7950
8221
  UpdateGateRequest:
7951
8222
  type: object
7952
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).
7953
8230
  rollout_pct:
7954
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.
7955
8232
  type: integer
@@ -8241,155 +8518,292 @@ components:
8241
8518
  - id
8242
8519
  - enabled
8243
8520
  additionalProperties: false
8244
- ListExperimentsResponse:
8521
+ ExperimentApiRow:
8245
8522
  type: object
8246
8523
  properties:
8247
- data:
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:
8248
8622
  type: array
8249
8623
  items:
8250
8624
  type: object
8251
8625
  properties:
8252
- id:
8253
- type: string
8254
- description: Stable opaque experiment id (`exp_…`).
8255
8626
  name:
8256
8627
  type: string
8257
- maxLength: 128
8258
- pattern: ^[a-z0-9](?:[a-z0-9_-]*[a-z0-9])?(?:\.[a-z0-9](?:[a-z0-9_-]*[a-z0-9])?)?$
8259
- 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.
8260
- description:
8261
- anyOf:
8262
- - type: string
8263
- - type: 'null'
8264
- hypothesis:
8265
- anyOf:
8266
- - type: string
8267
- - type: 'null'
8268
- tag:
8269
- anyOf:
8270
- - type: string
8271
- - type: 'null'
8272
- ownerEmail:
8273
- anyOf:
8274
- - type: string
8275
- - type: 'null'
8276
- audience:
8277
- anyOf:
8278
- - type: string
8279
- - type: 'null'
8280
- bucketBy:
8281
- anyOf:
8282
- - type: string
8283
- minLength: 1
8284
- maxLength: 128
8285
- pattern: ^[a-zA-Z_][a-zA-Z0-9_]*$
8286
- 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.
8287
- - type: 'null'
8288
- folder:
8289
- anyOf:
8290
- - type: string
8291
- - type: 'null'
8292
- status:
8293
- type: string
8294
- enum:
8295
- - draft
8296
- - running
8297
- - stopped
8298
- - archived
8299
- universe:
8300
- type: string
8301
- description: Universe name this experiment draws from.
8302
- targetingGate:
8303
- anyOf:
8304
- - type: string
8305
- - type: 'null'
8306
- allocationPct:
8628
+ minLength: 1
8629
+ maxLength: 64
8630
+ description: Group label (e.g. `control`, `treatment_a`). Max 64 chars.
8631
+ weight:
8307
8632
  type: integer
8308
- minimum: -9007199254740991
8309
- maximum: 9007199254740991
8310
- description: Allocation in basis points (0–10000).
8311
- salt:
8312
- 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%).
8313
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.
8314
8639
  type: object
8315
- 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:
8316
8704
  type: string
8317
- enum:
8318
- - string
8319
- - bool
8320
- - number
8321
- groups:
8322
- type: array
8323
- items:
8324
- type: object
8325
- properties:
8326
- name:
8327
- type: string
8328
- minLength: 1
8329
- maxLength: 64
8330
- description: Group label (e.g. `control`, `treatment_a`). Max 64 chars.
8331
- weight:
8332
- type: integer
8333
- minimum: 0
8334
- maximum: 10000
8335
- description: Allocation weight in basis points (0–10000). The sum across all groups must equal exactly **10000** (100%).
8336
- params:
8337
- default: {}
8338
- 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.
8339
- type: object
8340
- additionalProperties: {}
8341
- required:
8342
- - name
8343
- - weight
8344
- additionalProperties: false
8345
- description: One variant in the experiment. Weights across all groups must sum to 10000.
8346
- significanceThreshold:
8347
- type: number
8348
- minRuntimeDays:
8349
- type: integer
8350
- minimum: -9007199254740991
8351
- maximum: 9007199254740991
8352
- minSampleSize:
8353
- type: integer
8354
- minimum: -9007199254740991
8355
- maximum: 9007199254740991
8356
- sequentialTesting:
8357
- type: boolean
8358
- startedAt:
8359
- anyOf:
8360
- - type: string
8361
- - type: 'null'
8362
- description: ISO-8601 timestamp the experiment last transitioned to `running`, or `null`.
8363
- 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:
8364
8723
  anyOf:
8365
8724
  - type: string
8366
8725
  - type: 'null'
8367
- updatedAt:
8368
- type: string
8369
8726
  required:
8370
8727
  - id
8371
8728
  - name
8372
- - description
8373
- - hypothesis
8374
- - tag
8375
- - ownerEmail
8376
- - audience
8377
- - bucketBy
8378
- - folder
8379
- - status
8380
- - universe
8381
- - targetingGate
8382
- - allocationPct
8383
- - salt
8384
- - params
8385
- - groups
8386
- - significanceThreshold
8387
- - minRuntimeDays
8388
- - minSampleSize
8389
- - sequentialTesting
8390
- - startedAt
8391
- - updatedAt
8392
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'
8393
8807
  next_cursor:
8394
8808
  anyOf:
8395
8809
  - type: string
@@ -8433,6 +8847,12 @@ components:
8433
8847
  minLength: 1
8434
8848
  maxLength: 256
8435
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'
8436
8856
  additionalProperties: false
8437
8857
  description: Inline metric — a DSL `query`, or an `event` (+ `aggregation`/`value`) the server compiles into one.
8438
8858
  CreateExperimentRequest:
@@ -8495,13 +8915,19 @@ components:
8495
8915
  description: Name of an existing universe in the project. Returns `422` if the universe doesn't exist.
8496
8916
  targeting_gate:
8497
8917
  default: null
8498
- 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.
8499
8925
  anyOf:
8500
8926
  - type: string
8501
8927
  - type: 'null'
8502
8928
  allocation_pct:
8503
8929
  default: 0
8504
- 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.
8505
8931
  type: integer
8506
8932
  minimum: 0
8507
8933
  maximum: 10000
@@ -8510,6 +8936,11 @@ components:
8510
8936
  type: number
8511
8937
  minimum: 0
8512
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
8513
8944
  salt:
8514
8945
  description: Hash salt for bucketing. Auto-generated if omitted. Immutable while running.
8515
8946
  type: string
@@ -8524,7 +8955,7 @@ components:
8524
8955
  - string
8525
8956
  - bool
8526
8957
  - number
8527
- 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.'
8528
8959
  groups:
8529
8960
  minItems: 2
8530
8961
  type: array
@@ -8540,18 +8971,18 @@ components:
8540
8971
  type: integer
8541
8972
  minimum: 0
8542
8973
  maximum: 10000
8543
- 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).
8544
8975
  params:
8545
8976
  default: {}
8546
- 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.
8547
8978
  type: object
8548
8979
  additionalProperties: {}
8549
8980
  required:
8550
8981
  - name
8551
8982
  - weight
8552
8983
  additionalProperties: false
8553
- description: One variant in the experiment. Weights across all groups must sum to 10000.
8554
- 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.
8555
8986
  significance_threshold:
8556
8987
  default: 0.05
8557
8988
  description: p-value cutoff used by the analysis pass. Defaults to `0.05`. Values other than 0.05 require Pro plan or higher.
@@ -8566,188 +8997,44 @@ components:
8566
8997
  maximum: 365
8567
8998
  min_sample_size:
8568
8999
  default: 100
8569
- description: Minimum exposures per group before results are considered conclusive.
8570
- type: integer
8571
- minimum: 1
8572
- maximum: 9007199254740991
8573
- sequential_testing:
8574
- default: false
8575
- description: Enable sequential testing (always-valid p-values). Requires Premium plan or higher.
8576
- type: boolean
8577
- goal_metric:
8578
- description: Single goal metric defined inline — either a DSL `query` or an `event` (+`aggregation`/`value`) the server compiles. Attaching one is required before the experiment can be started. The underlying event is auto-created if missing.
8579
- $ref: '#/components/schemas/ExperimentInlineMetric'
8580
- guardrail_metrics:
8581
- default: []
8582
- description: Up to 10 guardrail metrics defined inline. Each is upserted (event + metric) and attached with role=guardrail.
8583
- maxItems: 10
8584
- type: array
8585
- items:
8586
- $ref: '#/components/schemas/ExperimentInlineMetric'
8587
- required:
8588
- - name
8589
- - universe
8590
- - groups
8591
- additionalProperties: false
8592
- description: Body for `POST /api/admin/experiments`. `name`, `universe`, and `groups` (≥2, weights sum to 10000) required.
8593
- CreateExperimentResponse:
8594
- type: object
8595
- properties:
8596
- id:
8597
- type: string
8598
- description: Newly assigned experiment id.
8599
- name:
8600
- type: string
8601
- maxLength: 128
8602
- pattern: ^[a-z0-9](?:[a-z0-9_-]*[a-z0-9])?(?:\.[a-z0-9](?:[a-z0-9_-]*[a-z0-9])?)?$
8603
- 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.
8604
- required:
8605
- - id
8606
- - name
8607
- additionalProperties: false
8608
- GetExperimentResponse:
8609
- type: object
8610
- properties:
8611
- id:
8612
- type: string
8613
- description: Stable opaque experiment id (`exp_…`).
8614
- name:
8615
- type: string
8616
- maxLength: 128
8617
- pattern: ^[a-z0-9](?:[a-z0-9_-]*[a-z0-9])?(?:\.[a-z0-9](?:[a-z0-9_-]*[a-z0-9])?)?$
8618
- 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.
8619
- description:
8620
- anyOf:
8621
- - type: string
8622
- - type: 'null'
8623
- hypothesis:
8624
- anyOf:
8625
- - type: string
8626
- - type: 'null'
8627
- tag:
8628
- anyOf:
8629
- - type: string
8630
- - type: 'null'
8631
- ownerEmail:
8632
- anyOf:
8633
- - type: string
8634
- - type: 'null'
8635
- audience:
8636
- anyOf:
8637
- - type: string
8638
- - type: 'null'
8639
- bucketBy:
8640
- anyOf:
8641
- - type: string
8642
- minLength: 1
8643
- maxLength: 128
8644
- pattern: ^[a-zA-Z_][a-zA-Z0-9_]*$
8645
- 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.
8646
- - type: 'null'
8647
- folder:
8648
- anyOf:
8649
- - type: string
8650
- - type: 'null'
8651
- status:
8652
- type: string
8653
- enum:
8654
- - draft
8655
- - running
8656
- - stopped
8657
- - archived
8658
- universe:
8659
- type: string
8660
- description: Universe name this experiment draws from.
8661
- targetingGate:
8662
- anyOf:
8663
- - type: string
8664
- - type: 'null'
8665
- allocationPct:
8666
- type: integer
8667
- minimum: -9007199254740991
8668
- maximum: 9007199254740991
8669
- description: Allocation in basis points (0–10000).
8670
- salt:
8671
- type: string
8672
- params:
8673
- type: object
8674
- additionalProperties:
8675
- type: string
8676
- enum:
8677
- - string
8678
- - bool
8679
- - number
8680
- groups:
8681
- type: array
8682
- items:
8683
- type: object
8684
- properties:
8685
- name:
8686
- type: string
8687
- minLength: 1
8688
- maxLength: 64
8689
- description: Group label (e.g. `control`, `treatment_a`). Max 64 chars.
8690
- weight:
8691
- type: integer
8692
- minimum: 0
8693
- maximum: 10000
8694
- description: Allocation weight in basis points (0–10000). The sum across all groups must equal exactly **10000** (100%).
8695
- params:
8696
- default: {}
8697
- 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.
8698
- type: object
8699
- additionalProperties: {}
8700
- required:
8701
- - name
8702
- - weight
8703
- additionalProperties: false
8704
- description: One variant in the experiment. Weights across all groups must sum to 10000.
8705
- significanceThreshold:
8706
- type: number
8707
- minRuntimeDays:
8708
- type: integer
8709
- minimum: -9007199254740991
8710
- maximum: 9007199254740991
8711
- minSampleSize:
8712
- type: integer
8713
- minimum: -9007199254740991
8714
- maximum: 9007199254740991
8715
- sequentialTesting:
8716
- type: boolean
8717
- startedAt:
8718
- anyOf:
8719
- - type: string
8720
- - type: 'null'
8721
- description: ISO-8601 timestamp the experiment last transitioned to `running`, or `null`.
8722
- stoppedAt:
8723
- anyOf:
8724
- - type: string
8725
- - type: 'null'
8726
- updatedAt:
8727
- type: string
9000
+ description: Minimum exposures per group before results are considered conclusive.
9001
+ type: integer
9002
+ minimum: 1
9003
+ maximum: 9007199254740991
9004
+ sequential_testing:
9005
+ default: false
9006
+ description: Enable sequential testing (always-valid p-values). Requires Premium plan or higher.
9007
+ type: boolean
9008
+ goal_metric:
9009
+ description: Single goal metric defined inline — either a DSL `query` or an `event` (+`aggregation`/`value`) the server compiles. Attaching one is required before the experiment can be started. The underlying event is auto-created if missing.
9010
+ $ref: '#/components/schemas/ExperimentInlineMetric'
9011
+ guardrail_metrics:
9012
+ default: []
9013
+ description: Up to 10 guardrail metrics defined inline. Each is upserted (event + metric) and attached with role=guardrail.
9014
+ maxItems: 10
9015
+ type: array
9016
+ items:
9017
+ $ref: '#/components/schemas/ExperimentInlineMetric'
8728
9018
  required:
8729
- - id
8730
9019
  - name
8731
- - description
8732
- - hypothesis
8733
- - tag
8734
- - ownerEmail
8735
- - audience
8736
- - bucketBy
8737
- - folder
8738
- - status
8739
9020
  - universe
8740
- - targetingGate
8741
- - allocationPct
8742
- - salt
8743
- - params
8744
9021
  - groups
8745
- - significanceThreshold
8746
- - minRuntimeDays
8747
- - minSampleSize
8748
- - sequentialTesting
8749
- - startedAt
8750
- - updatedAt
9022
+ additionalProperties: false
9023
+ description: Body for `POST /api/admin/experiments`. `name`, `universe`, and `groups` (≥2, weights sum to 10000) required.
9024
+ CreateExperimentResponse:
9025
+ type: object
9026
+ properties:
9027
+ id:
9028
+ type: string
9029
+ description: Newly assigned experiment id.
9030
+ name:
9031
+ type: string
9032
+ maxLength: 128
9033
+ pattern: ^[a-z0-9](?:[a-z0-9_-]*[a-z0-9])?(?:\.[a-z0-9](?:[a-z0-9_-]*[a-z0-9])?)?$
9034
+ 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.
9035
+ required:
9036
+ - id
9037
+ - name
8751
9038
  additionalProperties: false
8752
9039
  DeleteExperimentResponse:
8753
9040
  type: object
@@ -8805,11 +9092,21 @@ components:
8805
9092
  anyOf:
8806
9093
  - type: string
8807
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'
8808
9100
  allocation_pct:
8809
9101
  description: Basis-points allocation (0–10000). Use `allocation_percent` (0–100) for percent. Immutable while the experiment is running.
8810
9102
  type: integer
8811
9103
  minimum: 0
8812
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
8813
9110
  allocation_percent:
8814
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.
8815
9112
  type: number
@@ -8831,9 +9128,9 @@ components:
8831
9128
  - string
8832
9129
  - bool
8833
9130
  - number
8834
- 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.'
8835
9132
  groups:
8836
- 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.
8837
9134
  minItems: 2
8838
9135
  type: array
8839
9136
  items:
@@ -8848,17 +9145,17 @@ components:
8848
9145
  type: integer
8849
9146
  minimum: 0
8850
9147
  maximum: 10000
8851
- 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`.
8852
9149
  params:
8853
9150
  default: {}
8854
- 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.
8855
9152
  type: object
8856
9153
  additionalProperties: {}
8857
9154
  required:
8858
9155
  - name
8859
9156
  - weight
8860
9157
  additionalProperties: false
8861
- 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.
8862
9159
  significance_threshold:
8863
9160
  type: number
8864
9161
  minimum: 0.0001
@@ -8883,7 +9180,7 @@ components:
8883
9180
  items:
8884
9181
  $ref: '#/components/schemas/ExperimentInlineMetric'
8885
9182
  additionalProperties: false
8886
- 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.
8887
9184
  UpdateExperimentResponse:
8888
9185
  type: object
8889
9186
  properties:
@@ -8942,6 +9239,12 @@ components:
8942
9239
  - guardrail
8943
9240
  - secondary
8944
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'
8945
9248
  required:
8946
9249
  - metric_id
8947
9250
  - role
@@ -8969,9 +9272,15 @@ components:
8969
9272
  - goal
8970
9273
  - guardrail
8971
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'
8972
9280
  required:
8973
9281
  - metric_id
8974
9282
  - role
9283
+ - min_effect_of_interest
8975
9284
  additionalProperties: false
8976
9285
  required:
8977
9286
  - id
@@ -9035,6 +9344,11 @@ components:
9035
9344
  - type: number
9036
9345
  - type: 'null'
9037
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.
9038
9352
  required:
9039
9353
  - metric
9040
9354
  - group_name
@@ -9044,6 +9358,7 @@ components:
9044
9358
  - delta_pct
9045
9359
  - p_value
9046
9360
  - srm_detected
9361
+ - realized_mde
9047
9362
  additionalProperties: false
9048
9363
  verdict:
9049
9364
  type: string
@@ -9850,6 +10165,35 @@ components:
9850
10165
  - version
9851
10166
  - published
9852
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.
9853
10197
  ListUniversesResponse:
9854
10198
  type: object
9855
10199
  properties:
@@ -9866,6 +10210,11 @@ components:
9866
10210
  maxLength: 128
9867
10211
  pattern: ^[a-z0-9](?:[a-z0-9_-]*[a-z0-9])?(?:\.[a-z0-9](?:[a-z0-9_-]*[a-z0-9])?)?$
9868
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.
9869
10218
  unitType:
9870
10219
  type: string
9871
10220
  description: Unit of randomisation. Snake-case in request (`unit_type`), camelCase in response.
@@ -9878,6 +10227,16 @@ components:
9878
10227
  maxItems: 2
9879
10228
  - type: 'null'
9880
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.
9881
10240
  createdAt:
9882
10241
  type: string
9883
10242
  description: ISO-8601 timestamp of creation.
@@ -9906,6 +10265,13 @@ components:
9906
10265
  description: Stable universe key. Single segment or `folder.name`. Lowercase letters, digits, `_` or `-`; max 128 chars. Immutable after create.
9907
10266
  folder:
9908
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.
9909
10275
  unit_type:
9910
10276
  default: user_id
9911
10277
  description: Unit of randomisation. Typically `user_id`. Use `account_id` to keep whole accounts in the same group across an experiment.
@@ -9922,6 +10288,18 @@ components:
9922
10288
  maxItems: 2
9923
10289
  - type: 'null'
9924
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.
9925
10303
  required:
9926
10304
  - name
9927
10305
  additionalProperties: false
@@ -9955,6 +10333,12 @@ components:
9955
10333
  properties:
9956
10334
  folder:
9957
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.
9958
10342
  holdout_range:
9959
10343
  anyOf:
9960
10344
  - type: array
@@ -9966,8 +10350,18 @@ components:
9966
10350
  maxItems: 2
9967
10351
  - type: 'null'
9968
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).
9969
10363
  additionalProperties: false
9970
- 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.
9971
10365
  UpdateUniverseResponse:
9972
10366
  type: object
9973
10367
  properties:
@@ -10680,8 +11074,8 @@ components:
10680
11074
  winsorizePct:
10681
11075
  description: Winsorise percentile applied to the metric.
10682
11076
  type: number
10683
- minDetectableEffect:
10684
- description: Configured MDE, or `null`.
11077
+ defaultMinEffectOfInterest:
11078
+ description: Metric-level default minimum effect of interest (relative, 0–1), or `null`.
10685
11079
  anyOf:
10686
11080
  - type: number
10687
11081
  - type: 'null'
@@ -10716,9 +11110,9 @@ components:
10716
11110
  type: integer
10717
11111
  minimum: 1
10718
11112
  maximum: 99
10719
- MetricMinDetectableEffect:
11113
+ MetricDefaultMinEffectOfInterest:
10720
11114
  default: null
10721
- 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.
10722
11116
  anyOf:
10723
11117
  - type: number
10724
11118
  - type: 'null'
@@ -10743,8 +11137,8 @@ components:
10743
11137
  $ref: '#/components/schemas/MetricQueryDsl'
10744
11138
  winsorize_pct:
10745
11139
  $ref: '#/components/schemas/MetricWinsorizePct'
10746
- min_detectable_effect:
10747
- $ref: '#/components/schemas/MetricMinDetectableEffect'
11140
+ default_min_effect_of_interest:
11141
+ $ref: '#/components/schemas/MetricDefaultMinEffectOfInterest'
10748
11142
  direction:
10749
11143
  $ref: '#/components/schemas/MetricDirection'
10750
11144
  required:
@@ -10766,8 +11160,8 @@ components:
10766
11160
  $ref: '#/components/schemas/QueryIr'
10767
11161
  winsorize_pct:
10768
11162
  $ref: '#/components/schemas/MetricWinsorizePct'
10769
- min_detectable_effect:
10770
- $ref: '#/components/schemas/MetricMinDetectableEffect'
11163
+ default_min_effect_of_interest:
11164
+ $ref: '#/components/schemas/MetricDefaultMinEffectOfInterest'
10771
11165
  direction:
10772
11166
  $ref: '#/components/schemas/MetricDirection'
10773
11167
  required:
@@ -10829,8 +11223,8 @@ components:
10829
11223
  winsorizePct:
10830
11224
  description: Winsorise percentile applied to the metric.
10831
11225
  type: number
10832
- minDetectableEffect:
10833
- description: Configured MDE, or `null`.
11226
+ defaultMinEffectOfInterest:
11227
+ description: Metric-level default minimum effect of interest (relative, 0–1), or `null`.
10834
11228
  anyOf:
10835
11229
  - type: number
10836
11230
  - type: 'null'
@@ -10870,8 +11264,8 @@ components:
10870
11264
  $ref: '#/components/schemas/MetricQueryDsl'
10871
11265
  winsorize_pct:
10872
11266
  $ref: '#/components/schemas/MetricWinsorizePct'
10873
- min_detectable_effect:
10874
- $ref: '#/components/schemas/MetricMinDetectableEffect'
11267
+ default_min_effect_of_interest:
11268
+ $ref: '#/components/schemas/MetricDefaultMinEffectOfInterest'
10875
11269
  direction:
10876
11270
  $ref: '#/components/schemas/MetricDirection'
10877
11271
  required:
@@ -10889,8 +11283,8 @@ components:
10889
11283
  $ref: '#/components/schemas/QueryIr'
10890
11284
  winsorize_pct:
10891
11285
  $ref: '#/components/schemas/MetricWinsorizePct'
10892
- min_detectable_effect:
10893
- $ref: '#/components/schemas/MetricMinDetectableEffect'
11286
+ default_min_effect_of_interest:
11287
+ $ref: '#/components/schemas/MetricDefaultMinEffectOfInterest'
10894
11288
  direction:
10895
11289
  $ref: '#/components/schemas/MetricDirection'
10896
11290
  required:
@@ -10906,12 +11300,12 @@ components:
10906
11300
  $ref: '#/components/schemas/MetricEventName'
10907
11301
  winsorize_pct:
10908
11302
  $ref: '#/components/schemas/MetricWinsorizePct'
10909
- min_detectable_effect:
10910
- $ref: '#/components/schemas/MetricMinDetectableEffect'
11303
+ default_min_effect_of_interest:
11304
+ $ref: '#/components/schemas/MetricDefaultMinEffectOfInterest'
10911
11305
  direction:
10912
11306
  $ref: '#/components/schemas/MetricDirection'
10913
11307
  additionalProperties: false
10914
- 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).
10915
11309
  UpdateMetricRequest:
10916
11310
  oneOf:
10917
11311
  - $ref: '#/components/schemas/UpdateMetricWithQuery'
@@ -11580,6 +11974,11 @@ components:
11580
11974
  items:
11581
11975
  type: string
11582
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`.
11583
11982
  sdkVersion:
11584
11983
  anyOf:
11585
11984
  - type: string
@@ -11769,6 +12168,11 @@ components:
11769
12168
  OpsMeasurePlanContext:
11770
12169
  type: object
11771
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.
11772
12176
  created:
11773
12177
  type: array
11774
12178
  items:
@@ -12175,6 +12579,91 @@ components:
12175
12579
  - id
12176
12580
  additionalProperties: false
12177
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`.
12178
12667
  NotifyOpsRequest:
12179
12668
  type: object
12180
12669
  properties:
@@ -12447,7 +12936,9 @@ components:
12447
12936
  type: string
12448
12937
  enum:
12449
12938
  - free
12450
- - paid
12939
+ - pro
12940
+ - business
12941
+ - enterprise
12451
12942
  description: Billing plan tier.
12452
12943
  status:
12453
12944
  type: string
@@ -12502,6 +12993,50 @@ components:
12502
12993
  - type: boolean
12503
12994
  - type: number
12504
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.
12505
13040
  createdAt:
12506
13041
  type: string
12507
13042
  description: ISO-8601 timestamp of project creation.
@@ -12652,6 +13187,78 @@ components:
12652
13187
  moduleEvents:
12653
13188
  type: boolean
12654
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.
12655
13262
  additionalProperties: false
12656
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.
12657
13264
  ListI18nProfilesResponse:
@@ -13128,6 +13735,59 @@ components:
13128
13735
  - kv_verified
13129
13736
  additionalProperties: false
13130
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
13131
13791
  ErrorRecord:
13132
13792
  type: object
13133
13793
  description: |-
@@ -13143,7 +13803,7 @@ components:
13143
13803
  description: Project this issue belongs to.
13144
13804
  fingerprint:
13145
13805
  type: string
13146
- 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`.
13147
13807
  causedByFingerprint:
13148
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.
13149
13809
  anyOf:
@@ -13221,7 +13881,12 @@ components:
13221
13881
  count:
13222
13882
  type: integer
13223
13883
  minimum: 1
13224
- 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'
13225
13890
  status:
13226
13891
  type: string
13227
13892
  enum:
@@ -13255,7 +13920,7 @@ components:
13255
13920
  additionalProperties: false
13256
13921
  ListErrorsResponse:
13257
13922
  type: array
13258
- 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.
13259
13924
  items:
13260
13925
  $ref: '#/components/schemas/ErrorRecord'
13261
13926
  UpdateErrorStatusRequest:
@@ -14036,7 +14701,8 @@ components:
14036
14701
  - events:write
14037
14702
  - configs:write
14038
14703
  - experiments:write
14039
- 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.'
14040
14706
  expiresInDays:
14041
14707
  anyOf:
14042
14708
  - type: integer