diff --git a/codersdk/aigatewaykeys.go b/codersdk/aigatewaykeys.go
index 7c4eb1c7a1..cce57dafad 100644
--- a/codersdk/aigatewaykeys.go
+++ b/codersdk/aigatewaykeys.go
@@ -14,11 +14,11 @@ import (
// AIGatewayKey is a shared secret used by a standalone AI Gateway
// to authenticate into coderd.
type AIGatewayKey struct {
- ID uuid.UUID `json:"id" format:"uuid"`
- Name string `json:"name"`
- KeyPrefix string `json:"key_prefix"`
- CreatedAt time.Time `json:"created_at" format:"date-time"`
- LastUsedAt *time.Time `json:"last_used_at,omitempty" format:"date-time"`
+ ID uuid.UUID `json:"id" table:"id" format:"uuid"`
+ Name string `json:"name" table:"name,default_sort"`
+ KeyPrefix string `json:"key_prefix" table:"key prefix"`
+ CreatedAt time.Time `json:"created_at" table:"created at" format:"date-time"`
+ LastUsedAt *time.Time `json:"last_used_at,omitempty" table:"last used at" format:"date-time"`
}
// CreateAIGatewayKeyRequest requests a new AI Gateway key.
diff --git a/docs/reference/cli/ai-gateway.md b/docs/reference/cli/ai-gateway.md
new file mode 100644
index 0000000000..7666d36282
--- /dev/null
+++ b/docs/reference/cli/ai-gateway.md
@@ -0,0 +1,16 @@
+
+# ai-gateway
+
+Manage AI Gateway
+
+## Usage
+
+```console
+coder ai-gateway
+```
+
+## Subcommands
+
+| Name | Purpose |
+|-------------------------------------------|------------------------|
+| [keys](./ai-gateway_keys.md) | Manage AI Gateway keys |
diff --git a/docs/reference/cli/ai-gateway_keys.md b/docs/reference/cli/ai-gateway_keys.md
new file mode 100644
index 0000000000..c2e258ac6d
--- /dev/null
+++ b/docs/reference/cli/ai-gateway_keys.md
@@ -0,0 +1,18 @@
+
+# ai-gateway keys
+
+Manage AI Gateway keys
+
+## Usage
+
+```console
+coder ai-gateway keys
+```
+
+## Subcommands
+
+| Name | Purpose |
+|----------------------------------------------------|--------------------------|
+| [create](./ai-gateway_keys_create.md) | Create an AI Gateway key |
+| [delete](./ai-gateway_keys_delete.md) | Delete an AI Gateway key |
+| [list](./ai-gateway_keys_list.md) | List AI Gateway keys |
diff --git a/docs/reference/cli/ai-gateway_keys_create.md b/docs/reference/cli/ai-gateway_keys_create.md
new file mode 100644
index 0000000000..7f6b967684
--- /dev/null
+++ b/docs/reference/cli/ai-gateway_keys_create.md
@@ -0,0 +1,10 @@
+
+# ai-gateway keys create
+
+Create an AI Gateway key
+
+## Usage
+
+```console
+coder ai-gateway keys create
+```
diff --git a/docs/reference/cli/ai-gateway_keys_delete.md b/docs/reference/cli/ai-gateway_keys_delete.md
new file mode 100644
index 0000000000..eee21bc0a2
--- /dev/null
+++ b/docs/reference/cli/ai-gateway_keys_delete.md
@@ -0,0 +1,24 @@
+
+# ai-gateway keys delete
+
+Delete an AI Gateway key
+
+Aliases:
+
+* rm
+
+## Usage
+
+```console
+coder ai-gateway keys delete [flags]
+```
+
+## Options
+
+### -y, --yes
+
+| | |
+|------|-------------------|
+| Type | bool |
+
+Bypass confirmation prompts.
diff --git a/docs/reference/cli/ai-gateway_keys_list.md b/docs/reference/cli/ai-gateway_keys_list.md
new file mode 100644
index 0000000000..39f1ffcebe
--- /dev/null
+++ b/docs/reference/cli/ai-gateway_keys_list.md
@@ -0,0 +1,34 @@
+
+# ai-gateway keys list
+
+List AI Gateway keys
+
+Aliases:
+
+* ls
+
+## Usage
+
+```console
+coder ai-gateway keys list [flags]
+```
+
+## Options
+
+### -c, --column
+
+| | |
+|---------|---------------------------------------------------------------|
+| Type | [id\|name\|key prefix\|created at\|last used at] |
+| Default | id,name,key prefix,last used at,created at |
+
+Columns to display in table output.
+
+### -o, --output
+
+| | |
+|---------|--------------------------|
+| Type | table\|json |
+| Default | table |
+
+Output format.
diff --git a/docs/reference/cli/index.md b/docs/reference/cli/index.md
index a25269fd06..218cffa7db 100644
--- a/docs/reference/cli/index.md
+++ b/docs/reference/cli/index.md
@@ -66,6 +66,7 @@ Coder — A tool for provisioning self-hosted development environments with Terr
| [support](./support.md) | Commands for troubleshooting issues with a Coder deployment. |
| [server](./server.md) | Start a Coder server |
| [provisioner](./provisioner.md) | View and manage provisioner daemons and jobs |
+| [ai-gateway](./ai-gateway.md) | Manage AI Gateway |
| [agent-firewall](./agent-firewall.md) | Network isolation tool for monitoring and restricting HTTP/HTTPS requests |
| [features](./features.md) | List Enterprise features |
| [licenses](./licenses.md) | Add, delete, and list licenses |
diff --git a/enterprise/cli/aigateway.go b/enterprise/cli/aigateway.go
new file mode 100644
index 0000000000..408391456b
--- /dev/null
+++ b/enterprise/cli/aigateway.go
@@ -0,0 +1,185 @@
+package cli
+
+import (
+ "context"
+ "fmt"
+
+ "github.com/google/uuid"
+ "golang.org/x/xerrors"
+
+ "github.com/coder/coder/v2/cli/cliui"
+ "github.com/coder/coder/v2/codersdk"
+ "github.com/coder/serpent"
+)
+
+func (r *RootCmd) aiGateway() *serpent.Command {
+ return &serpent.Command{
+ Use: "ai-gateway",
+ Short: "Manage AI Gateway",
+ Handler: func(inv *serpent.Invocation) error {
+ return inv.Command.HelpHandler(inv)
+ },
+ Children: []*serpent.Command{
+ r.aiGatewayKeys(),
+ },
+ }
+}
+
+func (r *RootCmd) aiGatewayKeys() *serpent.Command {
+ return &serpent.Command{
+ Use: "keys",
+ Short: "Manage AI Gateway keys",
+ Handler: func(inv *serpent.Invocation) error {
+ return inv.Command.HelpHandler(inv)
+ },
+ Children: []*serpent.Command{
+ r.aiGatewayKeysCreate(),
+ r.aiGatewayKeysDelete(),
+ r.aiGatewayKeysList(),
+ },
+ }
+}
+
+func (r *RootCmd) aiGatewayKeysCreate() *serpent.Command {
+ return &serpent.Command{
+ Use: "create ",
+ Short: "Create an AI Gateway key",
+ Middleware: serpent.Chain(
+ serpent.RequireNArgs(1),
+ ),
+ Handler: func(inv *serpent.Invocation) error {
+ client, err := r.InitClient(inv)
+ if err != nil {
+ return err
+ }
+
+ res, err := client.CreateAIGatewayKey(inv.Context(), codersdk.CreateAIGatewayKeyRequest{
+ Name: inv.Args[0],
+ })
+ if err != nil {
+ return xerrors.Errorf("create AI Gateway key %q: %w", inv.Args[0], err)
+ }
+
+ _, _ = fmt.Fprintf(
+ inv.Stdout,
+ "Successfully created AI Gateway key %s (ID: %s, Prefix: %s).\nSave this authentication token, it will not be shown again.\n\n%s\n",
+ cliui.Keyword(res.Name),
+ res.ID,
+ res.KeyPrefix,
+ cliui.Keyword(res.Key),
+ )
+ return nil
+ },
+ }
+}
+
+func (r *RootCmd) aiGatewayKeysList() *serpent.Command {
+ formatter := cliui.NewOutputFormatter(
+ cliui.TableFormat([]codersdk.AIGatewayKey{}, []string{"id", "name", "key prefix", "last used at", "created at"}),
+ cliui.JSONFormat(),
+ )
+
+ cmd := &serpent.Command{
+ Use: "list",
+ Aliases: []string{"ls"},
+ Short: "List AI Gateway keys",
+ Middleware: serpent.Chain(
+ serpent.RequireNArgs(0),
+ ),
+ Handler: func(inv *serpent.Invocation) error {
+ client, err := r.InitClient(inv)
+ if err != nil {
+ return err
+ }
+
+ keys, err := client.ListAIGatewayKeys(inv.Context())
+ if err != nil {
+ return xerrors.Errorf("list AI Gateway keys: %w", err)
+ }
+
+ out, err := formatter.Format(inv.Context(), keys)
+ if err != nil {
+ return xerrors.Errorf("format AI Gateway keys: %w", err)
+ }
+ if out == "" {
+ cliui.Info(inv.Stderr, "No AI Gateway keys found.")
+ return nil
+ }
+
+ _, err = fmt.Fprintln(inv.Stdout, out)
+ return err
+ },
+ }
+
+ formatter.AttachOptions(&cmd.Options)
+ return cmd
+}
+
+func (r *RootCmd) aiGatewayKeysDelete() *serpent.Command {
+ cmd := &serpent.Command{
+ Use: "delete ",
+ Aliases: []string{"rm"},
+ Short: "Delete an AI Gateway key",
+ Middleware: serpent.Chain(
+ serpent.RequireNArgs(1),
+ ),
+ Options: serpent.OptionSet{
+ cliui.SkipPromptOption(),
+ },
+ Handler: func(inv *serpent.Invocation) error {
+ client, err := r.InitClient(inv)
+ if err != nil {
+ return err
+ }
+
+ key, err := aiGatewayKeyByNameOrID(inv.Context(), client, inv.Args[0])
+ if err != nil {
+ return err
+ }
+
+ _, err = cliui.Prompt(inv, cliui.PromptOptions{
+ Text: fmt.Sprintf("Are you sure you want to delete AI Gateway key %s (ID: %s, Prefix: %s)?", cliui.Keyword(key.Name), key.ID, key.KeyPrefix),
+ IsConfirm: true,
+ Default: cliui.ConfirmNo,
+ })
+ if err != nil {
+ return err
+ }
+
+ err = client.DeleteAIGatewayKey(inv.Context(), key.ID)
+ if err != nil {
+ return xerrors.Errorf("delete AI Gateway key %q: %w", key.Name, err)
+ }
+
+ _, _ = fmt.Fprintf(inv.Stdout, "Successfully deleted AI Gateway key %s (ID: %s, Prefix: %s).\n", cliui.Keyword(key.Name), key.ID, key.KeyPrefix)
+ return nil
+ },
+ }
+
+ return cmd
+}
+
+// aiGatewayKeyByNameOrID resolves an AI Gateway key from a name or ID. Names
+// take priority over IDs.
+func aiGatewayKeyByNameOrID(ctx context.Context, client *codersdk.Client, nameOrID string) (codersdk.AIGatewayKey, error) {
+ keys, err := client.ListAIGatewayKeys(ctx)
+ if err != nil {
+ return codersdk.AIGatewayKey{}, xerrors.Errorf("list AI Gateway keys: %w", err)
+ }
+
+ for _, key := range keys {
+ if key.Name == nameOrID {
+ return key, nil
+ }
+ }
+
+ if id, err := uuid.Parse(nameOrID); err == nil {
+ for _, key := range keys {
+ if key.ID == id {
+ return key, nil
+ }
+ }
+ }
+
+ return codersdk.AIGatewayKey{}, xerrors.Errorf("AI Gateway key %q not found", nameOrID)
+}
diff --git a/enterprise/cli/aigateway_test.go b/enterprise/cli/aigateway_test.go
new file mode 100644
index 0000000000..f79ddfa60c
--- /dev/null
+++ b/enterprise/cli/aigateway_test.go
@@ -0,0 +1,228 @@
+package cli_test
+
+import (
+ "bytes"
+ "context"
+ "encoding/json"
+ "fmt"
+ "regexp"
+ "slices"
+ "strings"
+ "testing"
+
+ "github.com/google/uuid"
+ "github.com/stretchr/testify/require"
+
+ "github.com/coder/coder/v2/cli/clitest"
+ "github.com/coder/coder/v2/coderd/aibridge/keys"
+ "github.com/coder/coder/v2/coderd/coderdtest"
+ "github.com/coder/coder/v2/codersdk"
+ "github.com/coder/coder/v2/enterprise/coderd/coderdenttest"
+ "github.com/coder/coder/v2/enterprise/coderd/license"
+ "github.com/coder/coder/v2/testutil"
+)
+
+var (
+ // aiGatewayCreateRe captures the ID and key prefix from create output.
+ aiGatewayCreateRe = regexp.MustCompile(`ID: ([0-9a-f-]+), Prefix: (\S+)\)`)
+
+ // aiGatewayKeyRe captures the one-time secret key from create output,
+ // asserting it is KeyLength alphanumeric characters.
+ aiGatewayKeyRe = regexp.MustCompile(fmt.Sprintf(`(?s)it will not be shown again\.\n\n([0-9A-Za-z]{%d})\n`, keys.KeyLength))
+)
+
+// aiGatewayKey holds the values parsed from `keys create` output.
+type aiGatewayKey struct {
+ name string
+ id uuid.UUID
+ prefix string
+}
+
+// runAIGatewayKeys runs `coder ai-gateway keys ` as the given client
+// and returns its stdout, stderr, and the run error.
+func runAIGatewayKeys(ctx context.Context, t *testing.T, client *codersdk.Client, args ...string) (stdout, stderr string, err error) {
+ t.Helper()
+ inv, root := newCLI(t, append([]string{"ai-gateway", "keys"}, args...)...)
+ clitest.SetupConfig(t, client, root) //nolint:gocritic // tests run CLI operations as the owner
+ outBuf, errBuf := bytes.NewBuffer(nil), bytes.NewBuffer(nil)
+ inv.Stdout = outBuf
+ inv.Stderr = errBuf
+ err = inv.WithContext(ctx).Run()
+ return outBuf.String(), errBuf.String(), err
+}
+
+// createAIGatewayKey creates a key and returns its parsed name, ID, and
+// prefix, asserting the create output is well-formed.
+func createAIGatewayKey(ctx context.Context, t *testing.T, client *codersdk.Client, name string) aiGatewayKey {
+ t.Helper()
+ stdout, _, err := runAIGatewayKeys(ctx, t, client, "create", name)
+ require.NoError(t, err)
+ require.Contains(t, stdout, "Successfully created AI Gateway key "+name)
+
+ // The one-time secret key must be rendered as KeyLength alphanumerics.
+ require.Len(t, aiGatewayKeyRe.FindStringSubmatch(stdout), 2, "expected secret key in create output")
+
+ matches := aiGatewayCreateRe.FindStringSubmatch(stdout)
+ require.Len(t, matches, 3, "expected ID and Prefix in create output")
+ id, err := uuid.Parse(matches[1])
+ require.NoError(t, err)
+ return aiGatewayKey{name: name, id: id, prefix: matches[2]}
+}
+
+// listAIGatewayKeys lists keys as JSON and returns the decoded result.
+func listAIGatewayKeys(ctx context.Context, t *testing.T, client *codersdk.Client) []codersdk.AIGatewayKey {
+ t.Helper()
+ stdout, _, err := runAIGatewayKeys(ctx, t, client, "list", "--output=json")
+ require.NoError(t, err)
+ var listed []codersdk.AIGatewayKey
+ require.NoError(t, json.Unmarshal([]byte(stdout), &listed))
+ return listed
+}
+
+// deleteAIGatewayKey deletes a key by name or ID, asserts success, and
+// returns the command stdout.
+func deleteAIGatewayKey(ctx context.Context, t *testing.T, client *codersdk.Client, arg string) string {
+ t.Helper()
+ stdout, _, err := runAIGatewayKeys(ctx, t, client, "delete", "--yes", arg)
+ require.NoError(t, err)
+ return stdout
+}
+
+func TestAIGatewayKeys(t *testing.T) {
+ t.Parallel()
+
+ dv := coderdtest.DeploymentValues(t)
+ dv.AI.BridgeConfig.Enabled = true
+ ownerClient, owner := coderdenttest.New(t, &coderdenttest.Options{
+ Options: &coderdtest.Options{
+ DeploymentValues: dv,
+ },
+ LicenseOptions: &coderdenttest.LicenseOptions{
+ Features: license.Features{
+ codersdk.FeatureAIBridge: 1,
+ },
+ },
+ })
+ t.Run("CRUD", func(t *testing.T) {
+ t.Parallel()
+ ctx := testutil.Context(t, testutil.WaitLong)
+
+ // List returns empty when no keys exist.
+ stdout, stderr, err := runAIGatewayKeys(ctx, t, ownerClient, "list")
+ require.NoError(t, err)
+ require.Empty(t, stdout)
+ require.Contains(t, stderr, "No AI Gateway keys found.")
+
+ // Create two keys and capture their names, IDs, and prefixes.
+ created := make([]aiGatewayKey, 0, 2)
+ for _, name := range []string{"gateway-key-a", "gateway-key-b"} {
+ created = append(created, createAIGatewayKey(ctx, t, ownerClient, name))
+ }
+
+ // List returns both created keys as JSON. Sort by name so the
+ // assertion does not depend on the list ordering.
+ listed := listAIGatewayKeys(ctx, t, ownerClient)
+ require.Len(t, listed, 2)
+ slices.SortFunc(listed, func(a, b codersdk.AIGatewayKey) int {
+ return strings.Compare(a.Name, b.Name)
+ })
+ for i, want := range created {
+ require.Equal(t, want.name, listed[i].Name)
+ require.Equal(t, want.id, listed[i].ID)
+ require.Equal(t, want.prefix, listed[i].KeyPrefix)
+ }
+
+ // Default table output renders created keys, guarding against table
+ // column and struct tag drift.
+ tableOut, _, err := runAIGatewayKeys(ctx, t, ownerClient, "list")
+ require.NoError(t, err)
+ require.Contains(t, tableOut, "KEY PREFIX")
+ for _, key := range created {
+ require.Contains(t, tableOut, key.id.String())
+ require.Contains(t, tableOut, key.name)
+ require.Contains(t, tableOut, key.prefix)
+ }
+
+ // Delete the first key by name.
+ stdout = deleteAIGatewayKey(ctx, t, ownerClient, created[0].name)
+ require.Contains(t, stdout, "Successfully deleted AI Gateway key "+created[0].name)
+
+ // List returns only the remaining key.
+ listed = listAIGatewayKeys(ctx, t, ownerClient)
+ require.Len(t, listed, 1)
+ require.Equal(t, created[1].name, listed[0].Name)
+
+ // Delete the second key by ID.
+ stdout = deleteAIGatewayKey(ctx, t, ownerClient, created[1].id.String())
+ require.Contains(t, stdout, "Successfully deleted AI Gateway key "+created[1].name)
+
+ // List returns empty after all keys deleted.
+ require.Empty(t, listAIGatewayKeys(ctx, t, ownerClient))
+
+ // Delete a non-existent key returns not found.
+ _, _, err = runAIGatewayKeys(ctx, t, ownerClient, "delete", "--yes", created[0].name)
+ require.ErrorContains(t, err, created[0].name)
+ require.ErrorContains(t, err, "not found")
+
+ // Name resolution takes priority over ID. A valid key name can be
+ // formatted like a UUID, so create a key whose name is another key's
+ // ID and confirm delete resolves by name first.
+ first := createAIGatewayKey(ctx, t, ownerClient, "some-key")
+ colliding := createAIGatewayKey(ctx, t, ownerClient, first.id.String())
+ require.NotEqual(t, first.id, colliding.id)
+
+ // Deleting with the ID-shaped argument deletes the colliding key
+ // (matched by name), not the key whose ID matches.
+ stdout = deleteAIGatewayKey(ctx, t, ownerClient, colliding.name)
+ require.Contains(t, stdout, "ID: "+colliding.id.String())
+
+ listed = listAIGatewayKeys(ctx, t, ownerClient)
+ require.Len(t, listed, 1)
+ require.Equal(t, first.id, listed[0].ID)
+ require.Equal(t, first.name, listed[0].Name)
+ })
+
+ t.Run("InvalidKeyName", func(t *testing.T) {
+ t.Parallel()
+ ctx := testutil.Context(t, testutil.WaitLong)
+
+ _, _, err := runAIGatewayKeys(ctx, t, ownerClient, "create", strings.Repeat("a", 65))
+ require.ErrorContains(t, err, "create AI Gateway key")
+ require.ErrorContains(t, err, "Invalid key name")
+ })
+
+ t.Run("MemberForbidden", func(t *testing.T) {
+ t.Parallel()
+
+ memberClient, _ := coderdtest.CreateAnotherUser(t, ownerClient, owner.OrganizationID)
+
+ testCases := []struct {
+ name string
+ args []string
+ }{
+ {
+ name: "list",
+ args: []string{"list"},
+ },
+ {
+ name: "create",
+ args: []string{"create", "member-key"},
+ },
+ {
+ name: "delete",
+ args: []string{"delete", "--yes", uuid.NewString()},
+ },
+ }
+
+ for _, tc := range testCases {
+ t.Run(tc.name, func(t *testing.T) {
+ t.Parallel()
+ ctx := testutil.Context(t, testutil.WaitLong)
+
+ _, _, err := runAIGatewayKeys(ctx, t, memberClient, tc.args...)
+ require.Error(t, err)
+ require.ErrorContains(t, err, "Forbidden")
+ })
+ }
+ })
+}
diff --git a/enterprise/cli/root.go b/enterprise/cli/root.go
index 720624031a..f081335722 100644
--- a/enterprise/cli/root.go
+++ b/enterprise/cli/root.go
@@ -18,6 +18,7 @@ func (r *RootCmd) enterpriseOnly() []*serpent.Command {
agplcli.ExperimentalCommand(append(r.AGPLExperimental(), r.enterpriseExperimental()...)),
// New commands that don't exist in AGPL:
+ r.aiGateway(),
r.agentFirewall(),
r.boundaryAlias(),
r.workspaceProxy(),
diff --git a/enterprise/cli/testdata/coder_--help.golden b/enterprise/cli/testdata/coder_--help.golden
index 4e392a8dd6..c9e3b53168 100644
--- a/enterprise/cli/testdata/coder_--help.golden
+++ b/enterprise/cli/testdata/coder_--help.golden
@@ -16,6 +16,7 @@ USAGE:
SUBCOMMANDS:
agent-firewall Network isolation tool for monitoring and restricting
HTTP/HTTPS requests
+ ai-gateway Manage AI Gateway
external-workspaces Create or manage external workspaces
features List Enterprise features
groups Manage groups
diff --git a/enterprise/cli/testdata/coder_ai-gateway_--help.golden b/enterprise/cli/testdata/coder_ai-gateway_--help.golden
new file mode 100644
index 0000000000..2c0d35058b
--- /dev/null
+++ b/enterprise/cli/testdata/coder_ai-gateway_--help.golden
@@ -0,0 +1,12 @@
+coder v0.0.0-devel
+
+USAGE:
+ coder ai-gateway
+
+ Manage AI Gateway
+
+SUBCOMMANDS:
+ keys Manage AI Gateway keys
+
+———
+Run `coder --help` for a list of global options.
diff --git a/enterprise/cli/testdata/coder_ai-gateway_keys_--help.golden b/enterprise/cli/testdata/coder_ai-gateway_keys_--help.golden
new file mode 100644
index 0000000000..8c465b07b2
--- /dev/null
+++ b/enterprise/cli/testdata/coder_ai-gateway_keys_--help.golden
@@ -0,0 +1,14 @@
+coder v0.0.0-devel
+
+USAGE:
+ coder ai-gateway keys
+
+ Manage AI Gateway keys
+
+SUBCOMMANDS:
+ create Create an AI Gateway key
+ delete Delete an AI Gateway key
+ list List AI Gateway keys
+
+———
+Run `coder --help` for a list of global options.
diff --git a/enterprise/cli/testdata/coder_ai-gateway_keys_create_--help.golden b/enterprise/cli/testdata/coder_ai-gateway_keys_create_--help.golden
new file mode 100644
index 0000000000..bcaa7cd6d1
--- /dev/null
+++ b/enterprise/cli/testdata/coder_ai-gateway_keys_create_--help.golden
@@ -0,0 +1,9 @@
+coder v0.0.0-devel
+
+USAGE:
+ coder ai-gateway keys create
+
+ Create an AI Gateway key
+
+———
+Run `coder --help` for a list of global options.
diff --git a/enterprise/cli/testdata/coder_ai-gateway_keys_delete_--help.golden b/enterprise/cli/testdata/coder_ai-gateway_keys_delete_--help.golden
new file mode 100644
index 0000000000..62b0db207a
--- /dev/null
+++ b/enterprise/cli/testdata/coder_ai-gateway_keys_delete_--help.golden
@@ -0,0 +1,15 @@
+coder v0.0.0-devel
+
+USAGE:
+ coder ai-gateway keys delete [flags]
+
+ Delete an AI Gateway key
+
+ Aliases: rm
+
+OPTIONS:
+ -y, --yes bool
+ Bypass confirmation prompts.
+
+———
+Run `coder --help` for a list of global options.
diff --git a/enterprise/cli/testdata/coder_ai-gateway_keys_list_--help.golden b/enterprise/cli/testdata/coder_ai-gateway_keys_list_--help.golden
new file mode 100644
index 0000000000..043ac660f9
--- /dev/null
+++ b/enterprise/cli/testdata/coder_ai-gateway_keys_list_--help.golden
@@ -0,0 +1,18 @@
+coder v0.0.0-devel
+
+USAGE:
+ coder ai-gateway keys list [flags]
+
+ List AI Gateway keys
+
+ Aliases: ls
+
+OPTIONS:
+ -c, --column [id|name|key prefix|created at|last used at] (default: id,name,key prefix,last used at,created at)
+ Columns to display in table output.
+
+ -o, --output table|json (default: table)
+ Output format.
+
+———
+Run `coder --help` for a list of global options.