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

# API Keys

> Workspace-scoped credentials for Fini's public APIs. Create, manage, and revoke keys for backend services and internal tools.

Fini API keys are workspace-scoped credentials for the public APIs documented in the [API overview](/en/api-reference/overview). Use them when an external service, backend job, BI pipeline, or internal tool needs programmatic access to Fini without going through a dashboard user session.

<Info>
  Workspace API keys currently use two scopes: `read` and `write`. The current Deploy → API Keys screen lets you choose either or both scopes, and the create form starts with both selected.
</Info>

## What an API key is in Fini

Every key:

* belongs to a **workspace**, not to a single agent
* is created by a specific teammate
* can carry one or more scopes
* is shown in plaintext **once**, at creation time
* can be revoked individually
* is sent as `Authorization: Bearer <key>`

The plaintext value starts with the `fini_` prefix. In the dashboard list, Fini only shows a short prefix preview, never the full secret.

## Scope model

| Scope | What it allows | Current routes |
| - | - | - |
| `read` | Non-mutating list, fetch, and status operations | Agent listing, interaction export, document reads, folder snapshots, article reads, and knowledge-job status lookups |
| `write` | Mutation and execution operations | Conversation events, document crawl and ingestion, folder and article create/update/move/delete, agent-folder attachments, and knowledge generation or import |

Use least privilege:

* Keep `Write` off for export-only or analytics-only workflows.
* Include `Write` when the integration needs to send conversation events, ingest or refresh documents, or manage knowledge content.
* Some `read` endpoints use `POST`, so pick scopes by endpoint purpose, not by HTTP method alone.

## Create an API key

Open **Deploy → API Keys** in Fini.

<Steps>
  <Step title="Open the Create API key form">
    Click **New API key** at the top of the page.
  </Step>

  <Step title="Give the key a recognizable name">
    Use a name that tells you where the key is used, such as `Warehouse export`, `Internal dashboard`, or `Zapier sync`. The name is the only thing you'll see in the table later, so be specific.
  </Step>

  <Step title="Choose the scopes">
    The form shows two checkboxes: **Read** and **Write**. Both are selected by default. Leave only the scopes this integration actually needs.
  </Step>

  <Step title="Click Create key">
    Fini generates the key and immediately shows the plaintext secret in a warning card, along with the scopes assigned to that key.
  </Step>

  <Step title="Copy and store it now">
    This is the only time the full key value is shown. Once you dismiss the warning card, you'll only see the short prefix in the table.
  </Step>
</Steps>

<Tip>
  If the key only needs to export data, uncheck **Write** before you create it. Separate keys with separate scope sets make revocation and incident response much easier.
</Tip>

<Warning>
  **If you lose the plaintext value, Fini cannot show it again.** Create a new key and revoke the old one instead. There is no recovery flow.
</Warning>

## Manage existing keys

The API Keys table shows one row per active key. Each row carries:

<ResponseField name="Name" type="string">
  Your human-readable label for the key, set at creation time. The only identifier you'll see day to day.
</ResponseField>

<ResponseField name="Scopes" type="array">
  The permissions assigned to the key. The UI shows them as `Read` and `Write` chips.
</ResponseField>

<ResponseField name="Prefix" type="string">
  The visible beginning of the key, for example `fini_abc12…`. Enough to match a key against a system that's using it, never enough to reconstruct the secret.
</ResponseField>

<ResponseField name="Created by" type="string">
  The teammate who generated the key. Useful for asking "do we still need this?" when a teammate leaves.
</ResponseField>

<ResponseField name="Created" type="datetime">
  When the key was created.
</ResponseField>

<ResponseField name="Last used" type="datetime | null">
  The most recent time the API accepted this key. If `null` or stale, the key is either unused or pointed at a system that's broken.
</ResponseField>

<Tip>
  Use separate keys for separate systems whenever possible. Rotation, debugging, and incident response all become easier when a single key maps to a single application.
</Tip>

## Revoke a key

Click the trash icon on a row to revoke that key.

Revoke is:

* **immediate**, clients using the key stop authenticating on their next request
* **irreversible**, the key cannot be reinstated; create a new one if you change your mind
* **scoped to that one key only**, other keys in the workspace continue working

Any client still using the revoked key will start receiving `401 Unauthorized` right away.

## How to use a key

