@shipeasy/openapi 3.1.0 → 3.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 (45) hide show
  1. package/README.md +1 -1
  2. package/dist/{chunk-V4ZESAIF.js → chunk-75Z4SQOM.js} +141 -1
  3. package/dist/chunk-75Z4SQOM.js.map +1 -0
  4. package/dist/{chunk-UV333BIK.js → chunk-ZYK74ZIW.js} +569 -119
  5. package/dist/chunk-ZYK74ZIW.js.map +1 -0
  6. package/dist/{client-yqGYju-M.d.ts → client-DAGqRpF1.d.ts} +2209 -401
  7. package/dist/client.d.ts +1 -1
  8. package/dist/client.js +33 -1
  9. package/dist/custom.d.ts +5 -5
  10. package/dist/custom.js +128 -84
  11. package/dist/custom.js.map +1 -1
  12. package/dist/index.d.ts +2 -2
  13. package/dist/index.js +34 -2
  14. package/dist/index.js.map +1 -1
  15. package/dist/schemas.d.ts +2007 -891
  16. package/dist/schemas.js +179 -1
  17. package/openapi-sdk.json +1769 -0
  18. package/openapi.json +5861 -2597
  19. package/openapi.yaml +2739 -452
  20. package/package.json +6 -4
  21. package/spec/components/responses.yaml +39 -0
  22. package/spec/components/schemas/alert-rules.yaml +405 -7
  23. package/spec/components/schemas/alerts.yaml +9 -7
  24. package/spec/components/schemas/common.yaml +2 -2
  25. package/spec/components/schemas/connectors.yaml +69 -6
  26. package/spec/components/schemas/events.yaml +20 -9
  27. package/spec/components/schemas/gates.yaml +87 -0
  28. package/spec/components/schemas/keys.yaml +32 -2
  29. package/spec/components/schemas/killswitches.yaml +64 -0
  30. package/spec/components/schemas/metrics.yaml +131 -251
  31. package/spec/components/schemas/ops.yaml +555 -22
  32. package/spec/components/schemas/projects.yaml +18 -0
  33. package/spec/components/schemas/tags.yaml +91 -0
  34. package/spec/openapi-sdk.yaml +86 -0
  35. package/spec/openapi.yaml +61 -0
  36. package/spec/paths/alerts.yaml +5 -3
  37. package/spec/paths/gates.yaml +282 -0
  38. package/spec/paths/i18n.yaml +2 -1
  39. package/spec/paths/keys.yaml +79 -0
  40. package/spec/paths/killswitches.yaml +96 -0
  41. package/spec/paths/metrics.yaml +26 -17
  42. package/spec/paths/ops.yaml +566 -10
  43. package/spec/paths/tags.yaml +270 -0
  44. package/dist/chunk-UV333BIK.js.map +0 -1
  45. package/dist/chunk-V4ZESAIF.js.map +0 -1
@@ -171,6 +171,12 @@ GetCurrentProjectResponse:
171
171
  - type: boolean
172
172
  - type: number
173
173
  description: Whether key-authenticated public ticket creation (`POST /cli/report`) is allowed into this project. Off by default.
174
+ errorAutocloseDays:
175
+ type: integer
176
+ description: Days an `open` tracked error may go unseen before the nightly sweep auto-resolves it. `0` disables auto-close for this project. Ingest reopens a resolved error the moment it recurs, so this never hides a live issue.
177
+ errorTicketMinOccurrences:
178
+ type: integer
179
+ description: Occurrence count a tracked error must cross before an `error` ticket is auto-filed into the ops queue. Customising it requires a paid plan; unentitled projects always run at the platform default of 10.
174
180
  stripeSubscriptionId:
175
181
  anyOf:
176
182
  - type: string
@@ -250,6 +256,8 @@ GetCurrentProjectResponse:
250
256
  - moduleUser
251
257
  - moduleEvents
252
258
  - allowPublicTickets
259
+ - errorAutocloseDays
260
+ - errorTicketMinOccurrences
253
261
  - slug
254
262
  - logo
255
263
  - defaultEnv
@@ -462,5 +470,15 @@ UpdateProjectRequest:
462
470
  minimum: 0.0001
463
471
  maximum: 0.05
464
472
  description: SRM chi-square p-value below which the run is called invalid.
