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

# Authentication

> How tracing keys authenticate ingestion, how session cookies authenticate everything else, and which permission each endpoint needs.

The API has two authentication schemes. Which one an endpoint uses depends on who is meant to call it.

| Scheme | Used by | How you send it |
| - | - | - |
| **Tracing key** | `POST /traces/ingest`, `POST /traces`, `POST /metrics`, `POST /logs` | `Authorization: Bearer tk_live_...` or `x-tracing-key: tk_live_...` |
| **Session** | Every other endpoint | The session cookie your browser receives when you sign in to AgentHub or ModelHub |

One endpoint needs neither: `POST /auth/sso-discovery`, which the sign-in page calls before anyone is signed in.

## Tracing keys

A tracing key identifies a **project**, not a person. Everything sent with it lands in that project, and the key can do nothing except send telemetry: it can't read traces or call any other endpoint.

### Format

```text theme={null}
tk_live_<48 lowercase hex characters>
tk_test_<48 lowercase hex characters>
```

The prefix records whether you created the key as **Live** or **Test** in **Settings → Tracing keys**. Both prefixes are accepted by every ingestion endpoint and behave identically; the distinction is for your own bookkeeping.

### Sending the key

<CodeGroup>
  ```bash Authorization header theme={null}
  curl -X POST https://pillarforge.corepillar.ai/api/v1/traces/ingest \
    -H "Authorization: Bearer tk_live_..." \
    -H "Content-Type: application/json" \
    -d @trace.json
  ```

  ```bash x-tracing-key header theme={null}
  curl -X POST https://pillarforge.corepillar.ai/api/v1/traces/ingest \
    -H "x-tracing-key: tk_live_..." \
    -H "Content-Type: application/json" \
    -d @trace.json
  ```
</CodeGroup>

Rules:

* If the `Authorization` header starts with `Bearer ` (capital B, one space), the rest of it is used as the key and `x-tracing-key` is ignored.
* Otherwise `x-tracing-key` is used. An `Authorization` header with any other scheme, or a lowercase `bearer`, is ignored.
* Surrounding whitespace is trimmed.

### How a key is checked

1. The key must match `tk_live_` or `tk_test_` followed by exactly 48 hex characters. Anything else is rejected with `401` "Invalid tracing key format." before any lookup.
2. AgentHub hashes the key with SHA-256 and looks the hash up. Only hashes are stored, which is why a key can be shown only once, at creation.
3. A revoked key is rejected with `401` "Invalid or revoked tracing key." Revocation takes effect on the next request.
4. If the hash doesn't match, AgentHub falls back to each project's built-in legacy key, which is generated when the project is created. That key isn't shown in the UI and can't be revoked under **Settings → Tracing keys**; it's accepted only so early integrations keep working. Always use a key from **Settings → Tracing keys** for new work.

### Managing keys

Creating and revoking keys needs the `manage_org_settings` permission (Owner and Admin roles). Any member of the organization can list a project's keys, but the list shows only the prefix and a short hint, never the full key. See [Tracing keys](/agenthub/settings/tracing-keys) for the UI, or the `Tracing keys` endpoints in this reference.

<Warning>
  Never put a tracing key in browser or mobile code. Anyone who can read the key can write traces into your project. Send telemetry from your servers.
</Warning>

## Session authentication

All other endpoints act **as the signed-in user**. They read the session cookie set when that user signed in to AgentHub or ModelHub with a password or through Okta SSO, and they see exactly what that user can see in the web app.

* There are no personal API tokens or service accounts. These endpoints are designed for the web apps; calling them from a script means reusing a browser session, which isn't supported.
* An expired or missing session returns `401`. Most endpoints use the code `UNAUTHORIZED`; the chat and encryption-key endpoints use `UNAUTHENTICATED`.
* The organization is always derived from the session's own memberships, never trusted from the request. Project-scoped endpoints look the project up through your memberships, so a project in another organization returns `404 PROJECT_NOT_FOUND` rather than revealing that it exists.
* Being signed in but not a member of the organization returns `403 FORBIDDEN` "You do not have access to this organization."

## Permissions

Each organization has four system roles. Their permissions are:

| Permission | Owner | Admin | Editor | Viewer |
| - | :-: | :-: | :-: | :-: |
| `manage_org_settings` | Yes | Yes | | |
| `manage_members` | Yes | Yes | | |
| `manage_sso` | Yes | Yes | | |
| `manage_model_keys` | Yes | Yes | Yes | |
| `manage_agents` | Yes | Yes | Yes | |
| `run_agents` | Yes | Yes | Yes | |
| `manage_metrics` | Yes | Yes | Yes | |
| `view_traces` | Yes | Yes | Yes | Yes |
| `manage_comments` | Yes | Yes | Yes | Yes |
| `export_rl_data` | Yes | Yes | | |
| `manage_billing` | Yes | | | |

