# Authentication — ACP API

Authenticate to the Agentic Control Plane API with gsk_ keys. Scopes, the Authorization header, base URLs, errors, and rate limits.

# Authentication

Every API request authenticates with an API key. Keys are workspace-scoped and carry a set of scopes that bound what they can do.

## API keys

Create keys from **Settings → API Keys** in the [console](https://cloud.agenticcontrolplane.com). A key looks like:

```
gsk_acme_8f2a1c9b7d6e5f4a3b2c1d0e9f8a7b6c
```

The middle segment is your **workspace slug** (`acme` above). It's part of the key, which is why the Agent API needs no slug in the path — ACP reads it from the key. For the Governance API, the slug you put in the path must match the slug in the key.

Treat keys like passwords. They grant every permission their scopes allow, for the whole workspace. Never commit one or ship it in client-side code — the API is server-to-server.

## Making an authenticated request

Send the key as a bearer token:

```bash
curl https://api.agenticcontrolplane.com/api/v1/agents \
  -H "Authorization: Bearer $ACP_KEY"
```

The `Bearer ` prefix is optional — a bare `Authorization: gsk_…` is also accepted — but `Bearer` is conventional and recommended.

```python
import os, requests

ACP = "https://api.agenticcontrolplane.com"
headers = {"Authorization": f"Bearer {os.environ['ACP_KEY']}"}

r = requests.get(f"{ACP}/api/v1/agents", headers=headers)
r.raise_for_status()
print(r.json()["profiles"])
```

## Scopes

A key carries scopes that bound what it can do. Grant the least a key needs.

| Scope | Grants |
|-------|--------|
| `*` | Everything — full read/write across agents and governance. Use for your own automation; avoid handing it out. |
| `admin.audit.read` | Read the [audit log](/docs/api/logs). |
| `agents.write` | Create and modify [agent profiles](/docs/api/agents). |
| `agents.read` | List and read agent profiles. |

Reading the audit log requires `admin.audit.read` (or `*`); a key without it gets `403`. Writing governance policy additionally requires that the key was created by an **owner or admin** of the workspace — scopes bound the key, membership bounds the human behind it.

> **Human auth.** The console authenticates with your session (a Firebase ID token), not a `gsk_` key. Some governance endpoints accept either. This reference documents the API-key path — the one you'll script against.

## Base URLs

| Surface | Base |
|---------|------|
| Agents | `https://api.agenticcontrolplane.com/api/v1/agents` |
| Governance | `https://api.agenticcontrolplane.com/<workspace>/admin` |

There is one production host. To isolate testing, create a separate workspace and a key scoped to it rather than reaching for a sandbox URL.

## Errors

Non-2xx responses carry a JSON body with a single `error` field:

```json
{ "error": "api key lacks admin.audit.read scope" }
```

| Status | Meaning |
|--------|---------|
| `400` | Malformed request — missing a required field, bad JSON, an invalid `since` timestamp. |
| `401` | Missing, malformed, or revoked key. Check the `Authorization` header and that the key is still active. |
| `403` | Authenticated, but the key's scopes (or the caller's role) don't permit this. |
| `404` | The resource — usually an agent profile ID — doesn't exist in this workspace. |
| `429` | Rate limited. Back off and retry (see below). |
| `500` | Something failed on our side. Safe to retry idempotent reads. |

Write defensively: branch on the status code, not on string-matching the `error` text, which is meant for humans and may change.

## Rate limits

Mutating governance endpoints are rate limited and return `429` when you exceed the budget. On a `429`, back off — exponential with jitter — and retry. Bulk operations (seeding many policies, importing a fleet of agents) should be paced rather than fired in parallel.

## Versioning

The Agent API is versioned in its path (`/api/v1`). Within a major version we add fields but never repurpose or remove them — so tolerate unknown keys in responses and you won't break on an upgrade. Breaking changes ship under a new version with notice.

## Next

<div style="display:flex;gap:12px;flex-wrap:wrap;margin-top:8px;">
  <a href="/docs/api/quickstart" class="acp-btn acp-btn-primary" style="padding:12px 24px;">Quickstart &rarr;</a>
  <a href="/docs/api/agents" class="acp-btn acp-btn-ghost" style="padding:12px 24px;">Agents reference &rarr;</a>
</div>
