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

# Introduction

> GrowthBook offers a full REST API for interacting with the application.

GrowthBook offers a full REST API for interacting with the application.

Request data can use either JSON or Form data encoding (with proper `Content-Type` headers). All response bodies are JSON-encoded.

The API base URL for GrowthBook Cloud is `https://api.growthbook.io/api`. For self-hosted deployments, it is the same as your API\_HOST environment variable (defaults to `http://localhost:3100/api`). The rest of these docs will assume you are using GrowthBook Cloud.

## Versioning

Endpoints are versioned by path prefix:

* `/v1/...` — stable, widely-supported endpoints
* `/v2/...` — updated endpoints with improved shapes (e.g. unified per-rule environment scope for feature flags)

New integrations should prefer v2 where available. Feature-flag and metric v1 routes are deprecated: they are tagged **LEGACY** in the sidebar and collapsed by default. Use the matching v2 group when both exist.

## Authentication

We support both the HTTP Basic and Bearer authentication schemes for convenience.

You first need to generate a new API Key in GrowthBook. Different keys have different permissions:

* **Personal Access Tokens**: These are sensitive and provide the same level of access as the user has to an organization, or less if the token's permissions are limited. These can be created by going to `Personal Access Tokens` under your user menu. Members with the API Keys **Manage members' tokens** permission can disable a member's token under `Settings -> Manage PATs`, and admins can require new tokens to expire within a maximum lifetime.
* **Secret Keys**: These are sensitive and provide the level of access of the role they are given, which can be any built-in or custom role, with optional per-project role overrides and environment limits. Managing Secret Keys on behalf of an organization takes the [API Keys permissions](https://docs.growthbook.io/account/user-permissions.md). These can be created by going to `Settings -> API Keys`, where Admins can also require new Secret Keys to expire within a maximum lifetime.

If using HTTP Basic auth, pass the Secret Key as the username and leave the password blank (when using curl, add `:` at the end of the secret to indicate an empty password)

```bash theme={null}
curl https://api.growthbook.io/api/v1/features \
  -u secret_abc123DEF456:
```

If using Bearer auth, pass the Secret Key as the token:

```bash theme={null}
curl https://api.growthbook.io/api/v1/features \
-H "Authorization: Bearer secret_abc123DEF456"
```

If a key has an expiration date, every response includes it in the `X-GrowthBook-Key-Expires-At` header (ISO 8601), so scripts can warn before the key stops working. Requests made with an expired key fail with a `401` that says where to create a replacement. See [Expiration](https://docs.growthbook.io/integrations/ai-agents/api-keys.md#expiration) for choosing a key's expiration, the warnings before it lapses, and the organization policies.

## Naming who asked

Requests made with a Secret Key may add an `X-GrowthBook-Requested-By` header naming the organization member who asked for the change, as a user id or an email address. History, revision logs and webhooks show that member next to the key.

Each key acts as **System** (the default) or **User**, set under `Settings -> API Keys`:

* **System**: the header is optional. The key acts as itself, with its own permissions, and the named member is only recorded in history.
* **User**: a request without the header is rejected with a 400. The request acts as the named member, with only what both the key and the member can do, so the header can narrow a key but never widen it. Drafts, owners, `mine` filters and reviews are the member's. The member's rights over their own drafts, comments and verdicts apply only where the key's own role allows the same action.

A System key, or any key that names no one, acts as itself: it can draft and review with its own permissions, and its verdicts are judged on its role. A key belongs to no team, so its approval never satisfies a rule that requires approval from a team. No one can rule on a draft they created, and a key acting as itself can't rule on a draft it created or edited. An approval a User key gives counts only where both the key and the member could approve, and stops counting if the key is disabled, expires or is deleted. Because the header isn't verified, whoever holds the key can act as any member. A value that does not match a member of the organization is rejected with a 400. A create request that omits `owner` defaults it to the member a User key names. Scheduled and auto-publishes run later with the permissions of the request that armed them, and don't run if that member has left the organization, the key has expired, or the key no longer acts as User. Personal Access Tokens and OAuth tokens already act as their user and reject the header.

```bash theme={null}
curl https://api.growthbook.io/api/v1/features \
-H "Authorization: Bearer secret_abc123DEF456" \
-H "X-GrowthBook-Requested-By: alice@example.com"
```

## Naming and human review

People review what the API creates in the GrowthBook UI.

* Keep keys and names short (2-5 words). Follow your organization's Feature Flag key format (`featureRegexValidator` and `featureKeyExample` in `GET /v1/settings`), or use kebab-case if it has none. Feature Flag keys can't be changed after creation, and an experiment's tracking key shouldn't change after launch.
* Put context (why, plan, tickets, links) in `description`, not in names or keys.
* Group with `tags`, not name prefixes. Projects (`project` or `projects`) control access and where a resource can be used, so set them deliberately instead of using them as labels.
* To create a Feature Flag disabled with `POST /v2/features`, list every environment in `environments` with `enabled: false` (on `POST /v1/features`, each entry also needs `rules: []`). An environment you leave out uses its default state, which may be enabled.
* Leave publishing and production changes to a person unless asked.
* If a request is blocked (an approval requirement, a warning, a Custom Hook, schema validation or a permission error), stop and ask a person. Don't retry with an override field, a different key or another workaround unless a person asked you to. If a write answers `202` with a `confirmation`, give a person its `url` and wait (see [Waiting for confirmation](#waiting-for-confirmation)).

## Waiting for confirmation

A key can require a person to confirm some writes before they run (see [API Keys for Agents](https://docs.growthbook.io/integrations/ai-agents/api-keys.md#require-confirmation)). A held write doesn't run. It answers `202 Accepted` with the confirmation, a `Location` header to poll, and a `Retry-After` header with the seconds to wait between polls:

```json theme={null}
{
  "confirmation": {
    "id": "cnf_abc123",
    "status": "pending",
    "actions": [
      { "action": "feature.publish", "environments": ["production"] }
    ],
    "summary": "Publish revision 3 of checkout-redesign",
    "url": "https://app.growthbook.io/confirmations/cnf_abc123",
    "expiresAt": "2026-10-15T18:00:00.000Z",
    "response": null,
    "note": null
  },
  "message": "A person must confirm this in GrowthBook. Give them the url, then poll the Location."
}
```

Give the person the `url`, then poll [`GET /v1/confirmations/{id}`](https://docs.growthbook.io/api/meta/operation/getConfirmation). Pass `wait` (in seconds, up to 30) to hold the poll open until the status changes:

```bash theme={null}
curl "https://api.growthbook.io/api/v1/confirmations/cnf_abc123?wait=30" \
-H "Authorization: Bearer secret_abc123DEF456"
```

| `status` | Meaning |
| - | - |
| `pending` | Waiting for a person, until `expiresAt` (an hour after the request) |
| `running` | Confirmed, and running now |
| `completed` | It ran. `response` holds the status and body the request returned, which may be an error |
| `rejected` | A person declined it, and nothing ran |
| `expired` | Nobody decided in time, or it acts on something that changed after it was held (with a `400` `response`). Nothing ran |

`note` carries anything the person left for the agent when they decided. `actions` lists what the request was held for, each with the environments it touches.

* Only the key that made the request, or another token of the same person, can read the confirmation.
* Sending the same request again while it's pending returns the same confirmation.
* A request that publishes, reverts or archives, or that changes a Feature Flag or an Experiment's status, schedule, targeting or traffic, isn't run if what it acts on changed after it was held. Other writes, such as editing settings or a draft, run as sent, so they can overwrite changes made while they waited.
* A confirmed request runs once, with the key's permissions (or the member's role it assumes), not the confirmer's. Send it again to retry, which asks for a new confirmation.
* Confirming isn't a review approval.

## Errors

The API may return the following error status codes:

* **400** - Bad Request - Often due to a missing required parameter
* **401** - Unauthorized - No valid API key provided
* **402** - Request Failed - The parameters are valid, but the request failed
* **403** - Forbidden - Provided API key does not have the required access
* **404** - Not Found - Unknown API route or requested resource
* **422** - Unprocessable Entity - The request is valid, but a warning, validation rule, approval requirement, or another publishing gate blocked it. Do not assume that `ignoreWarnings` clears every 422 response.
* **429** - Too Many Requests - You exceeded the rate limit of 60 requests per minute, or the limit your self-hosted server sets. Try again later.
* **5XX** - Server Error - Something went wrong on GrowthBook's end (these are rare)

The response body will be a JSON object with the following properties:

* **message** - Information about the error

### Publishing gates

Automated callers, including AI agents, should report a blocked response to a person instead of retrying with an override. Use an override only when a person has explicitly asked for it.

Publish responses include a `gates` array that explains every blocker:

* `type`, `severity`, and `messages` identify the problem.
* `override` names the request-body field that can bypass it. This is `ignoreWarnings` for warnings, `skipSchemaValidation` for schema and invariant failures, or `skipHooks` for Custom Hook rejections. A value of `null` means there is no request-body override.
* `requiresPermission` identifies any additional permission needed to use the override.
* `resolution` provides an API action, method, and path when the blocker must be resolved another way.

For example, an approval gate is cleared by approving the revision or by using a caller with **Bypass draft approvals** access. A Config lock is cleared through the unlock route in `resolution`.

When a successful publish bypasses a gate, the response includes `bypassedGates`. Each entry reports the gate `type` and how it was bypassed in `via`, which is one of `ignoreWarnings`, `skipSchemaValidation`, `skipHooks`, `bypassApprovalPermission`, `restApiBypassesReviews`, or `revertsBypassApproval` (reverts only). This field is omitted when no gates were bypassed.
