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

# API key guide

> How to generate, list, and revoke Nuvia API keys, and which scopes exist

An API key is the token your integration uses to authenticate with the Nuvia API. To learn **how
to send** the key in requests, see [Authentication](/en/autenticacao). This page covers the key's
**lifecycle**: creating, listing, revoking, and choosing scopes.

<Note>
  API keys are created and managed by an authenticated **human user**, using the app's login token
  (human JWT, see [Authentication](/en/autenticacao)). An API key **cannot** manage other API
  keys: the `/v1/api-keys` endpoints don't accept API key authentication.
</Note>

## Create an API key

Send a `POST /v1/api-keys` authenticated as a user. The body accepts:

| Field       | Type              | Required | Description                                                            |
| ----------- | ----------------- | -------- | ---------------------------------------------------------------------- |
| `name`      | string            | Yes      | Identifying name for the key (up to 100 characters).                   |
| `scopes`    | string\[]         | Yes      | List of scopes in `<subject>:<action>` format (see the catalog below). |
| `expiresAt` | string (ISO 8601) | No       | Future expiration date. If omitted, the key doesn't expire by time.    |

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST https://api.nuvia.ai/v1/api-keys \
    -H "Authorization: Bearer $NUVIA_USER_TOKEN" \
    -H "Content-Type: application/json" \
    -d '{
      "name": "Production integration",
      "scopes": ["contacts:read", "messages:create"],
      "expiresAt": "2027-01-01T00:00:00.000Z"
    }'
  ```

  ```javascript JavaScript theme={null}
  const res = await fetch("https://api.nuvia.ai/v1/api-keys", {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.NUVIA_USER_TOKEN}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      name: "Production integration",
      scopes: ["contacts:read", "messages:create"],
      expiresAt: "2027-01-01T00:00:00.000Z",
    }),
  });

  const { accessToken, apiKey } = await res.json();
  ```

  ```python Python theme={null}
  import os
  import requests

  res = requests.post(
      "https://api.nuvia.ai/v1/api-keys",
      headers={"Authorization": f"Bearer {os.environ['NUVIA_USER_TOKEN']}"},
      json={
          "name": "Production integration",
          "scopes": ["contacts:read", "messages:create"],
          "expiresAt": "2027-01-01T00:00:00.000Z",
      },
  )

  body = res.json()
  access_token = body["accessToken"]
  ```
</CodeGroup>

The response (`201`) carries the token in `accessToken` and the key's metadata in `apiKey`:

```json theme={null}
{
  "accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "apiKey": {
    "name": "Production integration",
    "scopes": ["contacts:read", "messages:create"],
    "status": "ACTIVE",
    "keyPreview": "...a1b2",
    "expiresAt": "2027-01-01T00:00:00.000Z"
  }
}
```

<Warning>
  The full token value appears **only once**, in the `accessToken` field of the creation response.
  Nuvia **doesn't store the token**, and there's no way to retrieve it later: only metadata and a
  preview (`keyPreview`, the last 4 characters) are kept. Copy and store the token securely at this
  moment. If you lose the token, revoke the key and create a new one.
</Warning>

## List API keys

`GET /v1/api-keys` returns your company's keys (paginated with `{ data, meta }`). The response
only carries metadata, never the token.

<CodeGroup>
  ```bash cURL theme={null}
  curl https://api.nuvia.ai/v1/api-keys \
    -H "Authorization: Bearer $NUVIA_USER_TOKEN"
  ```

  ```javascript JavaScript theme={null}
  const res = await fetch("https://api.nuvia.ai/v1/api-keys", {
    headers: { Authorization: `Bearer ${process.env.NUVIA_USER_TOKEN}` },
  });

  const { data, meta } = await res.json();
  ```

  ```python Python theme={null}
  import os
  import requests

  res = requests.get(
      "https://api.nuvia.ai/v1/api-keys",
      headers={"Authorization": f"Bearer {os.environ['NUVIA_USER_TOKEN']}"},
  )

  data = res.json()["data"]
  ```
</CodeGroup>

## Revoke an API key

`DELETE /v1/api-keys/:id` revokes the key immediately and responds `204 No Content`. The operation
is **idempotent**: revoking an already-revoked key doesn't cause an error. The key must belong to
your company.

```bash cURL theme={null}
curl -X DELETE https://api.nuvia.ai/v1/api-keys/652f1a9c8b3e4d0012a7f4c1 \
  -H "Authorization: Bearer $NUVIA_USER_TOKEN"