473
+ errorAutocloseDays:
474
+ type: integer
475
+ minimum: 0
476
+ maximum: 365
477
+ description: Days an `open` tracked error may go unseen before the nightly sweep auto-resolves it. `0` disables auto-close for this project.
478
+ errorTicketMinOccurrences:
479
+ type: integer
480
+ minimum: 1
481
+ maximum: 10000
482
+ description: Occurrence count a tracked error must cross before an `error` ticket is auto-filed into the ops queue. Requires a paid plan — the request is rejected with 403 on a plan without the `ops_auto_error_issues` entitlement.
465
483
  additionalProperties: false
466
484
  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."
@@ -0,0 +1,91 @@
1
+ TaggableEntityType:
2
+ type: string
3
+ enum:
4
+ - ops
5
+ - flag
6
+ - config
7
+ - killswitch
8
+ - metric
9
+ - error
10
+ - alert_rule
11
+ - experiment
12
+ description: >-
13
+ The kind of item a tag set hangs off: `ops` (any queue item — bug, feature
14
+ request, error/alert/measure-plan ticket), `flag` (feature gate), `config`
15
+ (dynamic config), `killswitch`, `metric`, `error` (tracked error group),
16
+ `alert_rule`, or `experiment`.
17
+ TagName:
18
+ type: string
19
+ minLength: 1
20
+ maxLength: 64
21
+ description: A tag name. Trimmed; matched case-insensitively against the project's existing tags.
22
+ Tag:
23
+ type: object
24
+ properties:
25
+ id:
26
+ type: string
27
+ description: Stable opaque tag id.
28
+ name:
29
+ type: string
30
+ description: Display name — also the tag's identity, unique case-insensitively within the project.
31
+ required:
32
+ - id
33
+ - name
34
+ additionalProperties: false
35
+ ListTagsResponse:
36
+ type: array
37
+ items:
38
+ $ref: "#/Tag"
39
+ description: The project's tags, sorted by name.
40
+ EntityTagsResponse:
41
+ type: object
42
+ properties:
43
+ entityType:
44
+ $ref: "#/TaggableEntityType"
45
+ entityId:
46
+ type: string
47
+ description: The item's canonical id (the handle you passed, resolved).
48
+ tags:
49
+ type: array
50
+ items:
51
+ type: string
52
+ description: The tag names now attached to the item, sorted by name.
53
+ required:
54
+ - entityType
55
+ - entityId
56
+ - tags
57
+ additionalProperties: false
58
+ SetEntityTagsRequest:
59
+ type: object
60
+ properties:
61
+ tags:
62
+ type: array
63
+ maxItems: 50
64
+ items:
65
+ $ref: "#/TagName"
66
+ description: >-
67
+ The complete tag set the item should carry — tags not listed are
68
+ detached. Names are get-or-created (a new name mints a project tag),
69
+ deduped case-insensitively; `[]` clears every tag.
70
+ required:
71
+ - tags
72
+ additionalProperties: false
73
+ UpdateEntityTagsRequest:
74
+ type: object
75
+ properties:
76
+ add:
77
+ type: array
78
+ maxItems: 50
79
+ items:
80
+ $ref: "#/TagName"
81
+ default: []
82
+ description: Tag names to attach (get-or-created by name, case-insensitive). Already-attached names are a no-op.
83
+ remove:
84
+ type: array
85
+ maxItems: 50
86
+ items:
87
+ $ref: "#/TagName"
88
+ default: []
89
+ description: Tag names to detach (case-insensitive). Names the item doesn't carry are a no-op. A name in both lists is removed.
90
+ additionalProperties: false
91
+ description: Incremental tag edit — pass `add`, `remove`, or both. Every other tag on the item is left as is.
@@ -0,0 +1,86 @@
1
+ # The SERVER-SDK surface of the admin API — a second root over the same
2
+ # `paths/` and `components/schemas/` tree that `openapi.yaml` bundles.
3
+ #
4
+ # WHY A SECOND ROOT. The CLI and MCP consume the FULL spec (127 operations);
5
+ # the published server SDKs ship a deliberately tiny opt-in `AdminClient`, and
6
+ # vendoring the whole contract into eight repos bloated each generated client
7
+ # into the megabytes for operations nobody called. This file IS that smaller
8
+ # contract, declared rather than derived: `paths` below lists exactly the
9
+ # operations the SDKs expose, and `redocly bundle` pulls in only the schemas
10
+ # those operations transitively reference.
11
+ #
12
+ # WHY IT'S EXACT. Every path listed here is a dedicated, single-purpose route,
13
+ # so a path item never drags in a method the SDK shouldn't have. That is the
14
+ # reason the SDK-facing endpoints were built as their own paths instead of
15
+ # extra query/discriminator modes on the generic CRUD routes.
16
+ #
17
+ # ADDING AN OPERATION is one `$ref` here — then `pnpm gen` in this package and
18
+ # `pnpm sdk:spec:regen` in the monorepo, and commit the regenerated client
19
+ # inside each SDK submodule. A monorepo pre-commit hook blocks any commit that
20
+ # changes this contract while a vendored copy is stale.
21
+ #
22
+ # DELIBERATELY ABSENT, so nobody re-adds them by accident: everything else.
23
+ # Listing, generic CRUD, key minting, projects, connectors, i18n, errors,
24
+ # alerts, metrics, events, experiments, universes, templates, attributes, and
25
+ # the AI-agent working seam (ack / link-pr / investigations) all stay reachable
26
+ # through the CLI and MCP, which read `openapi.yaml`.
27
+ openapi: 3.2.0
28
+ info:
29
+ title: Shipeasy Admin API (server-SDK surface)
30
+ version: 2.0.0
31
+ description: |-
32
+ The slice of the Shipeasy admin API that the published server SDKs expose as their optional `AdminClient`. Authenticate with an admin SDK key (`Authorization: Bearer sdk_admin_…`) and scope every request to a project via the `X-Project-Id` header.
33
+
34
+ Three capabilities, nothing else:
35
+
36
+ - **File a public ticket** — a bug or a feature request onto the project's ops queue.
37
+ - **Toggle a kill switch** — flip the switch itself, or one of its named sub-switches, on one environment.
38
+ - **Manage a flag's whitelist** — read, replace, add to, or remove from the allowlist that admits specific identities ahead of every targeting rule.
39
+
40
+ Everything else in the admin API is reachable through the Shipeasy CLI and MCP server, which speak the complete contract (`openapi.yaml`).
41
+ contact:
42
+ name: Shipeasy
43
+ url: https://shipeasy.ai
44
+ license:
45
+ name: Proprietary
46
+ identifier: LicenseRef-Proprietary
47
+ servers:
48
+ - url: https://shipeasy.ai
49
+ description: Production
50
+ - url: http://localhost:3000
51
+ description: Local Next.js dev server
52
+ security:
53
+ - bearerSdkKey: []
54
+ tags:
55
+ - name: Flags
56
+ description: A feature gate's whitelist — the always-first allowlist that admits named identities before any targeting rule or percentage rollout runs.
57
+ - name: Killswitch
58
+ description: Per-env boolean overrides for incident response. `toggle` reads the current value and publishes its opposite in one call, on the flat value or on one named sub-switch.
59
+ - name: Ops
60
+ description: The public ticket queue — filing a bug or a feature request, which also fires the project's GitHub/Slack connectors.
61
+ paths:
62
+ /ops/bug:
63
+ $ref: ./paths/ops.yaml#/~1ops~1bug
64
+ /ops/feature-request:
65
+ $ref: ./paths/ops.yaml#/~1ops~1feature-request
66
+ /api/admin/killswitches/{id}/toggle:
67
+ $ref: ./paths/killswitches.yaml#/~1api~1admin~1killswitches~1{id}~1toggle
68
+ /api/admin/gates/{id}/whitelist:
69
+ $ref: ./paths/gates.yaml#/~1api~1admin~1gates~1{id}~1whitelist
70
+ components:
71
+ securitySchemes:
72
+ bearerSdkKey:
73
+ type: http
74
+ scheme: bearer
75
+ bearerFormat: sdk_admin_*
76
+ description: 'Pass an admin SDK key as `Authorization: Bearer sdk_admin_…`. Mint via `POST /api/admin/keys` with `type: "admin"`.'
77
+ clientSdkKey:
78
+ type: apiKey
79
+ in: header
80
+ name: X-SDK-Key
81
+ description: |-
82
+ Pass a **client** SDK key as `X-SDK-Key: sdk_client_…`. Used only by the public ticket intake (`POST /ops/bug`, `POST /ops/feature-request`), which is served by the edge worker rather than the admin API.
83
+
84
+ Client keys are designed to be embedded in shipped code — a CLI, an installer, a browser bundle — so presenting one here is safe by construction: it can do nothing but file a rate-limited ticket into its own project, in a state a human must approve. It carries no read access and cannot reach any other operation in this contract.
85
+
86
+ The key must additionally carry the `tickets:public_create` scope, and its project must have public ticket creation enabled.
package/spec/openapi.yaml CHANGED
@@ -218,6 +218,24 @@ tags:
218
218
  the AI-write seam the cockpit's detail panel renders: `list` reads what a