Your organization's super admins act with the full Owner permission set in every organization they can reach, whatever their role there. For what each role means in the product, see [Roles and permissions](/agenthub/settings/roles-and-permissions).

A request that lacks the permission it needs returns `403 FORBIDDEN`.

### Required permission per endpoint

"Member" means any role in the endpoint's organization. "A or B" means either permission is enough.

| Endpoint | Auth | Permission |
| - | - | - |
| `POST /traces/ingest` | Tracing key | None |
| `POST /traces` (OTLP) | Tracing key | None |
| `POST /metrics` | Tracing key | None |
| `POST /logs` | Tracing key | None |
| `POST /auth/sso-discovery` | None | None (rate limited) |
| `GET /orgs` | Session | Signed in |
| `GET /orgs/{orgId}/members` | Session | Member |
| `POST /orgs/{orgId}/members/invite` | Session | `manage_members` |
| `PATCH /orgs/{orgId}/members/{userId}` | Session | `manage_members` |
| `GET /orgs/{orgId}/projects` | Session | Member |
| `POST /orgs/{orgId}/projects` | Session | `manage_org_settings` or `manage_members` |
| `GET /orgs/{orgId}/evaluators` | Session | Member |
| `POST /orgs/{orgId}/evaluators` | Session | `manage_agents` or `manage_org_settings` |
| `PATCH /orgs/{orgId}/evaluators/{evaluatorId}` | Session | `manage_agents` or `manage_org_settings` |
| `DELETE /orgs/{orgId}/evaluators/{evaluatorId}` | Session | `manage_agents` or `manage_org_settings` |
| `GET /orgs/{orgId}/evaluator-collections` | Session | Member |
| `POST /orgs/{orgId}/evaluator-collections` | Session | `manage_agents` or `manage_org_settings` |
| `GET /orgs/{orgId}/sso-config` | Session | `manage_sso` |
| `PUT /orgs/{orgId}/sso-config` | Session | `manage_sso` |
| `GET /projects/{projectId}/traces` | Session | Member |
| `GET /projects/{projectId}/traces/{traceId}` | Session | Member |
| `GET /projects/{projectId}/traces/{traceId}/evaluate` | Session | Member |
| `POST /projects/{projectId}/traces/{traceId}/evaluate` | Session | `run_agents` or `manage_agents` |
| `POST /projects/{projectId}/traces/adopt` | Session | `run_agents` or `manage_agents` |
| `GET /projects/{projectId}/tracing-keys` | Session | Member |
| `POST /projects/{projectId}/tracing-keys` | Session | `manage_org_settings` |
| `DELETE /projects/{projectId}/tracing-keys/{keyId}` | Session | `manage_org_settings` |
| `POST /projects/{projectId}/cost-calculator/estimate` | Session | Member |
| `POST /projects/{projectId}/cost-calculator/generate-scenarios` | Session | `run_agents` or `manage_agents` |
| `POST /projects/{projectId}/cost-calculator/generate-scenarios/stream` | Session | `run_agents` or `manage_agents` |
| `GET /projects/{projectId}/cost-calculator/runs/{runId}` | Session | Member |
| `POST /projects/{projectId}/cost-calculator/runs/{runId}/execute` | Session | `run_agents` or `manage_agents` |
| `POST /projects/{projectId}/cost-calculator/runs/{runId}/execute/stream` | Session | `run_agents` or `manage_agents` |
| `POST /projects/{projectId}/cost-calculator/recommend` | Session | Member |
| `GET /projects/{projectId}/cost-calculator/trace-runs` | Session | Member |
| `POST /projects/{projectId}/cost-calculator/trace-runs/extract` | Session | Member |
| `POST /projects/{projectId}/evaluations/explain` | Session | `run_agents` or `manage_agents` |
| `POST /projects/{projectId}/synthetic/extract` | Session | `run_agents` or `manage_agents` |
| `POST /projects/{projectId}/synthetic/synthesize` | Session | `run_agents` or `manage_agents` |
| `POST /projects/{projectId}/synthetic/scenarios` | Session | `run_agents` or `manage_agents` |
| `POST /chat/stream` | Session | Member of the organization and project named in the body |
| `POST /chat/scan` | Session | Signed in |
| `POST /chat/attachments` | Session | Signed in |
| `GET /chat/conversations` | Session | Member of the organization and project in the query; returns only your own conversations |

<Note>
  Reading traces checks organization membership, not the `view_traces` permission. Every role has `view_traces`, so this makes no practical difference today.
</Note>


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