> ## 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.

# How to create API keys programmatically

> Programmatic HoneyHive API key creation for provisioning services: organization credentials, project data plane and ingestion keys, and secret storage.

Create project data plane keys and ingestion keys from your provisioning service, then deliver them to the project's applications through your secret manager. An administrator first creates an organization-rooted data plane key with permission to issue these keys.

<Warning>
  Always store API keys in a secret manager and never as plaintext in databases, files, source code, or logs. Store only key IDs and secret references in your application database.

  If you store a key in plaintext, an attacker who compromises that storage can perform any action the key allows.
</Warning>

## Before you start

* Use the [TypeScript API client](/v2/sdk-reference/typescript) version **1.6.0 or later**, or the [HoneyHive CLI](/v2/cli-reference/getting-started) version **1.8.0 or later**.
* Copy the target project's **Project ID** and **Data plane URL** from **Settings > Project > API Keys > Data Plane**.
* To create the provisioning credential, you need `org.fine_grained_api_key_dp.list` and `org.fine_grained_api_key_dp.post` on the organization. See [role permissions](/v2/workspace/roles#permission-reference).

If your provisioning service also creates projects, use the [control plane SDK](/v2/control-plane-sdk-reference/typescript) for that step, then see [Project not ready or request returns 404](#project-not-ready-or-request-returns-404) before creating their keys.

## 1. Create the provisioning credential

In **Settings > Organization > API Keys**, select **Data Plane**. If a selector appears, choose the physical data plane that hosts the target project. Click **Create API Key**, give it a name such as `project-provisioning`, and choose an expiration date.

Select one or both of these permissions, depending on what your service needs to create:

| Key to create | Permission on the provisioning credential |
| - | - |
| Project data plane key | `project.fine_grained_api_key_dp.post` |
| Ingestion key | `project.ingestion_api_key.post` |

The picker groups these under **Data plane API keys** and **Ingestion API keys**. Selecting either disables data-access permissions, such as chart access, because a provisioning credential carries only key-creation permissions. These permissions are available only at the organization root and must be allowed by the organization's [Data Plane API Key Policy](/v2/workspace/api-keys#restricting-what-fine-grained-keys-can-carry).

Click **Create Key** and save the full value in your secret manager. Configure your provisioning service with that value as `HH_DATA_PLANE_API_KEY`, and set `HH_DATA_PLANE_URL` to the target data plane's URL.

This credential can issue keys for every project in its organization on that physical data plane. Treat it as an administrative credential, keep it in the provisioning service, and give applications only the keys it issues. For projects on another physical data plane, create a separate provisioning credential there.

## 2. Create the project's keys

Set `PROJECT_ID` to the project you are provisioning. Each response returns the new key's `data.key_value` only once, so store it immediately. Record `data.key_id` so an administrator can match the key in Settings.

`exampleSecretManager.store(name, value)` and `example-store-secret <name>` stand in for your secret manager's SDK or CLI. `example-store-secret` reads the create response from standard input, stores `data.key_value` under the given name, and prints only `data.key_id` after storage succeeds.

### Create an ingestion key

Use an ingestion key for an application that sends traces and events. It has no expiration date and takes no permissions list.

<CodeGroup>
  ```typescript TypeScript theme={null}
  import { Client } from '@honeyhive/api-client';

  const projectId = process.env.PROJECT_ID;
  if (!projectId) throw new Error('Set PROJECT_ID to your HoneyHive project ID');

  const client = new Client();
  const result = await client.ingestionApiKeys.create({
    project_id: projectId,
    name: 'production-tracing',
    description: 'Trace export from the production application',
  });
  await exampleSecretManager.store(`honeyhive/${projectId}/ingestion`, result.data.key_value);
  console.log(`Created ingestion key ${result.data.key_id}`);
  ```

  ```bash CLI theme={null}
  set -o pipefail
  honeyhive ingestion-api-keys create \
    --project-id "${PROJECT_ID:?Set PROJECT_ID}" \
    --name production-tracing \
    --description "Trace export from the production application" \
    | example-store-secret "honeyhive/${PROJECT_ID}/ingestion"
  ```
</CodeGroup>

### Create a project data plane key

Choose the permissions the application needs from the [supported operations](/v2/workspace/api-keys#supported-data-plane-operations). This example creates a key that can list and read charts.

The provisioning credential carries only key-creation permissions. It can grant any supported project data-access permission allowed by the organization's policy for this data plane. Issued keys are rooted at the project in `project_id`. Use Settings to create workspace- or organization-rooted keys.

`expires_at` is required and must be in the future and within 365 days of creation. The TypeScript example uses 30 days. For the CLI, set `KEY_EXPIRES_AT` to an ISO 8601 timestamp with a timezone. These commands set it to 30 days from now:

<CodeGroup>
  ```bash Linux theme={null}
  export KEY_EXPIRES_AT=$(date -u -d '+30 days' +%Y-%m-%dT%H:%M:%SZ)
  ```

  ```bash macOS theme={null}
  export KEY_EXPIRES_AT=$(date -u -v+30d +%Y-%m-%dT%H:%M:%SZ)
  ```
</CodeGroup>

<CodeGroup>
  ```typescript TypeScript theme={null}
  import { Client } from '@honeyhive/api-client';

  const projectId = process.env.PROJECT_ID;
  if (!projectId) throw new Error('Set PROJECT_ID to your HoneyHive project ID');

  const client = new Client();
  const result = await client.dataPlaneApiKeys.create({
    project_id: projectId,
    name: 'chart-reporting',
    permissions: ['project.chart.list', 'project.chart.get'],
    expires_at: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000).toISOString(),
  });
  await exampleSecretManager.store(`honeyhive/${projectId}/data-plane`, result.data.key_value);
  console.log(`Created data plane key ${result.data.key_id}`);
  ```

  ```bash CLI theme={null}
  set -o pipefail
  honeyhive data-plane-api-keys create \
    --project-id "${PROJECT_ID:?Set PROJECT_ID}" \
    --name chart-reporting \
    --permissions '["project.chart.list", "project.chart.get"]' \
    --expires-at "${KEY_EXPIRES_AT:?Set KEY_EXPIRES_AT}" \
    | example-store-secret "honeyhive/${PROJECT_ID}/data-plane"
  ```
</CodeGroup>

## 3. Configure the application

Configure the application's environment with the issued value:

* **Ingestion key:** `HH_INGESTION_API_KEY`, for tracing and event writes.
* **Project data plane key:** `HH_DATA_PLANE_API_KEY`, for the permissions you assigned. Pass `project_id` to supported SDK methods or `--project-id` to CLI commands.

See [Using an ingestion key with the SDKs](/v2/workspace/api-keys#using-an-ingestion-key-with-the-sdks) and [Using a data plane key](/v2/workspace/api-keys#using-a-data-plane-key).

## Rotate and revoke keys

Administrators manage the provisioning credential under **Settings > Organization > API Keys > Data Plane**, and the issued keys under **Settings > Project > API Keys**, on the **Data Plane** or **Ingestion** tab. The provisioning credential cannot list or revoke keys, so a leaked credential cannot be used to revoke the keys your applications depend on.

To rotate a key, create and deploy its replacement, then revoke the old key in Settings.

Revoking or expiring the provisioning credential stops further issuance without revoking the keys it issued, so production workloads keep running when you rotate it. Issued keys cannot create more keys, preventing a compromised key from starting an unbounded chain of credentials that keeps growing after the original key is revoked.

If the provisioning credential is compromised, revoke it, then review the keys it issued and revoke any you did not authorize. Each issued key lists the credential's masked ID (`hh_fgdp_<key id>_******`) as its creator in the project's key list. Check every project the credential could reach, including keys absent from your provisioning records.

Narrowing the organization's [API key policy](/v2/workspace/api-keys#restricting-what-fine-grained-keys-can-carry) does not revoke existing keys, including the provisioning credential.

## Troubleshooting

### Creation permissions are missing or disabled

* **Permissions not offered:** Create the credential at **Organization** scope on the **Data Plane** tab, because workspace- and project-rooted keys cannot carry them. If they are still missing, ask an organization admin to check the Data Plane API Key Policy.
* **Permissions disabled:** Clear any data-access permissions selected in the picker.
* **Tab or Create API Key button unavailable:** Check your role's management permissions.
* **Empty data plane selector:** See [Organization data plane visibility](/v2/workspace/api-keys#organization-data-plane-visibility).

### Project not ready or request returns 404

Authorization denials and missing resources both return `404`. Check the data plane URL, the project's organization and data plane, and the credential's creation permissions and expiration. An archived project or revoked credential also returns `404`.

After ruling out those causes, a newly created project may still be reaching its data plane from the control plane. Retry key creation with a bounded delay and retry limit while waiting for it to arrive.

### Request times out

Do not automatically retry a create that timed out or lost its response. It may have issued a key whose value cannot be recovered. Have an administrator revoke any unused key in the project's key list before you retry.

### Request returns 400

For a data plane key, check that `expires_at` is in the future and within 365 days, and that each requested permission is supported and allowed by the organization's policy. Do not include either key-creation permission in the project key's permissions.


## Related topics

- [Managing Projects](/v2/workspace/projects.md)
- [TypeScript API SDK](/v2/sdk-reference/typescript.md)
- [Getting Started](/v2/cli-reference/getting-started.md)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.