@shipeasy/openapi 2.2.0 → 2.4.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-643YSMLU.js → chunk-4QSU744W.js} +183 -1
  2. package/dist/chunk-4QSU744W.js.map +1 -0
  3. package/dist/{chunk-MRW3DJ57.js → chunk-ZPKHTUVV.js} +1238 -335
  4. package/dist/chunk-ZPKHTUVV.js.map +1 -0
  5. package/dist/{client-K0ShmsC7.d.ts → client-WGNXT4xl.d.ts} +5818 -2647
  6. package/dist/client.d.ts +1 -1
  7. package/dist/client.js +51 -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 +52 -2
  12. package/dist/index.js.map +1 -1
  13. package/dist/schemas.d.ts +3538 -534
  14. package/dist/schemas.js +253 -5
  15. package/openapi.json +12914 -7158
  16. package/openapi.yaml +7672 -3742
  17. package/package.json +1 -1
  18. package/spec/components/errors.yaml +33 -1
  19. package/spec/components/schemas/alerts.yaml +138 -0
  20. package/spec/components/schemas/attributes.yaml +53 -3
  21. package/spec/components/schemas/common.yaml +16 -0
  22. package/spec/components/schemas/configs.yaml +58 -0
  23. package/spec/components/schemas/connectors.yaml +5 -1
  24. package/spec/components/schemas/errors-api.yaml +116 -6
  25. package/spec/components/schemas/experiment-api-row.yaml +293 -0
  26. package/spec/components/schemas/experiments.yaml +395 -380
  27. package/spec/components/schemas/gates.yaml +355 -272
  28. package/spec/components/schemas/i18n.yaml +169 -40
  29. package/spec/components/schemas/keys.yaml +2 -1
  30. package/spec/components/schemas/killswitches.yaml +36 -0
  31. package/spec/components/schemas/metrics.yaml +200 -23
  32. package/spec/components/schemas/ops.yaml +918 -29
  33. package/spec/components/schemas/projects.yaml +246 -2
  34. package/spec/components/schemas/universes.yaml +83 -1
  35. package/spec/openapi.yaml +61 -0
  36. package/spec/paths/alert-rules.yaml +1 -0
  37. package/spec/paths/alerts.yaml +184 -0
  38. package/spec/paths/attributes.yaml +23 -5
  39. package/spec/paths/configs.yaml +71 -0
  40. package/spec/paths/errors.yaml +112 -2
  41. package/spec/paths/events.yaml +1 -0
  42. package/spec/paths/experiments.yaml +244 -16
  43. package/spec/paths/gates.yaml +138 -0
  44. package/spec/paths/i18n.yaml +290 -6
  45. package/spec/paths/keys.yaml +1 -0
  46. package/spec/paths/killswitches.yaml +7 -0
  47. package/spec/paths/metrics.yaml +187 -0
  48. package/spec/paths/ops.yaml +410 -0
  49. package/spec/paths/projects.yaml +62 -2
  50. package/spec/paths/universes.yaml +1 -0
  51. package/dist/chunk-643YSMLU.js.map +0 -1
  52. package/dist/chunk-MRW3DJ57.js.map +0 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shipeasy/openapi",
3
- "version": "2.2.0",
3
+ "version": "2.4.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,19 @@ 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
31
+ # Ops
32
+ - AGENT_NOT_CONNECTED
20
33
  x-enum-descriptions:
21
34
  BAD_REQUEST: Malformed request (bad JSON, missing project scope).
22
35
  UNAUTHORIZED: Missing or invalid admin SDK key.
@@ -33,9 +46,19 @@ ErrorCode:
33
46
  GROUPS_WEIGHT_SUM: Experiment group weights must sum to 10000 (basis points).
34
47
  EVENT_PENDING: The referenced event is still pending review and cannot back a metric yet.
35
48
  INTERNAL: Unexpected server error.
