@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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shipeasy/openapi",
3
- "version": "2.1.0",
3
+ "version": "2.3.0",
4
4
  "description": "Shipeasy admin OpenAPI 3.2 spec (hand-authored, single source of truth) + the generated TypeScript client, Zod schemas, and types. Consumed by @shipeasy/cli and @shipeasy/mcp.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -17,6 +17,17 @@ ErrorCode:
17
17
  - GROUPS_WEIGHT_SUM
18
18
  - EVENT_PENDING
19
19
  - INTERNAL
20
+ - PLAN_LIMIT
21
+ # Experiments
22
+ - EXPERIMENT_NO_GOAL_METRIC
23
+ - EXPERIMENT_ARCHIVED_RESTART
24
+ - EXPERIMENT_RESTORE_INVALID
25
+ - EXPERIMENT_NOT_RUNNING
26
+ - EXPERIMENT_RUNNING_ARCHIVE
27
+ - EXPERIMENT_IMMUTABLE_FIELD
28
+ # Metrics
29
+ - METRIC_NOT_FOUND
30
+ - METRIC_UNKNOWN_ID
20
31
  x-enum-descriptions:
21
32
  BAD_REQUEST: Malformed request (bad JSON, missing project scope).
22
33
  UNAUTHORIZED: Missing or invalid admin SDK key.
@@ -33,9 +44,18 @@ ErrorCode:
33
44
  GROUPS_WEIGHT_SUM: Experiment group weights must sum to 10000 (basis points).
34
45
  EVENT_PENDING: The referenced event is still pending review and cannot back a metric yet.
35
46
  INTERNAL: Unexpected server error.
47
+ 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.
48
+ EXPERIMENT_NO_GOAL_METRIC: The experiment has no goal metric set, which the requested action requires.
49
+ EXPERIMENT_ARCHIVED_RESTART: An archived experiment cannot be restarted.
50
+ EXPERIMENT_RESTORE_INVALID: The experiment cannot be restored from its current state.
51
+ EXPERIMENT_NOT_RUNNING: The action requires the experiment to be running.
52
+ EXPERIMENT_RUNNING_ARCHIVE: A running experiment cannot be archived — stop it first.
53
+ EXPERIMENT_IMMUTABLE_FIELD: A field that is immutable while the experiment is running was modified.
54
+ METRIC_NOT_FOUND: The referenced metric does not exist or is not visible to the caller.
55
+ METRIC_UNKNOWN_ID: The supplied metric id is malformed or not recognised.
36
56
  Error:
37
57
  type: object
38
- 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.
58
+ 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.
39
59
  required:
40
60
  - error
41
61
  additionalProperties: false
@@ -48,6 +68,15 @@ Error:
48
68
  detail:
49
69
  type: string
50
70
  description: Extra context (validation details, conflicting field, referenced row).
71
+ fields:
72
+ type: object
73
+ additionalProperties:
74
+ type: string
75
+ 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.
51
76
  instructions:
52
77
  type: string
53
78
  description: Actionable guidance for resolving this error — what to check or change next.
79
+ schema:
80
+ type: object
81
+ additionalProperties: true
82
+ 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.
@@ -22,3 +22,11 @@ PaginationCursor:
22
22
  description: Opaque cursor returned in the previous page's `next_cursor`. Omit for the first page.
23
23
  schema:
24
24
  type: string
25
+ Q:
26
+ name: q
27
+ in: query
28
+ required: false
29
+ 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.
30
+ schema:
31
+ type: string
32
+ maxLength: 100
@@ -105,3 +105,18 @@ UnprocessableEntity:
105
105
  value:
106
106
  error: The referenced event is still pending review and cannot back a metric yet.
107
107
  code: EVENT_PENDING
