@shipeasy/openapi 2.3.0 → 2.5.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 (46) hide show
  1. package/dist/{chunk-PRN4AEAM.js → chunk-DBTSD3W5.js} +1085 -261
  2. package/dist/chunk-DBTSD3W5.js.map +1 -0
  3. package/dist/{chunk-LYNWK55D.js → chunk-RDMD5XQT.js} +171 -1
  4. package/dist/chunk-RDMD5XQT.js.map +1 -0
  5. package/dist/{client-DO5HTb_L.d.ts → client-ObcuFHaV.d.ts} +5605 -2734
  6. package/dist/client.d.ts +1 -1
  7. package/dist/client.js +47 -1
  8. package/dist/custom.d.ts +9 -4
  9. package/dist/custom.js +57 -9
  10. package/dist/custom.js.map +1 -1
  11. package/dist/index.d.ts +2 -2
  12. package/dist/index.js +48 -2
  13. package/dist/index.js.map +1 -1
  14. package/dist/schemas.d.ts +3171 -485
  15. package/dist/schemas.js +229 -1
  16. package/openapi.json +13264 -8071
  17. package/openapi.yaml +5108 -1550
  18. package/package.json +1 -1
  19. package/spec/components/errors.yaml +3 -0
  20. package/spec/components/schemas/alerts.yaml +138 -0
  21. package/spec/components/schemas/attributes.yaml +53 -3
  22. package/spec/components/schemas/common.yaml +10 -0
  23. package/spec/components/schemas/configs.yaml +91 -3
  24. package/spec/components/schemas/connectors.yaml +5 -1
  25. package/spec/components/schemas/errors-api.yaml +51 -4
  26. package/spec/components/schemas/experiments.yaml +347 -87
  27. package/spec/components/schemas/gates.yaml +350 -277
  28. package/spec/components/schemas/i18n.yaml +169 -40
  29. package/spec/components/schemas/killswitches.yaml +36 -0
  30. package/spec/components/schemas/metrics.yaml +199 -7
  31. package/spec/components/schemas/ops.yaml +961 -29
  32. package/spec/components/schemas/projects.yaml +129 -3
  33. package/spec/openapi.yaml +58 -0
  34. package/spec/paths/alerts.yaml +184 -0
  35. package/spec/paths/attributes.yaml +23 -5
  36. package/spec/paths/configs.yaml +98 -21
  37. package/spec/paths/errors.yaml +16 -0
  38. package/spec/paths/experiments.yaml +236 -16
  39. package/spec/paths/gates.yaml +137 -0
  40. package/spec/paths/i18n.yaml +290 -6
  41. package/spec/paths/killswitches.yaml +6 -0
  42. package/spec/paths/metrics.yaml +186 -0
  43. package/spec/paths/ops.yaml +382 -0
  44. package/spec/paths/projects.yaml +60 -0
  45. package/dist/chunk-LYNWK55D.js.map +0 -1
  46. package/dist/chunk-PRN4AEAM.js.map +0 -1
@@ -25,6 +25,9 @@
25
25
  - id: cfg_01j7wae5h6j7k8l9m0n1p2q3r4
26
26
  name: pricing.tiers
27
27
  description: Pricing tier definitions consumed by the checkout flow.
28
+ folder: pricing
29
+ creatorEmail: ana@example.com
30
+ createdAt: 2026-05-01T10:12:00.000Z
28
31
  schema:
29
32
  type: object
30
33
  properties:
@@ -84,7 +87,7 @@
84
87
 
85
88
  - **Minimal create** — `name` + `schema`. Initial value defaults to `{}`.
86
89
  - **Seeded create** — supply a flat `value` to publish the same object on every env.
87
- - **Per-env seed** — supply a `{ env: value }` map for different per-env starting values.
90
+ - **Per-env seed** — supply a `{ env: value }` map under `value`, or pass the env keys `dev`/`staging`/`prod` directly (each overrides `value` for that env and is published at version 1).
88
91
  tags:
89
92
  - Configs
90
93
  parameters:
@@ -126,8 +129,8 @@
126
129
  - name: free
127
130
  - name: pro
128
131
  perEnvSeed:
129
- summary: Per-env seed
130
- description: "Pass a `{ env: value }` map to seed different initial values per env."
132
+ summary: Per-env seed (env keys)
133
+ description: "Pass the env keys directly to seed different initial values per env. Each overrides `value` for that env and is published at version 1."
131
134
  value:
