> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://documents.pyramid-ai.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://documents.pyramid-ai.com/_mcp/server.

# Environments & Keys

## Two environments, one codebase

Pyramid AI runs two completely separate environments:

|                | Production               | Staging (Sandbox)            |
| -------------- | ------------------------ | ---------------------------- |
| **Domain**     | `api.pyramid-ai.com`     | `api-staging.pyramid-ai.com` |
| **Key prefix** | `pai_live_`              | `pai_test_`                  |
| **Database**   | Production data          | Isolated test data           |
| **Purpose**    | Real client integrations | Development and testing      |

```
pai_test_* key ──> api-staging.pyramid-ai.com ──> Staging database
pai_live_* key ──> api.pyramid-ai.com ──────────> Production database
```

These environments are **completely isolated**. They share no data — a document uploaded to staging doesn't exist in production, and vice versa.

## Why two environments?

**Staging exists so you can experiment without consequences.** Upload test documents, create dummy projects, run compliance checks — nothing you do in staging affects real client data.

| Use staging when...                  | Use production when...          |
| ------------------------------------ | ------------------------------- |
| Building a new integration           | Integration is tested and ready |
| Testing upload flows                 | Serving real end-users          |
| Developing mobile app features       | Mobile app is in production     |
| Running automated tests in CI/CD     | Live client-facing workflows    |
| Training new team members on the API | Data matters and must persist   |

## Key-environment enforcement

Keys are **locked to their environment**. This prevents accidents:

| Key          | Used on                      | Result             |
| ------------ | ---------------------------- | ------------------ |
| `pai_test_*` | `api-staging.pyramid-ai.com` | Works              |
| `pai_test_*` | `api.pyramid-ai.com`         | `401 Unauthorized` |
| `pai_live_*` | `api.pyramid-ai.com`         | Works              |
| `pai_live_*` | `api-staging.pyramid-ai.com` | `401 Unauthorized` |

The error message intentionally does not tell you whether the key is valid — it simply says `Unauthorized`. This prevents leaking information about which keys exist.

## Recommended workflow

#### Develop with staging

Use `pai_test_*` keys against `api-staging.pyramid-ai.com`. Upload test documents, experiment freely.

#### Test in CI/CD with staging

Automated tests should use `pai_test_*` keys. Never use production keys in test pipelines.

#### Go live with production

Switch to `pai_live_*` keys and `api.pyramid-ai.com` when your integration is ready.

## Managing keys

### Where to get keys

API keys are managed in the Pyramid platform under **Settings > API Keys**. Organization admins can create, view, and revoke keys.

### Key format

Keys are 40 characters: a 9-character prefix + 32 random characters.

```
pai_live_7fK3aBxxxxxxxxxxxxxxxxxxxxQ2z
|________|________________________________|
 prefix          32 random characters
```

### Key display

After creation, the full key is shown **exactly once**. After that, only a fingerprint is visible:

```
pai_live_...Q2z
```

This fingerprint is safe to use in logs, dashboards, and support tickets.

### Key lifecycle

```
active ──(rotate)──> rotated (24h grace) ──> inactive
  │
  ├──(revoke)──> revoked (immediate)
  │
  └──(expires)──> expired (automatic)
```

* **Rotate** — generates a new key. The old key works for 24 more hours, giving you time to update your integration
* **Revoke** — immediately stops the key from working
* **Expire** — if an expiry date was set, the key stops working automatically

### Best practices

* **One key per integration** — if your Excel add-in and mobile app both use the API, give each its own key. This way you can revoke one without affecting the other
* **Store in environment variables** — never hardcode keys in source code
* **Rotate periodically** — even without a suspected compromise, rotating keys limits exposure
* **Monitor `last_used_at`** — keys that haven't been used in months should be reviewed and potentially revoked