108
+ BadGateway:
109
+ description: |-
110
+ The request was well-formed and the resource exists, but a downstream provider
111
+ the endpoint delegates to (e.g. a connector's Slack/GitHub/cloud-agent dispatch)
112
+ failed. The failure is upstream of this API, not a client error. No stable
113
+ `ErrorCode` is assigned to upstream failures, so `code` is omitted.
114
+ content:
115
+ application/json:
116
+ schema:
117
+ $ref: ./errors.yaml#/Error
118
+ examples:
119
+ UPSTREAM_FAILURE:
120
+ summary: Downstream provider dispatch failed
121
+ value:
122
+ error: "GitHub API responded 403: resource not accessible by integration."
@@ -1,4 +1,9 @@
1
1
  # Cross-cutting shared schemas referenced from multiple resources.
2
+ ResourceId:
3
+ type: string
4
+ minLength: 1
5
+ maxLength: 128
6
+ 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.
2
7
  Env:
3
8
  type: string
4
9
  enum:
@@ -83,5 +88,11 @@ ExperimentInlineMetric:
83
88
  enum: [count_users, count_events, retention_7d, retention_30d, sum, avg]
84
89
  description: Reducer for the `event` form. Defaults to `count_users`.
85
90
  value: { type: string, minLength: 1, maxLength: 256, description: Numeric event property for `sum`/`avg` (with `event`). }
91
+ min_effect_of_interest:
92
+ 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.
93
+ default: null
94
+ anyOf:
95
+ - type: number
96
+ - type: "null"
86
97
  additionalProperties: false
87
98
  description: Inline metric — a DSL `query`, or an `event` (+ `aggregation`/`value`) the server compiles into one.
@@ -564,30 +564,24 @@ FireConnectorResponse:
564
564
  properties:
565
565
  ok:
566
566
  type: boolean
567
- description: "`true` when the trigger run was successfully kicked off; `false` when the dispatch itself failed (still HTTP 200, with `error` set)."
568
- error:
569
- type: string
570
- description: "Failure reason. Present only when `ok` is `false` — the fire reached the handler but the underlying dispatch to the provider failed."
567
+ description: "Always `true` — a successful fire returns HTTP 200. A dispatch failure returns HTTP 502 with the `Error` envelope, not this body."
571
568
  required:
572
569
  - ok
573
570
  additionalProperties: false
574
- description: "Result of firing a trigger connector. `{ ok: true }` on success; `{ ok: false, error }` when the dispatch fails (the request still returns HTTP 200)."
571
+ 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`."
575
572
  TestConnectorResponse:
576
573
  type: object
577
574
  properties:
578
575
  ok:
579
576
  type: boolean
580
- description: "`true` when the test dispatch reached the destination; `false` when the dispatch itself failed (still HTTP 200, with `error` set)."
577
+ description: "Always `true` — a successful test returns HTTP 200. A dispatch failure returns HTTP 502 with the `Error` envelope, not this body."
581
578
  issueUrl:
582
579
  anyOf:
583
580
  - type: string
584
581
  - type: "null"
585
- 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)."
586
- error:
587
- type: string
588
- description: "Failure reason. Present only when `ok` is `false` — the test reached the handler but the underlying dispatch to the provider failed."
582
+ 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."
589
583
  required:
590
584
  - ok
591
585
  - issueUrl
592
586
  additionalProperties: false
593
- 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)."
587
+ 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`."
@@ -13,7 +13,11 @@ ErrorRecord:
13
13
  description: Project this issue belongs to.
14
14
  fingerprint:
15
15
  type: string
16
- description: Stable dedupe key (hash of `errorType` + normalized `message` + consequence). Unique per project — every occurrence with the same fingerprint folds into this one row.
16
+ description: >-
17
+ Stable dedupe key — the issue TITLE (hash of `errorType` + `subject` +
18
+ `outcome`, plus the violation name for violations). Unique per project —
19
+ every occurrence with the same title folds into this one row;
20
+ per-instance variety (message/stack/extras) lives in `occurrences`.
17
21
  causedByFingerprint:
18
22
  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.
19
23
  anyOf:
@@ -91,7 +95,13 @@ ErrorRecord:
91
95
  count:
92
96
  type: integer
93
97
  minimum: 1
94
- description: Number of folded occurrences for this fingerprint.
98
+ description: EXACT number of folded occurrences for this fingerprint — every occurrence increments it, regardless of detail-row sampling.
99
+ occurrences:
100
+ type: array
101
+ description: |-
102
+ 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.
103
+ items:
104
+ $ref: "#/ErrorOccurrence"
95
105
  status:
96
106
  type: string
97
107
  enum:
@@ -123,9 +133,62 @@ ErrorRecord:
123
133
  - createdAt
124
134
  - updatedAt
125
135
  additionalProperties: false
136
+ ErrorOccurrence:
137
+ type: object
138
+ description: One sampled instance behind a tracked error — the minimal per-occurrence payload (what varies between instances; everything else lives on the parent `ErrorRecord`).
139
+ properties:
140
+ id:
141
+ type: string
142
+ description: Stable opaque occurrence id.
143
+ message:
144
+ type: string
145
+ description: This instance's raw message (unlike the fingerprint, never normalized).
146
+ stack:
147
+ description: This instance's stack trace, or `null` if none was captured.
148
+ anyOf:
149
+ - type: string
150
+ - type: "null"
151
+ url:
152
+ description: This instance's raw URL (ids intact), or `null`.
153
+ anyOf:
154
+ - type: string
155
+ - type: "null"
156
+ env:
157
+ description: Published env this instance ran against, or `null`.
158
+ anyOf:
159
+ - type: string
160
+ - type: "null"
161
+ side:
162
+ description: Which SDK side reported it — `client` or `server`. `null` if unknown.
163
+ anyOf:
164
+ - type: string
165
+ - type: "null"
166
+ sdkVersion:
167
+ description: "`@shipeasy/sdk` version of this instance, or `null`."
168
+ anyOf:
169
+ - type: string
170
+ - type: "null"
171
+ extrasJson:
172
+ description: This instance's sanitized extras, JSON-encoded. `null` if none.
173
+ anyOf:
174
+ - type: string
175
+ - type: "null"
176
+ sampleRate:
177
+ type: integer
178
+ minimum: 1
179
+ description: 1-in-N sampling rate in force when this instance was kept (`1` = the issue was still being recorded exhaustively).
180
+ seenAt:
181
+ type: string
182
+ description: ISO-8601 timestamp this instance was ingested.
183
+ required:
184
+ - id
185
+ - message
186
+ - sampleRate
187
+ - seenAt
188
+ additionalProperties: false
126
189
  ListErrorsResponse:
127
190
  type: array
128
- description: A bare JSON array of tracked errors, ordered by `lastSeenAt` descending. There is no pagination envelope — `limit` caps the page size.
191
+ 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.
129
192
  items:
130
193
  $ref: "#/ErrorRecord"
131
194
  UpdateErrorStatusRequest:
@@ -0,0 +1,293 @@
1
+ # The single wire shape for an experiment row, shared by `ListExperimentsResponse`
2
+ # (`data[]` items) and `GET /api/admin/experiments/{id}`. The app projects every
3
+ # experiment through one `toExperimentApiRow` allow-list at the HTTP boundary, so
4
+ # both endpoints return the SAME object — this schema is that allow-list.
5
+ #
6
+ # Two tiers of fields:
7
+ # • base (always present) — persisted columns + the pool-slice / headroom /
8
+ # version fields the serializer defaults (`?? null` / `?? 0` / `?? 1`), so
9
+ # they appear on every row. These are `required`.
10
+ # • enriched (list only) — verdict + goal/guardrail summary + exposure spark +
11
+ # resolved creator/editor emails. The list handler derives them for every
12
+ # row; the by-id handler returns the raw row, so they are `undefined` and
13
+ # drop out of the JSON. They are therefore OPTIONAL (present on list rows,
14
+ # absent on the by-id detail) — never `required`.
15
+ ExperimentApiRow:
16
+ type: object
17
+ properties:
18
+ id:
19
+ type: string
20
+ description: Stable opaque experiment id (`exp_…`).
21
+ name:
22
+ type: string
23
+ maxLength: 128
24
+ pattern: ^[a-z0-9](?:[a-z0-9_-]*[a-z0-9])?(?:\.[a-z0-9](?:[a-z0-9_-]*[a-z0-9])?)?$
25
+ 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.
26
+ description:
27
+ anyOf:
28
+ - type: string
29
+ - type: "null"
30
+ hypothesis:
31
+ anyOf:
32
+ - type: string
33
+ - type: "null"
34
+ tag:
35
+ anyOf:
36
+ - type: string
37
+ - type: "null"
38
+ ownerEmail:
39
+ anyOf:
40
+ - type: string
41
+ - type: "null"
42
+ audience:
43
+ anyOf:
44
+ - type: string
45
+ - type: "null"
46
+ bucketBy:
47
+ anyOf:
48
+ - type: string
49
+ minLength: 1
50
+ maxLength: 128
51
+ pattern: ^[a-zA-Z_][a-zA-Z0-9_]*$
52
+ 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.
53
+ - type: "null"
54
+ folder:
55
+ anyOf:
56
+ - type: string
57
+ - type: "null"
58
+ status:
59
+ type: string
60
+ enum:
61
+ - draft
62
+ - running
63
+ - stopped
64
+ - archived
65
+ universe:
66
+ type: string
67
+ description: Universe name this experiment draws from.
68
+ targetingGate:
69
+ anyOf:
70
+ - type: string
71
+ - type: "null"
72
+ holdoutGate:
73
+ anyOf:
74
+ - type: string
75
+ - type: "null"
76
+ description: Per-experiment holdout gate name (a `holdout`-type flag), or `null`.
77
+ allocationPct:
78
+ type: integer
79
+ minimum: -9007199254740991
80
+ maximum: 9007199254740991
81
+ description: Allocation in basis points (0–10000).
82
+ reservedHeadroom:
83
+ type: integer
84
+ minimum: 0
85
+ maximum: 10000
86
+ description: Basis points of the split reserved for appended variants (group weights sum to 10000 − this).
87
+ hashVersion:
88
+ type: integer
89
+ minimum: 1
90
+ description: Bucketing hash algorithm version for the experiment's pool slice (§B4). Defaults to 1.
91
+ poolOffsetBp:
92
+ anyOf:
93
+ - type: integer
94
+ minimum: 0
95
+ maximum: 10000
96
+ - type: "null"
97
+ description: Basis-point offset of the experiment's contiguous slice in the universe pool, or `null` before a slice is allocated.
98
+ poolSizeBp:
99
+ anyOf:
100
+ - type: integer
101
+ minimum: 0
102
+ maximum: 10000
103
+ - type: "null"
104
+ description: Basis-point width of the experiment's pool slice (equal to its allocation), or `null` before a slice is allocated.
105
+ salt:
106
+ type: string
107
+ params:
108
+ type: object
109
+ additionalProperties:
110
+ type: string
111
+ enum:
112
+ - string
113
+ - bool
114
+ - number
115
+ groups:
116
+ type: array
117
+ items:
118
+ type: object
119
+ properties:
120
+ name:
121
+ type: string
122
+ minLength: 1
123
+ maxLength: 64
124
+ description: Group label (e.g. `control`, `treatment_a`). Max 64 chars.
125
+ weight:
126
+ type: integer
127
+ minimum: 0
128
+ maximum: 10000
129
+ description: Allocation weight in basis points (0–10000). The sum across all groups must equal exactly **10000** (100%).
130
+ params:
131
+ default: {}
132
+ 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.
133
+ type: object
134
+ additionalProperties: {}
135
+ required:
136
+ - name
137
+ - weight
138
+ additionalProperties: false
139
+ description: One variant in the experiment. Weights across all groups must sum to 10000.
140
+ significanceThreshold:
141
+ type: number
142
+ minRuntimeDays:
143
+ type: integer
144
+ minimum: -9007199254740991
145
+ maximum: 9007199254740991
146
+ minSampleSize:
147
+ type: integer
148
+ minimum: -9007199254740991
149
+ maximum: 9007199254740991
150
+ sequentialTesting:
151
+ type: boolean
152
+ startedAt:
153
+ anyOf:
154
+ - type: string
155
+ - type: "null"
156
+ description: ISO-8601 timestamp the experiment last transitioned to `running`, or `null`.
157
+ stoppedAt:
158
+ anyOf:
159
+ - type: string
160
+ - type: "null"
161
+ updatedAt:
162
+ type: string
163
+ version:
164
+ anyOf:
165
+ - type: integer
166
+ - type: "null"
167
+ description: Save counter (number of published edits), or `null` on pre-versioning rows.
168
+ creatorEmail:
169
+ anyOf:
170
+ - type: string
171
+ - type: "null"
172
+ description: "Resolved creator email (`created_by` → users). Enriched field: present on list rows, omitted from the by-id detail."
173
+ updaterEmail:
174
+ anyOf:
175
+ - type: string
176
+ - type: "null"
177
+ description: "Resolved last-editor email (`updated_by` → users). Enriched field: present on list rows, omitted from the by-id detail."
178
+ verdict:
179
+ type: string
180
+ enum:
181
+ - ship
182
+ - hold
183
+ - wait
184
+ - invalid
185
+ - draft
186
+ 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."
187
+ verdictTitle:
188
+ type: string
189
+ description: "Expanded verdict headline the results hero renders. Enriched field: list rows only."
190
+ verdictWhy:
191
+ type: string
192
+ description: "Verdict rationale the results hero renders. Enriched field: list rows only."
193
+ goalMetric:
194
+ anyOf:
195
+ - type: object
196
+ properties:
197
+ id:
198
+ type: string
199
+ name:
200
+ type: string
201
+ required:
202
+ - id
203
+ - name
204
+ additionalProperties: false
205
+ - type: "null"
206
+ description: "The experiment's goal metric `{ id, name }`, or `null` when none is set. Enriched field: list rows only."
207
+ guardrails:
208
+ type: array
209
+ items:
210
+ type: object
211
+ properties:
212
+ id:
213
+ type: string
214
+ name:
215
+ type: string
216
+ eventName:
217
+ anyOf:
218
+ - type: string
219
+ - type: "null"
220
+ required:
221
+ - id
222
+ - name
223
+ additionalProperties: false
224
+ description: "Guardrail metrics `{ id, name, eventName? }`. Enriched field: list rows only."
225
+ guardrailCount:
226
+ type: integer
227
+ minimum: 0
228
+ description: "Number of guardrail metrics. Enriched field: list rows only."
229
+ primaryLiftPct:
230
+ anyOf:
231
+ - type: number
232
+ - type: "null"
233
+ description: "Goal-metric lift of the leading variant vs. control, in percent, or `null` when not yet computed. Enriched field: list rows only."
234
+ significancePct:
235
+ anyOf:
236
+ - type: number
237
+ - type: "null"
238
+ description: "Statistical significance (1 − p) as a percent, or `null` when not yet computed. Enriched field: list rows only."
239
+ sampleSize:
240
+ anyOf:
241
+ - type: integer
242
+ - type: "null"
243
+ description: "Total distinct units analysed, or `null` when not yet computed. Enriched field: list rows only."
244
+ exposure:
245
+ type: array
246
+ items:
247
+ type: object
248
+ properties:
249
+ ds:
250
+ type: string
251
+ value:
252
+ type: number
253
+ required:
254
+ - ds
255
+ - value
256
+ additionalProperties: false
257
+ description: "Cumulative unique-users-enrolled series for the exposure sparkline. Enriched field: list rows only."
258
+ exposureTotal:
259
+ anyOf:
260
+ - type: integer
261
+ - type: "null"
262
+ description: "Total distinct units ever exposed (sparkline headline), or `null`. Enriched field: list rows only."
263
+ required:
264
+ - id
265
+ - name
266
+ - description
267
+ - hypothesis
268
+ - tag
269
+ - ownerEmail
270
+ - audience
271
+ - bucketBy
272
+ - folder
273
+ - status
274
+ - universe
275
+ - targetingGate
276
+ - holdoutGate
277
+ - allocationPct
278
+ - reservedHeadroom
279
+ - hashVersion
280
+ - poolOffsetBp
281
+ - poolSizeBp
282
+ - salt
283
+ - params
284
+ - groups
285
+ - significanceThreshold
286
+ - minRuntimeDays
287
+ - minSampleSize
288
+ - sequentialTesting
289
+ - startedAt
290
+ - stoppedAt
291
+ - updatedAt
292
+ - version
293
+ additionalProperties: false