132
135
  name: pricing.tiers
133
136
  schema:
@@ -137,20 +140,15 @@
137
140
  type: array
138
141
  required:
139
142
  - tiers
140
- value:
141
- dev:
142
- tiers:
143
- - name: free
144
- - name: pro
145
- - name: enterprise
146
- stage:
147
- tiers:
148
- - name: free
149
- - name: pro
150
- prod:
151
- tiers:
152
- - name: free
153
- - name: pro
143
+ dev:
144
+ tiers:
145
+ - name: free
146
+ - name: pro
147
+ - name: enterprise
148
+ prod:
149
+ tiers:
150
+ - name: free
151
+ - name: pro
154
152
  responses:
155
153
  "201":
156
154
  description: Create a dynamic config
@@ -226,6 +224,7 @@
226
224
  id: cfg_01j7wae5h6j7k8l9m0n1p2q3r4
227
225
  name: pricing.tiers
228
226
  description: Pricing tier definitions consumed by the checkout flow.
227
+ folder: pricing
229
228
  schema:
230
229
  type: object
231
230
  properties:
@@ -298,15 +297,13 @@
298
297
  operationId: updateConfig
299
298
  summary: Update a dynamic config
300
299
  description: |-
301
- Partial update. When `value` is supplied it is **republished on every env** (new version per env). When `schema` is supplied it replaces the current schema; every existing value is re-validated.
302
-
303
- For env-scoped edits, use the draft/publish flow (`PUT /{id}/drafts` then `POST /{id}/publish`) instead.
300
+ Partial update. When `value` is supplied it is **republished on every env** (new version per env). A per-env key (`dev`/`staging`/`prod`) publishes a new version to **only that env**, immediately, overriding `value` for it. When `schema` is supplied it replaces the current schema; every existing value is re-validated.
304
301
 
305
302
  **Use cases**
306
303
 
307
304
  - **Republish flat value** — `{ "value": {…} }` sets the same value on every env.
305
+ - **Publish one env** — `{ "prod": {…} }` publishes a new version to prod only, instantly.
308
306
  - **Schema migration** — `{ "schema": {…} }` replaces the schema; existing values are re-validated.
309
- - **Env-scoped edits** — use `PUT /{id}/drafts` + `POST /{id}/publish` instead of PATCH.
310
307
  tags:
311
308
  - Configs
312
309
  parameters:
@@ -332,6 +329,15 @@
332
329
  tiers:
333
330
  - name: free
334
331
  - name: pro
332
+ prodOnly:
333
+ summary: Per-env — publish prod only
334
+ description: Publishes a new version to prod only, immediately. dev/staging untouched.
335
+ value:
336
+ prod:
337
+ tiers:
338
+ - name: free
339
+ - name: pro
340
+ - name: enterprise
335
341
  schemaOnly:
336
342
  summary: Schema-only update
337
343
  description: Replaces the schema. Existing values must still validate or the update fails.
@@ -492,6 +498,7 @@
492
498
  - VALIDATION
493
499
  x-cli:
494
500
  name: draft
501
+ hidden: true
495
502
  positional:
496
503
  - id
497
504
  delete:
@@ -551,6 +558,7 @@
551
558
  - NOT_FOUND
552
559
  x-cli:
553
560
  name: discard-draft
561
+ hidden: true
554
562
  positional:
555
563
  - id
556
564
  /api/admin/configs/{id}/publish:
@@ -622,6 +630,7 @@
622
630
  - VALIDATION
623
631
  x-cli:
624
632
  name: publish
633
+ hidden: true
625
634
  positional:
626
635
  - id
627
636
  /api/admin/configs/{id}/activity:
@@ -686,6 +695,74 @@
686
695
  - NOT_FOUND
687
696
  x-cli:
688
697
  name: activity