219
219
  previous run found, `create` appends a new record, and `update` fills in the
220
220
  `working` run record you were handed (findings, PR, confidence) as you work.
221
+ - name: Questions
222
+ parent: Ops
223
+ description: |-
224
+ Structured questions an agent asks the team about a queue item — the
225
+ `AskUserQuestion` shape: a short `header`, the `question`, 2–4 `options`
226
+ (label + description), and `multiSelect` for checkboxes instead of radios.
227
+ Every question also accepts a free-text "Other" answer. Open questions sit
228
+ at the very top of the item's detail panel and mark the row in the queue.
229
+
230
+ **Asking.** `ask` posts 1–4 questions at once. Several agents (or several
231
+ runs) may have questions open on the same item at the same time.
232
+
233
+ **Getting the answer back.** `delivery: resume` (the default for a run
234
+ launched through a trigger connector) re-fires that same agent on the
235
+ item once every question its run asked is answered, continuing the same
236
+ run record with the answers in the prompt. `delivery: poll` (the default
237
+ for a live, connector-less session such as Claude Code in a terminal)
238
+ fires nothing — call `list` until the status reads `completed`.
221
239
  - name: Projects
222
240
  description: |-
223
241
  Projects: the account-level container every other resource is scoped to.
@@ -260,6 +278,19 @@ tags:
260
278
  Continue) schedule on their own surface — typically a GitHub Actions