49
+ 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.
50
+ EXPERIMENT_NO_GOAL_METRIC: The experiment has no goal metric set, which the requested action requires.
51
+ EXPERIMENT_ARCHIVED_RESTART: An archived experiment cannot be restarted.
52
+ EXPERIMENT_RESTORE_INVALID: The experiment cannot be restored from its current state.
53
+ EXPERIMENT_NOT_RUNNING: The action requires the experiment to be running.
54
+ EXPERIMENT_RUNNING_ARCHIVE: A running experiment cannot be archived — stop it first.
55
+ EXPERIMENT_IMMUTABLE_FIELD: A field that is immutable while the experiment is running was modified.
56
+ METRIC_NOT_FOUND: The referenced metric does not exist or is not visible to the caller.
57
+ METRIC_UNKNOWN_ID: The supplied metric id is malformed or not recognised.
58
+ AGENT_NOT_CONNECTED: The named AI agent type has no connected trigger connector in this project — list the available agents (`ops agents list` / `GET /api/admin/agent-profiles`) and use one of those, or connect the agent under Settings → Triggers.
36
59
  Error:
37
60
  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.
61
+ 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
62
  required:
40
63
  - error
41
64
  additionalProperties: false
@@ -48,6 +71,15 @@ Error:
48
71
  detail:
49
72
  type: string
50
73
  description: Extra context (validation details, conflicting field, referenced row).
74
+ fields:
75
+ type: object
76
+ additionalProperties:
77
+ type: string
78
+ 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
79
  instructions:
52
80
  type: string
53
81
  description: Actionable guidance for resolving this error — what to check or change next.