698
+ hidden: true
699
+ positional:
700
+ - id
701
+ /api/admin/configs/{id}/versions:
702
+ get:
703
+ operationId: listConfigVersions
704
+ summary: List config version history
705
+ description: |-
706
+ Returns every published version of the config's value on one env, newest first. The `env` query parameter picks the environment (`dev`, `staging`, or `prod`) and defaults to `prod`; an unknown env returns `400`. The config's JSON Schema is config-level and not versioned — this is value history only.
707
+
708
+ **Use case:** Render the History timeline in the config detail pane (value diff + restore), or audit which value was live on prod at a given version.
709
+ tags:
710
+ - Configs
711
+ parameters:
712
+ - $ref: ../components/parameters.yaml#/ProjectId
713
+ - name: id
714
+ in: path
715
+ required: true
716
+ description: Stable opaque config id (`cfg_…`) or the config's `name`.
717
+ schema:
718
+ $ref: ../components/schemas/common.yaml#/ResourceId
719
+ - name: env
720
+ in: query
721
+ required: false
722
+ description: Environment to list history for (`dev`, `staging`, or `prod`). Defaults to `prod` when omitted.
723
+ schema:
724
+ $ref: ../components/schemas/common.yaml#/Env
725
+ responses:
726
+ "200":
727
+ description: List config version history
728
+ content:
729
+ application/json:
730
+ schema:
731
+ $ref: ../components/schemas/configs.yaml#/ListConfigVersionsResponse
732
+ example:
733
+ - version: 5
734
+ value:
735
+ tiers:
736
+ - name: free
737
+ - name: pro
738
+ publishedAt: 2026-05-09T18:22:11.000Z
739
+ publishedBy: ana@example.com
740
+ - version: 4
741
+ value:
742
+ tiers:
743
+ - name: free
744
+ publishedAt: 2026-05-08T11:05:22.000Z
745
+ publishedBy: bo@example.com
746
+ "400":
747
+ $ref: ../components/responses.yaml#/BadRequest
748
+ "401":
749
+ $ref: ../components/responses.yaml#/Unauthorized
750
+ "403":
751
+ $ref: ../components/responses.yaml#/Forbidden
752
+ "404":
753
+ $ref: ../components/responses.yaml#/NotFound
754
+ "409":
755
+ $ref: ../components/responses.yaml#/Conflict
756
+ "422":
757
+ $ref: ../components/responses.yaml#/UnprocessableEntity
758
+ x-error-codes:
759
+ - BAD_REQUEST
760
+ - UNAUTHORIZED
761
+ - FORBIDDEN
762
+ - NOT_FOUND
763
+ x-cli:
764
+ name: versions
765
+ hidden: true
689
766
  positional:
690
767
  - id
691
768
  /api/admin/configs/{id}/schema:
@@ -73,6 +73,9 @@
73
73
  lastSeenAt: 2026-06-28T14:55:31.000Z
74
74
  createdAt: 2026-06-20T08:11:02.000Z
75
75
  updatedAt: 2026-06-28T14:55:31.000Z
76
+ assigneeId: null
77
+ assigneeConnectorId: null
78
+ assigneeAgent: null
76
79
  "400":
77
80
  $ref: ../components/responses.yaml#/BadRequest
78
81
  "401":
@@ -142,8 +145,13 @@
142
145
  lastSeenAt: 2026-06-28T14:55:31.000Z
143
146
  createdAt: 2026-06-20T08:11:02.000Z
144
147
  updatedAt: 2026-06-28T14:55:31.000Z
148
+ assigneeId: 6f1d2c3b-4a5e-4f60-9b7c-8d9e0f1a2b3c
149
+ assigneeConnectorId: null
150
+ assigneeAgent: null
145
151
  occurrences:
146
152
  - id: occ_01j7w7qq0v3k9d2m5rc8t1xh4n
153
+ projectId: e976b15e-2f0d-4c6e-9b1a-3a7c1f2d8e90
154
+ errorId: err_01j7w7m9q4hxbf6npe6s9zr3vc
147
155
  message: Cannot read properties of undefined (reading 'id')
148
156
  stack: |-
149
157
  TypeError: Cannot read properties of undefined (reading 'id')
@@ -156,6 +164,8 @@
156
164
  sampleRate: 100
157
165
  seenAt: 2026-06-28T14:55:31.000Z
158
166
  - id: occ_01j7w7pk8s6e3f9b0zad7y5w2q
167
+ projectId: e976b15e-2f0d-4c6e-9b1a-3a7c1f2d8e90
168
+ errorId: err_01j7w7m9q4hxbf6npe6s9zr3vc
159
169
  message: Cannot read properties of undefined (reading 'id')
160
170
  stack: |-
161
171
  TypeError: Cannot read properties of undefined (reading 'id')
@@ -257,6 +267,9 @@
257
267
  lastSeenAt: 2026-06-28T14:55:31.000Z
258
268
  createdAt: 2026-06-20T08:11:02.000Z
259
269
  updatedAt: 2026-06-28T15:02:10.000Z