261
279
  `schedule:` cron running the platform's headless CLI with the shared
262
280
  trigger prompt.
281
+ - name: Tags
282
+ description: |-
283
+ Tags: free-form, project-scoped labels any item can carry — queue items
284
+ (bugs, feature requests, error/alert tickets), flags, configs, kill
285
+ switches, metrics, tracked errors, alert rules, and experiments all share
286
+ one tag roster.
287
+
288
+ **Identity.** A tag is its name, unique case-insensitively per project. A
289
+ tag is born the first time it's attached — there is no separate create.
290
+
291
+ **Addressing.** An item is named by `entityType` + its handle (an ops
292
+ item's number, a resource's name, or any id). `set` replaces the full tag
293
+ set; `update` adds/removes names incrementally.
263
294
  - name: Connectors
264
295
  description: |-
265
296
  Outbound integrations: the per-project destinations feedback + trigger
@@ -282,6 +313,8 @@ paths:
282
313
  $ref: ./paths/gates.yaml#/~1api~1admin~1gates~1{id}~1disable
283
314
  /api/admin/gates/{id}/activity:
284
315
  $ref: ./paths/gates.yaml#/~1api~1admin~1gates~1{id}~1activity
316
+ /api/admin/gates/{id}/whitelist:
317
+ $ref: ./paths/gates.yaml#/~1api~1admin~1gates~1{id}~1whitelist
285
318
  /api/admin/experiments:
286
319
  $ref: ./paths/experiments.yaml#/~1api~1admin~1experiments
287
320
  /api/admin/experiments/{id}:
@@ -322,6 +355,8 @@ paths:
322
355
  $ref: ./paths/killswitches.yaml#/~1api~1admin~1killswitches~1{id}~1switch
323
356
  /api/admin/killswitches/{id}/value:
324
357
  $ref: ./paths/killswitches.yaml#/~1api~1admin~1killswitches~1{id}~1value
358
+ /api/admin/killswitches/{id}/toggle:
359
+ $ref: ./paths/killswitches.yaml#/~1api~1admin~1killswitches~1{id}~1toggle
325
360
  /api/admin/universes:
326
361
  $ref: ./paths/universes.yaml#/~1api~1admin~1universes
327
362
  /api/admin/universes/{id}:
@@ -352,6 +387,10 @@ paths:
352
387
  $ref: ./paths/events.yaml#/~1api~1admin~1events~1{id}~1approve
353
388
  /api/admin/ops:
354
389
  $ref: ./paths/ops.yaml#/~1api~1admin~1ops
390
+ /ops/bug:
391
+ $ref: ./paths/ops.yaml#/~1ops~1bug
392
+ /ops/feature-request:
393
+ $ref: ./paths/ops.yaml#/~1ops~1feature-request
355
394
  /api/admin/ops/{handle}:
356
395
  $ref: ./paths/ops.yaml#/~1api~1admin~1ops~1{handle}
357
396
  /api/admin/ops/{handle}/link-pr:
@@ -362,6 +401,12 @@ paths:
362
401
  $ref: ./paths/ops.yaml#/~1api~1admin~1ops~1{handle}~1investigation