```

Once revoked, the key stops authenticating on any request.

## Available scopes

Scopes follow the `<subject>:<action>` format. When creating a key, grant **only** the scopes your
integration needs. The table below lists the catalog available in the dashboard, grouped by
subject.

| Subject                | Actions                                 | What the subject unlocks                                                                                      |
| ---------------------- | --------------------------------------- | ------------------------------------------------------------------------------------------------------------- |
| `agents`               | `read` · `create` · `update` · `delete` | AI agents: list, create, clone, and update.                                                                   |
| `contacts`             | `read` · `create` · `update` · `delete` | Contacts: manage contacts and their links.                                                                    |
| `conversations`        | `read` · `create` · `update`            | Conversations: list and update (assign agent, owner, priority, team).                                         |
| `campaigns`            | `read` · `create` · `update` · `delete` | Campaigns: manage campaigns, activation, and analytics.                                                       |
| `campaign-enrollments` | `read` · `create`                       | Campaign enrollments: enroll contacts (individually, by list, funnel, or `contact_ids`) and list enrollments. |
| `triggers`             | `create`                                | Campaign triggers: fire enrollment by event.                                                                  |
| `messages`             | `read` · `create`                       | Messages: read messages and attachments from a conversation and send (text and template).                     |
| `inboxes`              | `read` · `create` · `update`            | Inboxes: manage inboxes.                                                                                      |
| `tables`               | `read` · `create` · `update` · `delete` | Tables and lists: manage tables, columns, and exports.                                                        |
| `knowledge-base`       | `read` · `create` · `update`            | Knowledge base: manage knowledge items.                                                                       |
| `webhooks`             | `read` · `create` · `update` · `delete` | Webhooks: manage webhook configurations.                                                                      |
| `search-v2`            | `read` · `create`                       | Search for companies and prospects (Brazil and global) and autocomplete.                                      |
| `linkedin-search`      | `read` · `create`                       | LinkedIn search: profiles, companies, and parameters.                                                         |
| `enrichment`           | `read` · `create`                       | Contact enrichment and eligibility/credit lookup.                                                             |

<Note>
  The dashboard only offers the scopes in this catalog. If an endpoint returns `403` even with a
  valid key, confirm the key was created with the scope that endpoint requires.
</Note>

<Warning>
  **Known exception:** `DELETE /v1/inboxes/{id}` requires the `inboxes:delete` scope, which **isn't**
  in the dashboard's catalog: a key created there gets `403` on this endpoint. To grant it, create
  the key through `POST /v1/api-keys` including `"inboxes:delete"` in `scopes`: creation only
  validates the `<subject>:<action>` format, not the catalog. Every other scope required by the
  Reference's endpoints is in the catalog.
</Warning>

## Ownership model

An API key belongs to the **company** (the tenant), not to the user who created it. The creating
user is recorded only for auditing. One important consequence:

<Warning>
  The key **isn't automatically revoked** when the user who created it is deactivated or removed
  from the company. It stays valid. To end a key's access, **revoke it explicitly** with
  `DELETE /v1/api-keys/:id`.
</Warning>

## Security best practices

<Steps>
  <Step title="Treat the key as a secret">
    The API key is a Bearer token that grants access to everything its scopes allow. Store it in
    an environment variable or secrets vault, never in source code or version control.
  </Step>

  <Step title="Apply least privilege">
    Grant only the scopes the integration needs. Avoid creating keys with the entire catalog "just
    in case".
  </Step>

  <Step title="Set an expiration">
    Use `expiresAt` for short-lived or temporary keys, reducing the exposure window if the key
    leaks.
  </Step>

  <Step title="Revoke what you don't use">
    Revoke old, test, or unused keys. Since the key outlives the creating user's departure,
    cleanup is the company's responsibility.
  </Step>
</Steps>

## Next steps

<CardGroup cols={2}>
  <Card title="Authentication" icon="lock" href="/en/autenticacao">
    How to send the API key in requests and understand auth errors.
  </Card>

  <Card title="Rate limits" icon="gauge-high" href="/en/guias/rate-limits">
    How the API handles request limits today.
  </Card>
</CardGroup>