82
+ schema:
83
+ type: object
84
+ additionalProperties: true
85
+ 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.
@@ -0,0 +1,138 @@
1
+ AlertApiRow:
2
+ type: object
3
+ description: |-
4
+ One FIRED alert — a condition the platform observed and raised, not the rule that defines it (alert *rules* are the `/api/admin/alert-rules` resource). Alerts are never filed by hand: the UI (killswitch flips) and the worker (analysis consumer + alerts cron) raise them, keyed by `(source, dedupeKey)` so re-raising refreshes the active row and a clearing condition auto-resolves it.
5
+
6
+ Every field is always present on the wire; nullable fields are `null`, never absent.
7
+ properties:
8
+ id:
9
+ type: string
10
+ description: Stable opaque alert id.
11
+ source:
12
+ type: string
13
+ enum:
14
+ - metric_rule
15
+ - killswitch_armed
16
+ - experiment_srm
17
+ - experiment_peek
18
+ - guardrail
19
+ description: Which platform subsystem raised the alert — a metric-threshold rule crossing (`metric_rule`), an armed kill switch (`killswitch_armed`), a sample-ratio mismatch or peeking guard on an experiment (`experiment_srm` / `experiment_peek`), or a guardrail-metric breach (`guardrail`).
20
+ ruleId:
21
+ description: >-
22
+ Id of the alert rule that raised it (`ar_…`), for `source: metric_rule`.
23
+ `null` for alerts raised outside the rules engine.
24
+ anyOf:
25
+ - type: string
26
+ - type: "null"
27
+ severity:
28
+ type: string
29
+ enum:
30
+ - danger
31
+ - warn
32
+ - info
33
+ description: How loud the alert is — `danger` pages, `warn` is the default, `info` is FYI-only.
34
+ dedupeKey:
35
+ type: string
36
+ description: Idempotency + reconcile key within `(project, source)` — re-raising the same condition refreshes the existing active row instead of stacking duplicates, and the condition clearing flips that row to `resolved`.
37
+ title:
38
+ type: string
39
+ description: Short human-readable headline (what fired).
40
+ detail:
41
+ type: string
42
+ description: Longer human-readable explanation — the observed condition, thresholds, and context.
43
+ href:
44
+ description: Dashboard-relative link to the surface that explains the alert (e.g. the experiment or kill switch page), or `null` if there is no single destination.
45
+ anyOf:
46
+ - type: string
47
+ - type: "null"
48
+ observedValue:
49
+ description: The numeric value that tripped the condition (e.g. the metric aggregate a rule compared against its threshold), or `null` when the source has no scalar observation.
50
+ anyOf:
51
+ - type: number
52
+ - type: "null"
53
+ status:
54
+ type: string
55
+ enum:
56
+ - active
57
+ - resolved
58
+ - dismissed
59
+ description: Lifecycle state. `active` means the condition currently holds (shown as "Firing" in the UI); `resolved` means it cleared (auto, when the raiser reconciles, or by hand); `dismissed` means a human waved it off.
60
+ createdAt:
61
+ type: string
62
+ description: ISO-8601 timestamp the alert was first raised.
63
+ resolvedAt:
64
+ description: ISO-8601 timestamp the alert flipped to `resolved`, or `null` while it hasn't.
65
+ anyOf:
66
+ - type: string
67
+ - type: "null"
68
+ dismissedAt:
69
+ description: ISO-8601 timestamp the alert was dismissed, or `null` while it hasn't been.
70
+ anyOf:
71
+ - type: string
72
+ - type: "null"
73
+ assigneeId:
74
+ description: PERSON owner of the triage — a `users.id` (soft reference, same value space as a feedback item's `assigneeId`), or `null` if unassigned. Set from the ops cockpit's Owner column; independent of the agent half.
75
+ anyOf:
76
+ - type: string
77
+ - type: "null"
78
+ assigneeConnectorId:
79
+ description: AGENT owner — the id of a connected trigger connector (`connectors.id`), or `null`. Mutually exclusive with `assigneeAgent`.
80
+ anyOf:
81
+ - type: string
82
+ - type: "null"
83
+ assigneeAgent:
84
+ description: AGENT owner — a built-in hosted Shipeasy agent (`"jarvis"`, Enterprise-gated), or `null`. Mutually exclusive with `assigneeConnectorId`.
85
+ anyOf:
86
+ - type: string
87
+ - type: "null"
88
+ required:
89
+ - id
90
+ - source
91
+ - ruleId
92
+ - severity
93
+ - dedupeKey
94
+ - title
95
+ - detail
96
+ - href
97
+ - observedValue
98
+ - status
99
+ - createdAt
100
+ - resolvedAt
101
+ - dismissedAt
102
+ - assigneeId
103
+ - assigneeConnectorId
104
+ - assigneeAgent
105
+ additionalProperties: false
106
+ ListAlertsResponse:
107
+ type: array
108
+ description: A bare JSON array of fired alerts, ordered by `createdAt` descending. There is no pagination envelope.
109
+ items:
110
+ $ref: "#/AlertApiRow"
111
+ UpdateAlertRequest:
112
+ type: object
113
+ description: |-
114
+ Body for `PATCH /api/admin/alerts/{id}` — the triage writes the ops cockpit needs on a fired alert. All fields optional, at least one required; only the fields present are changed.
115
+
116
+ Flipping `status` stamps the matching timestamp (`resolvedAt` on `resolved`, `dismissedAt` on `dismissed`; both cleared on `active`). The person owner (`assigneeId`) and the agent owner (`agent`) are independent halves — setting one never touches the other.
117
+ properties:
118
+ status:
119
+ type: string
120
+ enum:
121
+ - active
122
+ - resolved
123
+ - dismissed
124
+ description: New lifecycle state. `resolved` / `dismissed` stamp their timestamp; `active` re-opens and clears both.
125
+ assigneeId:
126
+ description: PERSON owner — a `users.id`, or `null` to clear the assignment.
127
+ anyOf:
128
+ - type: string
129
+ - type: "null"
130
+ agent:
131
+ description: AGENT owner — a connected trigger connector's id (`connectors.id`), the built-in `"jarvis"` (Enterprise plan only — rejected with `403` otherwise), or `null` to clear. Stored in `assigneeConnectorId` or `assigneeAgent` depending on the value; the two are mutually exclusive.
132
+ anyOf:
133
+ - type: string
134
+ - type: "null"
135
+ minProperties: 1
136
+ additionalProperties: false
137
+ UpdateAlertResponse:
138
+ $ref: "#/AlertApiRow"
@@ -3,17 +3,67 @@ ListAttributesResponse:
3
3
  items:
4
4
  type: object
5
5
  properties:
6
+ id:
7
+ type: string
8
+ description: Stable opaque attribute id.
6
9
  name:
7
10
  type: string
8
11
  description: Attribute key as seen in evaluation context (e.g. `plan`, `country`).
9
12
  type:
10
- description: Inferred value type (`string`, `number`, `boolean`, …) when known.
13
+ $ref: "#/AttributeType"
14
+ enumValues:
15
+ description: Allowed values for `enum` attributes, else `null`.
16
+ anyOf:
17
+ - type: array
18
+ items:
19
+ type: string
20
+ - type: "null"
21
+ required:
22
+ type: integer
23
+ enum:
24
+ - 0
25
+ - 1
26
+ description: Whether the attribute must be present on the evaluation context (D1 stores the flag as `0`/`1`).
27
+ description:
28
+ description: Human note shown in the dashboard, or `null`.
29
+ anyOf:
30
+ - type: string
31
+ - type: "null"
32
+ sdkPath:
33
+ description: Dotted path the SDK reads the value from, or `null`.
34
+ anyOf:
35
+ - type: string
36
+ - type: "null"
37
+ createdAt:
38
+ type: string
39
+ description: ISO-8601 creation timestamp.
40
+ source:
11
41
  type: string
42
+ enum:
43
+ - auto
44
+ - manual
45
+ description: How the attribute came to exist — `auto` (inferred by the `/sdk/evaluate` sampler) or `manual` (declared via the UI/API).
46
+ lastSeenAt:
47
+ description: ISO-8601 timestamp of the most recent SDK sample that carried this attribute, or `null` when never sampled.
48
+ anyOf:
49
+ - type: string
50
+ - type: "null"
51
+ deprecated:
52
+ type: integer
53
+ enum:
54
+ - 0
55
+ - 1
56
+ description: "`1` when an auto-discovered attribute has stopped appearing in samples. Deprecated attributes stay listed; pickers may hide or de-rank them."
12
57
  required:
58
+ - id
13
59
  - name
60
+ - type
61
+ # Deliberately left open — the endpoint returns the raw attribute row, which
62
+ # still carries undocumented internal columns; tighten to `false` only once
63
+ # the handler projects an explicit allow-list.
14
64
  additionalProperties: {}
15
- description: One auto-inferred targeting attribute.
16
- description: Every auto-inferred targeting attribute in the project.
65
+ description: One declared or auto-inferred targeting attribute row.
66
+ description: Every targeting attribute in the project (declared and auto-inferred).
17
67
  AttributeType:
18
68
  type: string
19
69
  enum:
@@ -49,6 +49,16 @@ NotificationTarget:
49
49
  additionalProperties: false
50
50
  - type: "null"
51
51
  description: Delivery target for a notification; `null` = use the project default.
52
+ OkResponse:
53
+ type: object
54
+ properties:
55
+ ok:
56
+ type: boolean
57
+ const: true
58
+ description: Always `true` — the operation succeeded (errors use the error envelope instead).
59
+ required: [ok]
60
+ additionalProperties: false
61
+ description: Generic success acknowledgement for operations with no meaningful payload.
52
62
  PageQuery:
53
63
  type: object
54
64
  properties:
@@ -88,5 +98,11 @@ ExperimentInlineMetric:
88
98
  enum: [count_users, count_events, retention_7d, retention_30d, sum, avg]
89
99
  description: Reducer for the `event` form. Defaults to `count_users`.
90
100
  value: { type: string, minLength: 1, maxLength: 256, description: Numeric event property for `sum`/`avg` (with `event`). }
101
+ min_effect_of_interest:
102
+ 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.
103
+ default: null
104
+ anyOf:
105
+ - type: number
106
+ - type: "null"
91
107
  additionalProperties: false
92
108
  description: Inline metric — a DSL `query`, or an `event` (+ `aggregation`/`value`) the server compiles into one.
@@ -17,10 +17,25 @@ ListConfigsResponse:
17
17
  anyOf:
18
18
  - type: string
19
19
  - type: "null"
20
+ folder:
21
+ description: Folder segment used for dashboard organisation, or `null` when the config lives at the root.
22
+ anyOf:
23
+ - type: string
24
+ - type: "null"
20
25
  schema:
21
26
  type: object
22
27
  additionalProperties: {}
23
28
  description: JSON Schema (draft 2020-12) describing the shape of the config value. Top-level `type` must be `'object'`; every published value is validated against this schema.
29
+ creatorEmail:
30
+ description: Resolved email of the creator (`created_by` → `users.email`), or `null` when unknown. List-only enrichment — present on `GET /api/admin/configs` rows, absent from the by-id detail — so it is optional, never required.
31
+ anyOf:
32
+ - type: string
33
+ - type: "null"
34
+ createdAt:
35
+ description: ISO-8601 creation timestamp, or `null` on legacy rows. List-only enrichment — present on `GET /api/admin/configs` rows, absent from the by-id detail — so it is optional, never required.
36
+ anyOf:
37
+ - type: string
38
+ - type: "null"
24
39
  updatedAt:
25
40
  type: string
26
41
  description: ISO-8601 timestamp of last mutation.
@@ -80,6 +95,7 @@ ListConfigsResponse:
80
95
  - id
81
96
  - name
82
97
  - description
98
+ - folder
83
99
  - schema
84
100
  - updatedAt
85
101
  - envs
@@ -107,10 +123,25 @@ GetConfigResponse:
107
123
  anyOf:
108
124
  - type: string
109
125
  - type: "null"
126
+ folder:
127
+ description: Folder segment used for dashboard organisation, or `null` when the config lives at the root.
128
+ anyOf:
129
+ - type: string
130
+ - type: "null"
110
131
  schema:
111
132
  type: object
112
133
  additionalProperties: {}
113
134
  description: JSON Schema (draft 2020-12) describing the shape of the config value. Top-level `type` must be `'object'`; every published value is validated against this schema.
135
+ creatorEmail:
136
+ description: Resolved email of the creator (`created_by` → `users.email`), or `null` when unknown. List-only enrichment — present on `GET /api/admin/configs` rows, absent from this by-id detail — so it is optional, never required.
137
+ anyOf:
138
+ - type: string
139
+ - type: "null"
140
+ createdAt:
141
+ description: ISO-8601 creation timestamp, or `null` on legacy rows. List-only enrichment — present on `GET /api/admin/configs` rows, absent from this by-id detail — so it is optional, never required.
142
+ anyOf:
143
+ - type: string
144
+ - type: "null"
114
145
  updatedAt:
115
146
  type: string
116
147
  description: ISO-8601 timestamp of last mutation.
@@ -170,6 +201,7 @@ GetConfigResponse:
170
201
  - id
171
202
  - name
172
203
  - description
204
+ - folder
173
205
  - schema
174
206
  - updatedAt
175
207
  - envs
@@ -369,3 +401,29 @@ ListConfigActivityResponse:
369
401
  - createdAt
370
402
  additionalProperties: false
371
403
  description: Recent audit rows for one config, newest first.
404
+ ListConfigVersionsResponse:
405
+ type: array
406
+ items:
407
+ type: object
408
+ properties:
409
+ version:
410
+ type: integer
411
+ minimum: 1
412
+ maximum: 9007199254740991
413
+ description: Published version number on the requested env (monotonically increasing per env).
414
+ value:
415
+ description: The full JSON value that was published at this version.
416
+ publishedAt:
417
+ type: string
418
+ description: ISO-8601 publish timestamp.
419
+ publishedBy:
420
+ type: string
421
+ description: Email/id of the actor who published this version.
422
+ required:
423
+ - version
424
+ - value
425
+ - publishedAt
426
+ - publishedBy
427
+ additionalProperties: false
428
+ description: One published version of the config's value on the requested env.
429
+ description: Full published-value history for one (config, env), newest first. The config's schema is config-level and not versioned — this is value history only.
@@ -47,6 +47,9 @@ ConnectorRecord:
47
47
  - type: string
48
48
  - type: "null"
49
49
  description: "Display label for the connected account / target (e.g. the OAuth account email, or a trigger's idempotency key such as its repo url or routine id). `null` until the connector is authenticated/configured."
50
+ hasCredentials:
51
+ type: boolean
52
+ description: "Whether an encrypted credentials cipher is stored for this connector (`true` once OAuth/token setup completed). The cipher itself is never serialised — this boolean is the only signal. `false` for a fresh OAuth stub or a tokenless trigger."
50
53
  lastError:
51
54
  anyOf:
52
55
  - type: string
@@ -77,13 +80,14 @@ ConnectorRecord:
77
80
  - events
78
81
  - config
79
82
  - accountLabel
83
+ - hasCredentials
80
84
  - lastError
81
85
  - lastAttemptAt
82
86
  - lastSuccessAt
83
87
  - createdAt
84
88
  - updatedAt
85
89
  additionalProperties: false
86
- description: "A connector row. The encrypted credentials cipher backing the connector is intentionally never serialised."
90
+ description: "A connector row. The encrypted credentials cipher backing the connector is intentionally never serialised — `hasCredentials` reports only whether one is stored."
87
91
  ListConnectorsResponse:
88
92
  type: array
89
93
  items:
@@ -3,7 +3,7 @@ ErrorRecord:
3
3
  description: |-
4
4
  A tracked production error — one row per distinct issue, keyed by `fingerprint`. Rows are never created by hand: an ingestion path (worker log drain / the `see()` SDK reporter) folds each occurrence into the matching row, bumping `count` and `lastSeenAt`. The admin surface only lists them, reads one, and flips `status`.
5
5
 
6
- Field names are camelCase (the D1 row projected through Drizzle). Many columns are nullable because the reporting source may not supply them.
6
+ Field names are camelCase (the D1 row projected through Drizzle). Every row column is always present on the wire; many are *nullable* because the reporting source may not supply them — `null`, never absent. Only `occurrences` is conditional (detail reads only).
7
7
  properties:
8
8
  id:
9
9
  type: string
@@ -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:
@@ -111,21 +121,119 @@ ErrorRecord:
111
121
  updatedAt:
112
122
  type: string
113
123
  description: ISO-8601 timestamp of the last mutation (e.g. a status flip).
124
+ assigneeId:
125
+ description: PERSON owner of the triage — a `users.id` (soft reference, same value space as a feedback item's `assigneeId`), or `null` if unassigned. Set from the ops cockpit's Owner column; independent of the agent half.
126
+ anyOf:
127
+ - type: string
128
+ - type: "null"
129
+ assigneeConnectorId:
130
+ description: AGENT owner — the id of a connected trigger connector (`connectors.id`), or `null`. Mutually exclusive with `assigneeAgent`.
131
+ anyOf:
132
+ - type: string
133
+ - type: "null"
134
+ assigneeAgent:
135
+ description: AGENT owner — a built-in hosted Shipeasy agent (`"jarvis"`, Enterprise-gated), or `null`. Mutually exclusive with `assigneeConnectorId`.
136
+ anyOf:
137
+ - type: string
138
+ - type: "null"
114
139
  required:
115
140
  - id
116
141
  - projectId
117
142
  - fingerprint
143
+ - causedByFingerprint
118
144
  - message
145
+ - errorType
146
+ - stack
147
+ - source
148
+ - url
149
+ - seenUrls
150
+ - subject
151
+ - outcome
152
+ - side
153
+ - env
154
+ - kind
155
+ - lastExtrasJson
156
+ - sdkVersion
119
157
  - count
120
158
  - status
121
159
  - firstSeenAt
122
160
  - lastSeenAt
123
161
  - createdAt
124
162
  - updatedAt
163
+ - assigneeId
164
+ - assigneeConnectorId
165
+ - assigneeAgent
166
+ additionalProperties: false
167
+ ErrorOccurrence:
168
+ type: object
169
+ description: One sampled instance behind a tracked error — the minimal per-occurrence payload (what varies between instances; everything else lives on the parent `ErrorRecord`). Every column is always present on the wire; nullable columns are `null`, never absent.
170
+ properties:
171
+ id:
172
+ type: string
173
+ description: Stable opaque occurrence id.
174
+ projectId:
175
+ type: string
176
+ description: Project this occurrence belongs to. Always echoes the parent error's `projectId` — redundant on this nested read (a projection candidate), kept because the raw row is returned as-is today.
177
+ errorId:
178
+ type: string
179
+ description: Id of the parent tracked error (`err_…`). Always echoes the enclosing `ErrorRecord.id` — redundant on this nested read (a projection candidate), kept because the raw row is returned as-is today.
180
+ message:
181
+ type: string
182
+ description: This instance's raw message (unlike the fingerprint, never normalized).
183
+ stack:
184
+ description: This instance's stack trace, or `null` if none was captured.
185
+ anyOf:
186
+ - type: string
187
+ - type: "null"
188
+ url:
189
+ description: This instance's raw URL (ids intact), or `null`.
190
+ anyOf:
191
+ - type: string
192
+ - type: "null"
193
+ env:
194
+ description: Published env this instance ran against, or `null`.
195
+ anyOf:
196
+ - type: string
197
+ - type: "null"
198
+ side:
199
+ description: Which SDK side reported it — `client` or `server`. `null` if unknown.
200
+ anyOf:
201
+ - type: string
202
+ - type: "null"
203
+ sdkVersion:
204
+ description: "`@shipeasy/sdk` version of this instance, or `null`."
205
+ anyOf:
206
+ - type: string
207
+ - type: "null"
208
+ extrasJson:
209
+ description: This instance's sanitized extras, JSON-encoded. `null` if none.
210
+ anyOf:
211
+ - type: string
212
+ - type: "null"
213
+ sampleRate:
214
+ type: integer
215
+ minimum: 1
216
+ description: 1-in-N sampling rate in force when this instance was kept (`1` = the issue was still being recorded exhaustively).
217
+ seenAt:
218
+ type: string
219
+ description: ISO-8601 timestamp this instance was ingested.
220
+ required:
221
+ - id
222
+ - projectId
223
+ - errorId
224
+ - message
225
+ - stack
226
+ - url
227
+ - env
228
+ - side
229
+ - sdkVersion
230
+ - extrasJson
231
+ - sampleRate
232
+ - seenAt
125
233
  additionalProperties: false
126
234
  ListErrorsResponse:
127
235
  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.
236
+ 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
237
  items:
130
238
  $ref: "#/ErrorRecord"
131
239
  UpdateErrorStatusRequest:
@@ -151,8 +259,10 @@ FileErrorTicketResponse:
151
259
  type: string
152
260
  description: Feedback ticket id.
153
261
  number:
154
- type: integer
155
- description: Human-facing per-project ticket number.
262
+ description: Human-facing per-project ticket number. `null` only on the idempotent dedupe path when the pre-existing ticket is a legacy row created before per-project numbering.
263
+ anyOf:
264
+ - type: integer
265
+ - type: "null"
156
266
  required:
157
267
  - id
158
268
  - number