363
402
  /api/admin/ops/{handle}/investigation/{investigationId}:
364
403
  $ref: ./paths/ops.yaml#/~1api~1admin~1ops~1{handle}~1investigation~1{investigationId}
404
+ /api/admin/ops/{handle}/questions:
405
+ $ref: ./paths/ops.yaml#/~1api~1admin~1ops~1{handle}~1questions
406
+ /api/admin/ops/{handle}/questions/{questionId}/answer:
407
+ $ref: ./paths/ops.yaml#/~1api~1admin~1ops~1{handle}~1questions~1{questionId}~1answer
408
+ /api/admin/ops/{handle}/questions/{questionId}/cancel:
409
+ $ref: ./paths/ops.yaml#/~1api~1admin~1ops~1{handle}~1questions~1{questionId}~1cancel
365
410
  /api/admin/agent-profiles:
366
411
  $ref: ./paths/ops.yaml#/~1api~1admin~1agent-profiles
367
412
  /api/admin/ops/{handle}/comments:
@@ -426,10 +471,16 @@ paths:
426
471
  $ref: ./paths/connectors.yaml#/~1api~1admin~1connectors~1trigger
427
472
  /api/admin/keys:
428
473
  $ref: ./paths/keys.yaml#/~1api~1admin~1keys
474
+ /api/admin/keys/{id}:
475
+ $ref: ./paths/keys.yaml#/~1api~1admin~1keys~1{id}
429
476
  /api/admin/keys/{id}/revoke:
430
477
  $ref: ./paths/keys.yaml#/~1api~1admin~1keys~1{id}~1revoke
431
478
  /api/admin/search:
432
479
  $ref: ./paths/search.yaml#/~1api~1admin~1search
480
+ /api/admin/tags:
481
+ $ref: ./paths/tags.yaml#/~1api~1admin~1tags
482
+ /api/admin/tags/{entityType}/{entityId}:
483
+ $ref: ./paths/tags.yaml#/~1api~1admin~1tags~1{entityType}~1{entityId}
433
484
  components:
434
485
  securitySchemes:
435
486
  bearerSdkKey:
@@ -437,3 +488,13 @@ components:
437
488
  scheme: bearer
438
489
  bearerFormat: sdk_admin_*
439
490
  description: 'Pass an admin SDK key as `Authorization: Bearer sdk_admin_…`. Mint via `POST /api/admin/keys` with `type: "admin"`.'
491
+ clientSdkKey:
492
+ type: apiKey
493
+ in: header
494
+ name: X-SDK-Key
495
+ description: |-
496
+ Pass a **client** SDK key as `X-SDK-Key: sdk_client_…`. Used only by the public ticket intake (`POST /ops/bug`, `POST /ops/feature-request`), which is served by the edge worker rather than the admin API.
497
+
498
+ Client keys are designed to be embedded in shipped code — a CLI, an installer, a browser bundle — so presenting one here is safe by construction: it can do nothing but file a rate-limited ticket into its own project, in a state a human must approve. It carries no read access and cannot reach any other operation in this contract.
499
+
500
+ The key must additionally carry the `tickets:public_create` scope, and its project must have public ticket creation enabled.
@@ -5,7 +5,9 @@
5
5
  description: |-
6
6
  Returns the project's FIRED alerts as a **bare JSON array** (no pagination envelope), ordered by `createdAt desc`. Defaults to the currently-firing ones (`status=active`); pass a `status` to widen to resolved/dismissed history or `all`.
7
7
 
8
- Fired alerts are raised only by the platform — the UI's killswitch handlers plus the worker's analysis consumer and alerts cron — never filed by hand, so this surface is list + triage (via PATCH), with no create. The rules that *define* metric-threshold alerts live at `/api/admin/alert-rules`.
8
+ A fired alert is an **instance** of an alert rule, and an instance IS its ops queue item — the same row `/api/admin/ops` returns with `type: "alert"`, viewed through the alert lens. One rule produces many instances over its life and at most one is open at a time: when the condition clears the instance closes, and the next firing is a NEW instance with its own id and history. That is why `severity` reads off the rule (the queue item carries it as `priority`) and why resolving the queue item and clearing the alert are one act, not two rows to keep in step.
9
+
10
+ Instances are opened only by the platform — the UI's killswitch handlers plus the worker's analysis consumer and alerts cron — never filed by hand, so this surface is list + triage (via PATCH), with no create. The rules that *define* metric-threshold alerts live at `/api/admin/alert-rules`.
9
11
 