270
+ assigneeId: null
271
+ assigneeConnectorId: null
272
+ assigneeAgent: null
260
273
  "400":
261
274
  $ref: ../components/responses.yaml#/BadRequest
262
275
  "401":
@@ -377,6 +390,9 @@
377
390
  lastSeenAt: 2026-06-28T14:55:31.000Z
378
391
  createdAt: 2026-06-20T08:11:02.000Z
379
392
  updatedAt: 2026-06-28T15:02:10.000Z
393
+ assigneeId: null
394
+ assigneeConnectorId: null
395
+ assigneeAgent: null
380
396
  "400":
381
397
  $ref: ../components/responses.yaml#/BadRequest
382
398
  "401":
@@ -648,21 +648,51 @@
648
648
  status: running
649
649
  results:
650
650
  - metric: checkout_completed
651
- group_name: control
651
+ groupName: control
652
652
  ds: 2026-05-09
653
653
  n: 12421
654
654
  mean: 0.1834
655
- delta_pct: null
656
- p_value: null
657
- srm_detected: 0
655
+ variance: 0.1498
656
+ delta: null
657
+ deltaPct: null
658
+ ci95Low: null
659
+ ci95High: null
660
+ ci99Low: null
661
+ ci99High: null
662
+ ciLow: null
663
+ ciHigh: null
664
+ pValue: null
665
+ expectedN: 15000
666
+ realizedMde: null
667
+ srmPValue: 0.82
668
+ srmDetected: 0
669
+ msprtSignificant: null
670
+ msprtLambda: null
671
+ isFinal: 0
672
+ peekWarning: 0
658
673
  - metric: checkout_completed
659
- group_name: treatment
674
+ groupName: treatment
660
675
  ds: 2026-05-09
661
676
  n: 12519
662
677
  mean: 0.1922
663
- delta_pct: 4.8
664
- p_value: 0.018
665
- srm_detected: 0
678
+ variance: 0.1553
679
+ delta: 0.0088
680
+ deltaPct: 4.8
681
+ ci95Low: 0.0015
682
+ ci95High: 0.0161
683
+ ci99Low: -0.0008
684
+ ci99High: 0.0184
685
+ ciLow: 0.0015
686
+ ciHigh: 0.0161
687
+ pValue: 0.018
688
+ expectedN: 15000
689
+ realizedMde: 0.0079
690
+ srmPValue: 0.82
691
+ srmDetected: 0
692
+ msprtSignificant: null
693
+ msprtLambda: null
694
+ isFinal: 0
695
+ peekWarning: 0
666
696
  verdict: ship
667
697
  "400":
668
698
  $ref: ../components/responses.yaml#/BadRequest
@@ -723,21 +753,51 @@
723
753
  status: running
724
754
  series:
725
755
  - metric: checkout_completed
726
- group_name: treatment
756
+ groupName: treatment
727
757
  ds: 2026-05-08
728
758
  n: 11200
729
759
  mean: 0.1903
730
- delta_pct: 3.9
731
- p_value: 0.034
732
- srm_detected: 0
760
+ variance: 0.1541
761
+ delta: 0.0071
762
+ deltaPct: 3.9
763
+ ci95Low: -0.0004
764
+ ci95High: 0.0146
765
+ ci99Low: -0.0028
766
+ ci99High: 0.017
767
+ ciLow: -0.0004
768
+ ciHigh: 0.0146
769
+ pValue: 0.034
770
+ expectedN: 15000
771
+ realizedMde: 0.0083
772
+ srmPValue: 0.79
773
+ srmDetected: 0
774
+ msprtSignificant: null
775
+ msprtLambda: null
776
+ isFinal: 0
777
+ peekWarning: 0
733
778
  - metric: checkout_completed
734
- group_name: treatment
779
+ groupName: treatment
735
780
  ds: 2026-05-09
736
781
  n: 12519
737
782
  mean: 0.1922
738
- delta_pct: 4.8
739
- p_value: 0.018
740
- srm_detected: 0
783
+ variance: 0.1553
784
+ delta: 0.0088
785
+ deltaPct: 4.8
786
+ ci95Low: 0.0015
787
+ ci95High: 0.0161
788
+ ci99Low: -0.0008
789
+ ci99High: 0.0184
790
+ ciLow: 0.0015
791
+ ciHigh: 0.0161
792
+ pValue: 0.018
793
+ expectedN: 15000
794
+ realizedMde: 0.0079
795
+ srmPValue: 0.82
796
+ srmDetected: 0
797
+ msprtSignificant: null
798
+ msprtLambda: null
799
+ isFinal: 0
800
+ peekWarning: 0
741
801
  "400":
