> ## Documentation Index
> Fetch the complete documentation index at: https://docs.honeyhive.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# TypeScript Control Plane SDK

> Install and use the type-safe HoneyHive TypeScript Control Plane client to provision projects and manage alerts programmatically with full editor autocompletion.

`@honeyhive/control-plane-sdk` is a type-safe TypeScript client for the HoneyHive Control Plane REST API. It provides a one-to-one mapping of methods to API endpoints, organized into namespaces: `client.projects`, `client.alerts`.

It is the control-plane sibling of the [TypeScript API SDK](/v2/sdk-reference/typescript) (`@honeyhive/api-client`, the data plane SDK). Use this client for control-plane resources ([projects](/v2/workspace/projects) and alerts), and the data plane SDK for trace, event, dataset, and evaluation work.

Built on [openapi-fetch](https://openapi-ts.dev/openapi-fetch/), the client handles response parsing, query serialization, and error handling automatically. All request and response types are generated from the OpenAPI specification, so your editor provides full autocompletion and type checking for every API call.

## Installation

```sh theme={null}
npm install @honeyhive/control-plane-sdk
```

## Quick start

Create a project in a workspace, rename it, and archive it when finished:

```typescript theme={null}
import { Client } from '@honeyhive/control-plane-sdk';

// The API key is read from the HH_CONTROL_PLANE_API_KEY environment variable.
// The control plane URL is read from the HH_CONTROL_PLANE_URL environment
// variable and defaults to https://api.cp.us.honeyhive.ai
const client = new Client();

// Projects are created inside a workspace, and this SDK exposes no way to look a
// workspace up, so its id has to be supplied. The id appears in the HoneyHive
// app's URL while you are viewing that workspace.
const workspaceId = 'your-workspace-id';

// 1. Create a project. Without `project_creator` the project is created with no
// memberships, so it is reachable only by users whose access is inherited or
// granted through SSO.
const created = await client.projects.create({
  workspace_id: workspaceId,
  name: 'checkout-assistant',
  description: 'Traces and evaluations for the checkout assistant',
  project_creator: 'teammate@example.com',
});
const projectId = created.data.id;

// 2. Rename it. Only the fields present in the request are modified, so the
// description set above survives.
const updated = await client.projects.update({
  project_id: projectId,
  name: 'checkout-assistant-v2',
});
console.log(updated.data.name, updated.data.description);

// 3. Delete it. The project is archived rather than destroyed: it stops
// appearing in reads, and the response carries the archived project.
const deleted = await client.projects.delete({
  project_id: projectId,
});
console.log(`Archived ${deleted.data.name}`);
```

<Note>
  `project_creator` grants the project-creator membership to the named user, who must already be a member of the workspace. Omit it and the project is created with **no memberships**, leaving it reachable only by users whose access is inherited (an org admin, for instance) or granted through an SSO group claim. That is the intended path for organizations that manage access that way, so omitting the field is a legitimate choice rather than an oversight. The field is only accepted on API-key-authenticated requests like the one above; a user-initiated create always makes the calling user the creator and rejects the field with a `400`.
</Note>

## Example: Reading alerts

```typescript theme={null}
import { Client } from '@honeyhive/control-plane-sdk';

const client = new Client();

// List a project's alerts, most recently created first
const { data, pagination } = await client.alerts.list({
  project_id: 'your-project-id',
  limit: 25,
});
console.log(`${data.length} of ${pagination.total} alerts`);

// Fetch one by id
const alert = await client.alerts.get({
  alert_id: 'your-alert-id',
});
console.log(alert.data.name, alert.data.status);
```

## Authorization

The HoneyHive Control Plane API authenticates requests using an API key sent as a Bearer token in the `Authorization` header.

### Which keys work

The control plane accepts **only fine-grained control-plane API keys**, whose values start with `hh_fgcp_`. Create one in the HoneyHive app under an organization's or a workspace's **Settings → Fine-grained API keys**, choosing the scope it roots at and the permissions it carries.

Coarse-grained project keys (a full project key, `hh_`, or a read-only project key, `hh_ro_`) are **not** accepted by any operation this SDK exposes. The client sends whatever key it is given, so supplying one surfaces as a `401` from the control plane on your first request rather than an error at construction.

<Note>
  Data plane keys belong to the [TypeScript API SDK](/v2/sdk-reference/typescript), which reads `HH_PROJECT_API_KEY`. That is a separate variable, so both clients can be configured in the same process without collision.
</Note>

There are three ways to provide the key, listed in order of preference.

### Environment variable (recommended)

Set the `HH_CONTROL_PLANE_API_KEY` environment variable. The client reads it automatically when no `apiKey` option is provided:

```typescript theme={null}
const client = new Client();
```

### apiKey option

Prefer the environment variable above. Reach for this option only when the environment variable can't carry the key, for example when one process talks to several control planes, or the key arrives from a secret manager at startup.

<Note>
  Never hard-code the key or commit it to source control. Always read it from a secret store or environment variable.
</Note>

```typescript theme={null}
const client = new Client({
  apiKey: await secrets.get('honeyhive-cp-key'),
});
```

### Custom middleware

For advanced scenarios (rotating keys, fetching tokens at request time), supply custom middleware that sets the `Authorization` header on each request. Middleware runs after the initial headers are set, so it will override any API key provided via `apiKey` or `HH_CONTROL_PLANE_API_KEY`.

When middleware is supplied, no API key is required, because the middleware is assumed to handle authentication. If both are provided, the API key sets the initial header and the middleware can override it per-request. A client that authenticates entirely through middleware should simply pass no key.

The required header format is `Authorization: Bearer <api-key>`.

```typescript theme={null}
import { Client } from '@honeyhive/control-plane-sdk';

const client = new Client({
  // Passing the middleware inline lets TypeScript infer the correct type from
  // the `middleware` array, so no separate Middleware import is needed.
  middleware: [
    {
      async onRequest({ request }) {
        const key = await fetchApiKeyFromVault();
        request.headers.set('Authorization', `Bearer ${key}`);
        return request;
      },
    },
  ],
});
```

See the [openapi-fetch middleware documentation](https://openapi-ts.dev/openapi-fetch/middleware-auth) for more details on the `Middleware` interface.

## Control plane URL

By default the client talks to `https://api.cp.us.honeyhive.ai` (HoneyHive's managed control plane). To point at a self-hosted or per-customer deployment, set the `HH_CONTROL_PLANE_URL` environment variable or pass `controlPlaneUrl`:

```sh theme={null}
export HH_CONTROL_PLANE_URL=https://honeyhive.example.com
```

```typescript theme={null}
const client = new Client({
  controlPlaneUrl: 'https://honeyhive.example.com',
});
```

<Note>
  This is deliberately not the URL the HoneyHive web app talks to. The app authenticates with a session cookie rather than an API key, so its endpoint is not usable by this client.
</Note>

## Verbose logging

Set `verbose: true` (or the `HH_VERBOSE` environment variable to `true`) to log the resolved control plane URL, a masked API key, and the SDK package + version when the client is constructed. Useful for confirming which environment and credential the client is configured with.

```typescript theme={null}
const client = new Client({ verbose: true });
// Control plane URL: https://api.cp.us.honeyhive.ai
// API key: hh_fgcp_9tPqXm2LrKdV7wBnZs4HcY6f_******
// Package: @honeyhive/control-plane-sdk v0.1.0
```

```sh theme={null}
HH_VERBOSE=true node my-script.js
```

Output is written via `console.error` (stderr in Node, devtools in the browser) and only fires once per client construction. An explicit `verbose: false` overrides `HH_VERBOSE`.

The masked key shows the key's **id** and none of its secret, which makes it identical to the masked key value shown for that key in the app, so a log line can be matched directly to a key. Any value that isn't a well-formed fine-grained key is replaced entirely with asterisks rather than partially echoed, and `(none)` is logged when no key resolved.