10
12
  **Use case:** Snapshot what is currently firing for an on-call view or the home Alerts block, or pull `status=all` to audit how a noisy rule has behaved over time.
11
13
  tags:
@@ -89,7 +91,7 @@
89
91
  description: |-
90
92
  Triage writes on one fired alert — the only mutations this surface allows. All body fields are optional (at least one required); only the fields present are changed.
91
93
 
92
- - **`status`** — flip between `active` / `resolved` / `dismissed`. `resolved` and `dismissed` stamp `resolvedAt` / `dismissedAt`; `active` re-opens and clears both. A resolved alert re-fires (as the same row, re-activated) if its condition is raised again.
94
+ - **`status`** — flip between `active` / `resolved` / `dismissed`. `resolved` and `dismissed` stamp `resolvedAt` / `dismissedAt`; `active` re-opens and clears both. These write the underlying queue item's own status (`resolved` / `wont_fix` / `open`), so clearing an alert here and resolving it in the ops queue are the same act. A cleared instance is never revived: if the condition fires again it opens a NEW instance with its own id.
93
95
  - **`assigneeId`** — the PERSON owner (a `users.id`), or `null` to unassign.
94
96
  - **`agent`** — the AGENT owner: a connected trigger connector's id, or the built-in `"jarvis"` (**Enterprise plan only** — `403` otherwise), or `null` to clear. Person and agent halves are independent.
95
97
 
@@ -106,7 +108,7 @@
106
108
  - name: id
107
109
  in: path
108
110
  required: true
109
- description: Stable opaque alert id (`al_…`).
111
+ description: Stable opaque instance id — the same id as the `alert` ops item this instance is.
110
112
  schema:
111
113
  $ref: ../components/schemas/common.yaml#/ResourceId
112
114
  requestBody:
@@ -601,3 +601,285 @@
601
601
  name: activity
602
602
  positional:
603
603
  - id