742
802
  $ref: ../components/responses.yaml#/BadRequest
743
803
  "401":
@@ -759,6 +819,166 @@
759
819
  name: timeseries
760
820
  positional:
761
821
  - id
822
+ /api/admin/experiments/{id}/readouts:
823
+ post:
824
+ operationId: createExperimentReadout
825
+ summary: Mint a readout snapshot
826
+ description: |-
827
+ Freezes the current results view into an immutable, dated readout snapshot — verdict, headline, per-metric numbers, the caveat list with its acknowledgment state, enrollment, and a hash of the assignment-relevant config. Ship/stop flows mint one automatically; "Share readout" mints one on demand. Snapshots are never updated after insert.
828
+
829
+ Pass `requireAllAcknowledged: true` to enforce ship gating server-side — returns `422` while any open caveat is not listed in `acknowledgedCaveatIds`.
830
+
831
+ **Use case:** Capture "what the data said when we decided" before shipping or stopping, so the decision stays auditable even after results move.
832
+ tags:
833
+ - Experiments
834
+ parameters:
835
+ - $ref: ../components/parameters.yaml#/ProjectId
836
+ - name: id
837
+ in: path
838
+ required: true
839
+ description: Stable opaque experiment id (`exp_…`) or the experiment's `name`.
840
+ schema:
841
+ $ref: ../components/schemas/common.yaml#/ResourceId
842
+ requestBody:
843
+ required: true
844
+ content:
845
+ application/json:
846
+ schema:
847
+ $ref: ../components/schemas/experiments.yaml#/CreateExperimentReadoutRequest
848
+ examples:
849
+ manual:
850
+ summary: Manual — "Share readout"
851
+ description: Snapshot the current results view on demand.
852
+ value:
853
+ kind: manual
854
+ shipGated:
855
+ summary: Ship — with caveat gating
856
+ description: Mint a ship readout, acknowledging the open caveats; fails with `422` if any open caveat is missing from the list.
857
+ value:
858
+ kind: ship
859
+ acknowledgedCaveatIds:
860
+ - srm
861
+ - min_runtime
862
+ requireAllAcknowledged: true
863
+ responses:
864
+ "200":
865
+ description: Mint a readout snapshot
866
+ content:
867
+ application/json:
868
+ schema:
869
+ $ref: ../components/schemas/experiments.yaml#/CreateExperimentReadoutResponse
870
+ example:
871
+ id: 5f0c1f2d-8e90-4c6e-9b1a-3a7c1f2d8e90
872
+ links:
873
+ GetExperimentReadout:
874
+ operationId: getExperimentReadout
875
+ parameters: { readoutId: $response.body#/id }
876
+ "400":
877
+ $ref: ../components/responses.yaml#/BadRequest
878
+ "401":
879
+ $ref: ../components/responses.yaml#/Unauthorized
880
+ "403":
881
+ $ref: ../components/responses.yaml#/Forbidden
882
+ "404":
883
+ $ref: ../components/responses.yaml#/NotFound
884
+ "409":
885
+ $ref: ../components/responses.yaml#/Conflict
886
+ "422":
887
+ $ref: ../components/responses.yaml#/UnprocessableEntity
888
+ x-error-codes:
889
+ - BAD_REQUEST
890
+ - UNAUTHORIZED
891
+ - FORBIDDEN
892
+ - NOT_FOUND
893
+ - VALIDATION
894
+ x-cli:
895
+ name: readout-create
896
+ positional:
897
+ - id
898
+ /api/admin/experiments/{id}/readouts/{readoutId}:
899
+ get:
900
+ operationId: getExperimentReadout
901
+ summary: Get a readout snapshot
902
+ description: |-
903
+ Returns one immutable readout snapshot — the frozen results view (verdict, headline, per-metric numbers, caveats, enrollment) captured when it was minted, plus the `configHash` that tells you whether it is still comparable to the live view.
904
+
905
+ **Use case:** Render a shared, dated readout exactly as it looked at decision time.
906
+ tags:
907
+ - Experiments
908
+ parameters:
909
+ - $ref: ../components/parameters.yaml#/ProjectId
910
+ - name: id
911
+ in: path
912
+ required: true
913
+ description: Stable opaque experiment id (`exp_…`) or the experiment's `name`.
914
+ schema:
915
+ $ref: ../components/schemas/common.yaml#/ResourceId
916
+ - name: readoutId
917
+ in: path
918
+ required: true
919
+ description: Readout snapshot id, as returned by `createExperimentReadout`.
920
+ schema:
921
+ $ref: ../components/schemas/common.yaml#/ResourceId
922
+ responses:
923
+ "200":
924
+ description: Get a readout snapshot
925
+ content:
926
+ application/json:
927
+ schema:
928
+ $ref: ../components/schemas/experiments.yaml#/GetExperimentReadoutResponse
929
+ example:
930
+ id: 5f0c1f2d-8e90-4c6e-9b1a-3a7c1f2d8e90
931
+ experimentId: exp_01j7wb12c3d4e5f6g7h8j9k0l1
932
+ experimentName: checkout_button_color
933
+ kind: ship
934
+ createdBy: pm@example.com
935
+ createdAt: 2026-05-09T18:22:11.000Z
936
+ verdict: ship
937
+ title: Treatment wins on checkout conversion
938
+ why: Goal metric significant with guardrails flat after 8 days.
939
+ configHash: 9c1f4f1f2c0c4a5fa1c2b6d3e7c8e3a19c1f4f1f2c0c4a5fa1c2b6d3e7c8e3a1
940
+ metricsJson:
941
+ - name: checkout_completed
942
+ role: goal
943
+ delta: 0.0088
944
+ deltaPct: 4.8
945
+ ci: [0.0015, 0.0161]
946
+ p: 0.018
947
+ sig: true
948
+ caveatsJson:
949
+ - id: min_runtime
950
+ severity: warn
951
+ text: Below the 14-day minimum runtime.
952
+ acknowledged: true
953
+ usersJson:
954
+ groups:
955
+ - name: control
956
+ n: 12421
957
+ - name: treatment
958
+ n: 12519
959
+ days: 8
960
+ "400":
961
+ $ref: ../components/responses.yaml#/BadRequest
962
+ "401":
963
+ $ref: ../components/responses.yaml#/Unauthorized
964
+ "403":
965
+ $ref: ../components/responses.yaml#/Forbidden
966
+ "404":
967
+ $ref: ../components/responses.yaml#/NotFound
968
+ "409":
969
+ $ref: ../components/responses.yaml#/Conflict
970
+ "422":
971
+ $ref: ../components/responses.yaml#/UnprocessableEntity
972
+ x-error-codes:
973
+ - BAD_REQUEST
974
+ - UNAUTHORIZED
975
+ - FORBIDDEN
976
+ - NOT_FOUND
977
+ x-cli:
978
+ name: readout-get
979
+ positional:
980
+ - id
981
+ - readoutId
762
982
  /api/admin/experiments/{id}/reanalyze:
763
983
  post:
764
984
  operationId: reanalyzeExperiment
@@ -43,6 +43,10 @@
43
43
  groupName: growth
44
44
  ownerEmail: ana@example.com
45
45
  stack: null
46
+ creatorEmail: ana@example.com
47
+ updaterEmail: bo@example.com
48
+ version: 4
49
+ createdAt: 2026-05-01T10:12:00.000Z
46
50
  updatedAt: 2026-05-09T16:01:22.000Z
47
51
  next_cursor: null
48
52
  "400":
@@ -201,6 +205,76 @@
201
205
  positional:
202
206
  - name
203
207
  /api/admin/gates/{id}:
208
+ get:
209
+ operationId: getGate
210
+ summary: Get one gate
211
+ description: |-
212
+ Returns the full gate row — including the gatekeeper `stack`, resolved creator/last-editor emails, and the edit `version` — for one gate, addressed by id or `name`.
213
+
214
+ **Use case:** Inspect a single gate's current rollout, rules, and stack before editing it, without paging through the whole list.
215
+ tags:
216
+ - Flags
217
+ parameters:
218
+ - $ref: ../components/parameters.yaml#/ProjectId
219
+ - name: id
220
+ in: path
221
+ required: true
222
+ description: Stable opaque gate id (`gat_…`) or the gate's `name`.
223
+ schema:
224
+ $ref: ../components/schemas/common.yaml#/ResourceId
225
+ responses:
226
+ "200":
227
+ description: Get one gate
228
+ content:
229
+ application/json:
230
+ schema:
231
+ $ref: ../components/schemas/gates.yaml#/GetGateResponse
232
+ example:
233
+ id: gat_01j7w7m9q4hxbf6npe6s9zr3vc
234
+ name: checkout_v2
235
+ enabled: 1
236
+ type: targeting
237
+ rolloutPct: 5000
238
+ rules:
239
+ - attr: country
240
+ op: in
241
+ value:
242
+ - US
243
+ - CA
244
+ - GB
245
+ salt: 9c1f4f1f2c0c4a5fa1c2b6d3e7c8e3a1
246
+ title: Checkout v2
247
+ description: New checkout flow. Pro users in US/CA/GB only.
248
+ folder: checkout
249
+ groupName: growth
250
+ ownerEmail: ana@example.com
251
+ stack: null
252
+ creatorEmail: ana@example.com
253
+ updaterEmail: bo@example.com
254
+ version: 4
255
+ createdAt: 2026-05-01T10:12:00.000Z
256
+ updatedAt: 2026-05-09T16:01:22.000Z
257
+ "400":
258
+ $ref: ../components/responses.yaml#/BadRequest
259
+ "401":
260
+ $ref: ../components/responses.yaml#/Unauthorized
261
+ "403":
262
+ $ref: ../components/responses.yaml#/Forbidden
263
+ "404":
264
+ $ref: ../components/responses.yaml#/NotFound
265
+ "409":
266
+ $ref: ../components/responses.yaml#/Conflict
267
+ "422":
268
+ $ref: ../components/responses.yaml#/UnprocessableEntity
269
+ x-error-codes:
270
+ - BAD_REQUEST
271
+ - UNAUTHORIZED
272
+ - FORBIDDEN
273
+ - NOT_FOUND
274
+ x-cli:
275
+ name: get
276
+ positional:
277
+ - id
204
278
  patch:
205
279
  operationId: updateGate
206
280
  summary: Update a feature gate
@@ -464,3 +538,66 @@
464
538
  name: disable
465
539
  positional:
466
540
  - id
541
+ /api/admin/gates/{id}/activity:
542
+ get:
543
+ operationId: listGateActivity
544
+ summary: List gate activity
545
+ description: |-
546
+ Returns recent audit rows for one gate (create, update, enable, disable, delete) ordered newest first. Use the `limit` query parameter to cap the result (1–100, default 20).
547
+
548
+ **Use case:** Render the activity feed in the gate detail panel or answer "who ramped this gate, and when?" during an incident review.
549
+ tags:
550
+ - Flags
551
+ parameters:
552
+ - $ref: ../components/parameters.yaml#/ProjectId
553
+ - name: id
554
+ in: path
555
+ required: true
556
+ description: Stable opaque gate id (`gat_…`) or the gate's `name`.
557
+ schema:
558
+ $ref: ../components/schemas/common.yaml#/ResourceId
559
+ - name: limit
560
+ in: query
561
+ required: false
562
+ description: Max rows to return (1–100). Defaults to 20.
563
+ schema:
564
+ default: 20
565
+ type: integer
566
+ minimum: 1
567
+ maximum: 100
568
+ responses:
569
+ "200":
570
+ description: List gate activity
571
+ content:
572
+ application/json:
573
+ schema:
574
+ $ref: ../components/schemas/gates.yaml#/ListGateActivityResponse
575
+ example:
576
+ - id: act_01j7waf01a2b3c4d5e6f7g8h9i
577
+ action: gate.update
578
+ actorEmail: ana@example.com
579
+ actorType: user
580
+ payload:
581
+ rollout_pct: 5000
582
+ createdAt: 2026-05-09T16:01:22.000Z
583
+ "400":
584
+ $ref: ../components/responses.yaml#/BadRequest
585
+ "401":
586
+ $ref: ../components/responses.yaml#/Unauthorized
587
+ "403":
588
+ $ref: ../components/responses.yaml#/Forbidden
589
+ "404":
590
+ $ref: ../components/responses.yaml#/NotFound
591
+ "409":
592
+ $ref: ../components/responses.yaml#/Conflict
593
+ "422":
594
+ $ref: ../components/responses.yaml#/UnprocessableEntity
595
+ x-error-codes:
596
+ - BAD_REQUEST
597
+ - UNAUTHORIZED
598
+ - FORBIDDEN
599
+ - NOT_FOUND
600
+ x-cli:
601
+ name: activity
602
+ positional:
603
+ - id