Send the key in the `Authorization` header as a Bearer token. The example below lists agents from the workspace tied to the key:

<CodeGroup>
  ```bash curl theme={null}
  curl https://api-prod.usefini.com/v2/bots/public \
    -H "Authorization: Bearer fini_xxxxxxxxxxxxxxxxx"
  ```

  ```javascript Node theme={null}
  const res = await fetch(
    "https://api-prod.usefini.com/v2/bots/public",
    {
      headers: {
        Authorization: `Bearer ${process.env.FINI_API_KEY}`,
      },
    }
  );
  const agents = await res.json();
  ```

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

  res = requests.get(
      "https://api-prod.usefini.com/v2/bots/public",
      headers={"Authorization": f"Bearer {os.environ['FINI_API_KEY']}"},
  )
  agents = res.json()
  ```

  ```go Go theme={null}
  req, _ := http.NewRequest("GET",
      "https://api-prod.usefini.com/v2/bots/public", nil)
  req.Header.Set("Authorization", "Bearer "+os.Getenv("FINI_API_KEY"))

  resp, err := http.DefaultClient.Do(req)
  ```
</CodeGroup>

<Warning>
  Do **not** send the key as `x-api-key` or as a query parameter. The public API guard expects an `Authorization: Bearer` header. Other transports will be rejected with `401 Unauthorized`.
</Warning>

For the full endpoint list, request and response shapes, and error semantics, see the [API reference](/en/api-reference/overview).

## Security best practices

<AccordionGroup>
  <Accordion title="Keep keys on the server side" icon="server">
    Never ship a Fini API key in browser code, mobile app bundles, or anywhere a customer can inspect. Keys belong in your backend secrets manager, or environment variables on a server you control.
  </Accordion>

  <Accordion title="One key per application or workflow" icon="layer-group">
    Avoid the shared-key anti-pattern. Separate keys for your warehouse export, your internal dashboard, and your Zapier flow means you can revoke or rotate one without breaking the others, and you can tell from the **Last used** column which system is calling.
  </Accordion>

  <Accordion title="Revoke immediately on teammate offboarding or integration retirement" icon="user-minus">
    Anyone who had access to a key in plaintext can still use it after they leave. Treat key revocation as part of offboarding. Same on the system side: when an integration is retired, revoke its key the same day.
  </Accordion>

  <Accordion title="Rotate on suspected leakage" icon="rotate">
    If a key may have been exposed, committed to git, leaked in a log, or shared in a screenshot, revoke it immediately and create a replacement. Rotation is cheap, investigation later is not.
  </Accordion>

  <Accordion title="Use descriptive names" icon="tag">
    The name is the only thing standing between you and a row of indistinguishable `fini_abc12…` prefixes. `Warehouse export, nightly cron`, not `key 3`.
  </Accordion>
</AccordionGroup>

## Troubleshooting

<AccordionGroup>
  <Accordion title="I dismissed the warning card and now I need the key again" icon="circle-question">
    The full value is shown exactly once, at creation time. Create a new key and revoke the old one. There is no way to retrieve the plaintext later.
  </Accordion>

  <Accordion title="The API returns 401 Unauthorized" icon="lock">
    Check three things in order:

    1. The key hasn't been revoked. Open Deploy → API Keys and confirm the key still appears in the table.
    2. You're sending `Authorization: Bearer <key>`, not `x-api-key` and not a query parameter.
    3. The key value was copied fully, with no missing or extra characters. Bearer tokens are sensitive to truncation.
  </Accordion>

  <Accordion title="The API returns 403 Forbidden" icon="ban">
    The endpoint likely requires a scope the key doesn't have. Check the assigned scope chips on the key row. `Write` is required for conversation events, document ingestion, and knowledge-management mutations. `Read` is required for list, fetch, and status endpoints, even on a few routes that use `POST`.
  </Accordion>

  <Accordion title="Last used never updates" icon="clock">
    The **Last used** timestamp only moves when a request reaches Fini successfully and authenticates with that key. If your client is failing before the request lands, DNS, TLS, wrong base URL, network egress blocked, the column won't move even though you think the key is in use.
  </Accordion>
</AccordionGroup>


## Related topics

- [API overview](/en/api-reference/overview.md)
- [API contract](/en/api-reference/api-contract.md)
- [Get source](/en/api-reference/get-source.md)


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