604
+ /api/admin/gates/{id}/whitelist:
605
+ get:
606
+ operationId: getGateWhitelist
607
+ summary: Read a gate's whitelist
608
+ description: |-
609
+ Returns the gate's whitelist — the always-first allowlist that admits the listed identities before any targeting rule or percentage rollout is evaluated.
610
+
611
+ A gate with no whitelist returns `entries: []` (and the default `attr`), never a 404 — so a caller can read-then-write without special-casing the empty gate.
612
+
613
+ **Use case:** Check whether an account is already let through before adding it.
614
+ tags:
615
+ - Flags
616
+ parameters:
617
+ - $ref: ../components/parameters.yaml#/ProjectId
618
+ - name: id
619
+ in: path
620
+ required: true
621
+ description: Stable opaque gate id (`gate_…`) or the gate's `name`.
622
+ schema:
623
+ $ref: ../components/schemas/common.yaml#/ResourceId
624
+ responses:
625
+ "200":
626
+ description: Read a gate's whitelist
627
+ content:
628
+ application/json:
629
+ schema:
630
+ $ref: ../components/schemas/gates.yaml#/GetGateWhitelistResponse
631
+ example:
632
+ id: gate_01j7w8a1b2c3d4e5f6g7h8i9j0
633
+ name: new_checkout
634
+ attr: email
635
+ entries:
636
+ - alice@acme.dev
637
+ - bob@acme.dev
638
+ "400":
639
+ $ref: ../components/responses.yaml#/BadRequest
640
+ "401":
641
+ $ref: ../components/responses.yaml#/Unauthorized
642
+ "403":
643
+ $ref: ../components/responses.yaml#/Forbidden
644
+ "404":
645
+ $ref: ../components/responses.yaml#/NotFound
646
+ "409":
647
+ $ref: ../components/responses.yaml#/Conflict
648
+ "422":
649
+ $ref: ../components/responses.yaml#/UnprocessableEntity
650
+ x-error-codes:
651
+ - BAD_REQUEST
652
+ - UNAUTHORIZED
653
+ - FORBIDDEN
654
+ - NOT_FOUND
655
+ x-cli:
656
+ name: whitelist
657
+ positional:
658
+ - id
659
+ put:
660
+ operationId: setGateWhitelist
661
+ summary: Replace a gate's whitelist
662
+ description: |-
663
+ Replaces the gate's whole whitelist with `entries`, creating the block if the gate didn't have one. Idempotent — the same call twice leaves the same list.
664
+
665
+ This is the only whitelist call that can switch `attr` (`email` ⇄ `user_id`) or clear the block: `entries: []` removes the whitelist from the gate entirely.
666
+
667
+ **Use cases**
668
+
669
+ - **Pin an exact list** — `{ "entries": ["alice@acme.dev", "bob@acme.dev"] }`.
670
+ - **Switch to user ids** — `{ "attr": "user_id", "entries": ["usr_123"] }`.
671
+ - **Drop the whitelist** — `{ "entries": [] }`.
672
+ tags:
673
+ - Flags
674
+ parameters:
675
+ - $ref: ../components/parameters.yaml#/ProjectId
676
+ - name: id
677
+ in: path
678
+ required: true
679
+ description: Stable opaque gate id (`gate_…`) or the gate's `name`.
680
+ schema:
681
+ $ref: ../components/schemas/common.yaml#/ResourceId
682
+ requestBody:
683
+ required: true
684
+ content:
685
+ application/json:
686
+ schema:
687
+ $ref: ../components/schemas/gates.yaml#/SetGateWhitelistRequest
688
+ examples:
689
+ pinList:
690
+ summary: Pin an exact list of emails
691
+ value:
692
+ entries:
693
+ - alice@acme.dev
694
+ - bob@acme.dev
695
+ switchToUserIds:
696
+ summary: Re-key the whitelist onto user ids
697
+ description: Switching `attr` replaces the entries wholesale — the old emails are not translated.
698
+ value:
699
+ attr: user_id
700
+ entries:
701
+ - usr_01j7w8a1b2c3d4e5f6g7h8i9j0
702
+ clear:
703
+ summary: Remove the whitelist
704
+ description: An empty list drops the pinned block from the gate's stack.
705
+ value:
706
+ entries: []
707
+ responses:
708
+ "200":
709
+ description: Replace a gate's whitelist
710
+ content:
711
+ application/json:
712
+ schema:
713
+ $ref: ../components/schemas/gates.yaml#/SetGateWhitelistResponse
714
+ example:
715
+ id: gate_01j7w8a1b2c3d4e5f6g7h8i9j0
716
+ name: new_checkout
717
+ attr: email
718
+ entries:
719
+ - alice@acme.dev
720
+ - bob@acme.dev
721
+ "400":
722
+ $ref: ../components/responses.yaml#/BadRequest
723
+ "401":
724
+ $ref: ../components/responses.yaml#/Unauthorized
725
+ "403":
726
+ $ref: ../components/responses.yaml#/Forbidden
727
+ "404":
728
+ $ref: ../components/responses.yaml#/NotFound
729
+ "409":
730
+ $ref: ../components/responses.yaml#/Conflict
731
+ "422":
732
+ $ref: ../components/responses.yaml#/UnprocessableEntity
733
+ x-error-codes:
734
+ - BAD_REQUEST
735
+ - UNAUTHORIZED
736
+ - FORBIDDEN
737
+ - NOT_FOUND
738
+ - VALIDATION
739
+ x-cli:
740
+ name: whitelist-set
741
+ positional:
742
+ - id
743
+ post:
744
+ operationId: addToGateWhitelist
745
+ summary: Add entries to a gate's whitelist
746
+ description: |-
747
+ Adds identities to the gate's whitelist, creating the block if the gate doesn't have one yet. Entries already on the list are skipped, so the call is idempotent and safe to retry.
748
+
749
+ Adding to a gate that already has a whitelist keyed on the other attribute is rejected (409) rather than silently re-keying the entries already there — use `PUT` to switch `attr` deliberately.
750
+
751
+ **Use case:** Let one more customer into a private beta without reading the current list first.
752
+ tags:
753
+ - Flags
754
+ parameters:
755
+ - $ref: ../components/parameters.yaml#/ProjectId
756
+ - name: id
757
+ in: path
758
+ required: true
759
+ description: Stable opaque gate id (`gate_…`) or the gate's `name`.
760
+ schema:
761
+ $ref: ../components/schemas/common.yaml#/ResourceId
762
+ requestBody:
763
+ required: true
764
+ content:
765
+ application/json:
766
+ schema:
767
+ $ref: ../components/schemas/gates.yaml#/AddToGateWhitelistRequest
768
+ examples:
769
+ addOne:
770
+ summary: Admit one more email
771
+ value:
772
+ entries:
773
+ - carol@acme.dev
774
+ createOnUserIds:
775
+ summary: Start a whitelist keyed on user ids
776
+ description: "`attr` is honoured only because this gate has no whitelist yet."
777
+ value:
778
+ attr: user_id
779
+ entries:
780
+ - usr_01j7w8a1b2c3d4e5f6g7h8i9j0
781
+ responses:
782
+ "200":
783
+ description: Add entries to a gate's whitelist
784
+ content:
785
+ application/json:
786
+ schema:
787
+ $ref: ../components/schemas/gates.yaml#/AddToGateWhitelistResponse
788
+ example:
789
+ id: gate_01j7w8a1b2c3d4e5f6g7h8i9j0
790
+ name: new_checkout
791
+ attr: email
792
+ entries:
793
+ - alice@acme.dev
794
+ - bob@acme.dev
795
+ - carol@acme.dev
796
+ "400":
797
+ $ref: ../components/responses.yaml#/BadRequest
798
+ "401":
799
+ $ref: ../components/responses.yaml#/Unauthorized
800
+ "403":
801
+ $ref: ../components/responses.yaml#/Forbidden
802
+ "404":
803
+ $ref: ../components/responses.yaml#/NotFound
804
+ "409":
805
+ $ref: ../components/responses.yaml#/Conflict
806
+ "422":
807
+ $ref: ../components/responses.yaml#/UnprocessableEntity
808
+ x-error-codes:
809
+ - BAD_REQUEST
810
+ - UNAUTHORIZED
811
+ - FORBIDDEN
812
+ - NOT_FOUND
813
+ - IMMUTABLE_FIELD
814
+ - VALIDATION
815
+ x-cli:
816
+ name: whitelist-add
817
+ positional:
818
+ - id
819
+ delete:
820
+ operationId: removeFromGateWhitelist
821
+ summary: Remove entries from a gate's whitelist
822
+ description: |-
823
+ Removes identities from the gate's whitelist. Entries that aren't on the list are skipped, so the call is idempotent.
824
+
825
+ Removing the last entry leaves an empty whitelist block in place; to drop the block itself use `PUT` with `entries: []`.
826
+
827
+ **Use case:** Revoke one beta tester's access without touching anyone else's.
828
+ tags:
829
+ - Flags
830
+ parameters:
831
+ - $ref: ../components/parameters.yaml#/ProjectId
832
+ - name: id
833
+ in: path
834
+ required: true
835
+ description: Stable opaque gate id (`gate_…`) or the gate's `name`.
836
+ schema:
837
+ $ref: ../components/schemas/common.yaml#/ResourceId
838
+ requestBody:
839
+ required: true
840
+ content:
841
+ application/json:
842
+ schema:
843
+ $ref: ../components/schemas/gates.yaml#/RemoveFromGateWhitelistRequest
844
+ examples:
845
+ removeOne:
846
+ summary: Revoke one email
847
+ value:
848
+ entries:
849
+ - carol@acme.dev
850
+ responses:
851
+ "200":
852
+ description: Remove entries from a gate's whitelist
853
+ content:
854
+ application/json:
855
+ schema:
856
+ $ref: ../components/schemas/gates.yaml#/RemoveFromGateWhitelistResponse
857
+ example:
858
+ id: gate_01j7w8a1b2c3d4e5f6g7h8i9j0
859
+ name: new_checkout
860
+ attr: email
861
+ entries:
862
+ - alice@acme.dev
863
+ - bob@acme.dev
864
+ "400":
865
+ $ref: ../components/responses.yaml#/BadRequest
866
+ "401":
867
+ $ref: ../components/responses.yaml#/Unauthorized
868
+ "403":
869
+ $ref: ../components/responses.yaml#/Forbidden
870
+ "404":
871
+ $ref: ../components/responses.yaml#/NotFound
872
+ "409":
873
+ $ref: ../components/responses.yaml#/Conflict
874
+ "422":
875
+ $ref: ../components/responses.yaml#/UnprocessableEntity
876
+ x-error-codes:
877
+ - BAD_REQUEST
878
+ - UNAUTHORIZED
879
+ - FORBIDDEN
880
+ - NOT_FOUND
881
+ - VALIDATION
882
+ x-cli:
883
+ name: whitelist-remove
884
+ positional:
885
+ - id
@@ -128,11 +128,12 @@
128
128
  - name: limit
129
129
  in: query
130
130
  required: false
131
- description: Max keys to return (1–500).
131
+ description: Max keys to return (1–500). Defaults to 200.
132
132
  schema:
133
133
  type: integer
134
134
  minimum: 1
135
135
  maximum: 500
136
+ default: 200
136
137
  - name: offset
137
138
  in: query
138
139
  required: false