*REST API › Guides*

# Authentication

> Every operation requires an API key sent as a bearer token. Only an administrator can create a key, and the key acts as that administrator, so decide who owns it before you create it.

## Send the key

Put the key in the `Authorization` header of every request, as a bearer token. Every key starts with `fk_live_`: there is a single prefix, and there are no separate test or sandbox keys, so every key works on the live data of its account. The documentation is public; what is restricted is the credential.

*The header*

```http
GET /api/v1/account HTTP/1.1
Host: app.filmo.io
Authorization: Bearer <your API key>
```

To check what a key can do, call [`GET /api/v1/account`](https://docs.filmo.io/rest-api/endpoint-reference/account/get-the-account). It returns the key that made the request, with its name and scopes but never its secret ([`apiKey`](https://docs.filmo.io/rest-api/objects/account#response-apiKey)).

*Check the key and its scopes*

**cURL**

```bash
# The scopes of the key are in data.apiKey.scopes.
curl -sS "${FILMO_BASE_URL:-https://app.filmo.io}/api/v1/account" \
  -H "Authorization: Bearer $FILMO_API_KEY" | jq '.data.apiKey'
```

**Node**

```javascript
const base = process.env.FILMO_BASE_URL ?? "https://app.filmo.io";
const res = await fetch(base + "/api/v1/account", {
  headers: { Authorization: `Bearer ${process.env.FILMO_API_KEY}` },
});
if (!res.ok) {
  // 401 and 403 come back as application/problem+json: branch on `code`.
  const problem = await res.json();
  throw new Error(res.status + " " + problem.code + " (request " + problem.requestId + ")");
}
const { data } = await res.json();
console.log(data.apiKey.name, data.apiKey.scopes);
```

**Python**

```python
import os
import requests

base = os.environ.get("FILMO_BASE_URL", "https://app.filmo.io")
res = requests.get(
    base + "/api/v1/account",
    headers={"Authorization": f"Bearer {os.environ['FILMO_API_KEY']}"},
    timeout=30,
)
if not res.ok:
    # 401 and 403 come back as application/problem+json: branch on "code".
    problem = res.json()
    raise SystemExit(f"{res.status_code} {problem['code']} (request {problem['requestId']})")
key = res.json()["data"]["apiKey"]
print(key["name"], key["scopes"])
```

## Create a key

1. **Sign in as an administrator** Only administrators of the account can create keys, by hand or through OAuth.
2. **Open the API and MCP settings** In the Spanish interface the section is called Ajustes → API y MCP.
3. **Create the key and name it after the integration** Use one key per integration, so you can revoke one without touching the others.
4. **Choose the scopes** Tick only the permissions the integration calls. The scopes of a key cannot be edited afterwards: to change them, create a new key and revoke the old one. See [Scopes](https://docs.filmo.io/rest-api/guides/scopes).
5. **Set an expiry date, if you want one** The date is optional and must be in the future. A key without a date does not expire, and no key expires for lack of use.
6. **Copy the key and store it** Filmo shows the key once and stores only a hash of it, so it cannot show it again. Put it in a secret manager or an environment variable before you close the dialog.

> **Warning:** The key is shown only once and amounts to programmatic access to your company's data. If you lose it, create a new key and revoke the old one.

The Help Center has the same procedure with screens: [Generate an API key](https://www.filmo.io/help/reference/integrations-and-api/create-an-api-key).

## Who owns the key

A key hangs from a real administrator of the account, the one who created it, and everything it writes is attributed to that administrator. The same holds for each OAuth connection.

Filmo checks that condition on every request, not only when the key is created. If the person who created the key leaves the account or is deactivated, the key answers 401 [`api_key_owner_inactive`](https://docs.filmo.io/rest-api/errors/api_key_owner_inactive). If they stop being an administrator, it answers 401 [`api_key_owner_not_admin`](https://docs.filmo.io/rest-api/errors/api_key_owner_not_admin). The key itself is fine: what changed is the role of its owner, and the fix is to reissue the key from an account that is still an administrator.

### Create production keys from a dedicated administrator account

Do not create production keys from the account of a person who may leave or change role. Create a dedicated, long-lived administrator account for integrations, name it after what it is for, and create the keys from it. There is no service or bot user today: the key owner is a real user. The key itself does not take a seat, but the administrator account you create it from is a user like any other and does, so budget for that account.

### Contractors and agencies

A developer or an agency working for a rental company is not an administrator of its account and cannot create a key. The administrator of the rental company creates the key with only the scopes the work needs and hands it over through a shared secret manager, never by email or chat. The contractor does not need an administrator login. When the work ends, the administrator revokes the key.

## Revoke or rotate a key

Revoke a key in the API and MCP section of the settings (*Ajustes → API y MCP*). A revoked key answers 401 [`api_key_revoked`](https://docs.filmo.io/rest-api/errors/api_key_revoked). A connection made through OAuth appears there labeled as a connected integration, and revoking it cuts off access immediately.

There is no rotation command. Several keys can be active at once, and that overlap is how you rotate a key without downtime:

1. **Create a new key** Same name pattern and same scopes.
2. **Deploy it** Update the secret your integration reads and restart it.
3. **Check the traffic** Confirm the integration calls succeed with the new key.
4. **Revoke the old key**

A subscription can have up to 50 keys that are not revoked (an expired key still counts). OAuth connections count in that total, but only creating a key by hand is refused at the limit. Revoke the ones you no longer use.

## Authentication errors

A request without a usable key is answered with 401 and a problem document. Branch on `code`. Filmo checks a key in this order: it is recognized, then not revoked, then not expired, then its owner is still an active administrator; the first check that fails decides the code. A 401 and a 403 [`insufficient_scope`](https://docs.filmo.io/rest-api/errors/insufficient_scope) do not consume rate-limit quota; every other response of an authenticated key does.

| Status | Error | Meaning | What to do |
| --- | --- | --- | --- |
| 401 | `unauthorized` | The request has no valid `Authorization` bearer header. | Send `Authorization: Bearer <key>`. |
| 401 | `api_key_invalid` | The key is not recognized. | Check that the whole key was copied; create a new one if in doubt. |
| 401 | `api_key_revoked` | The key was revoked. | Create a new key. |
| 401 | `api_key_expired` | The key has expired. | Create a new key. |
| 401 | `api_key_owner_inactive` | The administrator who created the key left the account or was deactivated. | Reissue the key from an account that is still an administrator. |
| 401 | `api_key_owner_not_admin` | The owner of the key is no longer an administrator. | Reissue the key from an administrator account. |

A 403 is a different problem: the key authenticated but may not do this. [`insufficient_scope`](https://docs.filmo.io/rest-api/errors/insufficient_scope) is about the key, [`forbidden`](https://docs.filmo.io/rest-api/errors/forbidden) about its owner and [`addon_required`](https://docs.filmo.io/rest-api/errors/addon_required) (402) about the add-on of a module. [Errors](https://docs.filmo.io/rest-api/guides/errors#cannot) tells them apart. Every error body carries a `requestId`; quote it when you contact support.

## Keep the key safe

- Call the API from a server. Never put a key in browser code, a mobile app or a public repository: anyone who has it has programmatic access to the company's data. In production the API accepts cross-origin browser requests only from `https://docs.filmo.io`, and the OAuth and MCP endpoints send no CORS headers at all, so a page on any other origin cannot call it anyway.
- Read the key from an environment variable or a secret manager, as the examples do with `FILMO_API_KEY`.
- Do not write the `Authorization` header to logs.
- Give each integration its own key with the narrowest scopes it needs.
- Alert on 401 [`api_key_owner_inactive`](https://docs.filmo.io/rest-api/errors/api_key_owner_inactive) and [`api_key_owner_not_admin`](https://docs.filmo.io/rest-api/errors/api_key_owner_not_admin): they mean an integration is about to stop. See [Going live](https://docs.filmo.io/rest-api/guides/going-live).

## How scopes work

Each key carries a set of scopes, chosen when an administrator creates it in the API and MCP section of the settings (*Ajustes → API y MCP*). Each operation requires exactly one scope, shown on its page and in the OpenAPI file as `x-required-scope`. There are 59 scopes in all.

- [`orders:read`](https://docs.filmo.io/rest-api/guides/scopes#orders:read) lets a key read orders. [`orders:write`](https://docs.filmo.io/rest-api/guides/scopes#orders:write) lets it create, change and act on them. Do not assume that write includes read: a key that writes orders and reads them back needs both.
- A scope covers a resource, not a single operation. One write scope covers every create, update, delete and action on that resource.
- Some scopes have no write twin, such as [`account:read`](https://docs.filmo.io/rest-api/guides/scopes#account:read), [`users:read`](https://docs.filmo.io/rest-api/guides/scopes#users:read) and [`schedule:read`](https://docs.filmo.io/rest-api/guides/scopes#schedule:read).
- Two scopes are special. [`workflows:run`](https://docs.filmo.io/rest-api/guides/scopes#workflows:run) runs a workflow. [`ai:ask`](https://docs.filmo.io/rest-api/guides/scopes#ai:ask) asks the AI about your data and spends AI credits.

Scopes bound which operations a key may call. The data it sees is bounded separately by the account (see [Accounts and tenancy](https://docs.filmo.io/concepts/accounts-and-tenancy)), and some operations also need an add-on on the account. Over MCP the same scopes apply, and a key sees only the tools it can call.

## When a call is refused: 403 insufficient_scope

A key without the scope an operation needs gets 403 [`insufficient_scope`](https://docs.filmo.io/rest-api/errors/insufficient_scope). The body names the missing scope in `requiredScope`. Fix it by issuing a key that has that scope.

```json
{
  "type": "https://docs.filmo.io/errors/insufficient_scope",
  "title": "Insufficient scope",
  "status": 403,
  "code": "insufficient_scope",
  "detail": "The API key does not have the required scope (orders:write)",
  "requiredScope": "orders:write",
  "requestId": "req_5f1c2f4e3a5b4f6d8e7a0b1c2d3e4f5a"
}
```

Two other refusals look similar and have different fixes.

| Response | About | Fix |
| --- | --- | --- |
| 403 `insufficient_scope` | The key. It lacks the scope. | Issue a key with the scope in `requiredScope`. |
| 403 `forbidden` | The owner of the key. The key is right, but its owner lacks the module, organization or administrator permission the operation needs. | An administrator of the account grants the permission. |
| 402 `addon_required` | The plan. The account has not contracted, or has let expire, the add-on of that module. | Contract the add-on named in `requiredAddon`. No role bypasses it. |

The help center explains the permissions in the app: [API scopes and permissions](https://www.filmo.io/help/reference/integrations-and-api/api-scopes-and-permissions) and [Roles and permissions](https://www.filmo.io/help/reference/account-and-settings/sign-in-team-and-permissions/roles-and-permissions).

## Least privilege

- One key per integration, so you can revoke one without touching the others.
- Start with the `:read` scopes. Add a write scope only for the resource you write.
- Always include [`account:read`](https://docs.filmo.io/rest-api/guides/scopes#account:read). [`GET /api/v1/account`](https://docs.filmo.io/rest-api/endpoint-reference/account/get-the-account) is the first call of any integration: it returns the scopes of the key, the add-ons of the account and the version of the contract.
- Leave [`ai:ask`](https://docs.filmo.io/rest-api/guides/scopes#ai:ask) and [`workflows:run`](https://docs.filmo.io/rest-api/guides/scopes#workflows:run) out unless the integration really uses them.
- Check what a key can really do by calling [`GET /api/v1/account`](https://docs.filmo.io/rest-api/endpoint-reference/account/get-the-account) with it. A key made through OAuth can end up with different scopes than requested (fewer, or extra ones the administrator ticked).

*See the scopes of a key*

```bash
curl -sS "${FILMO_BASE_URL:-https://app.filmo.io}/api/v1/account" \
  -H "Authorization: Bearer $FILMO_API_KEY"
```

## Recipes by integration type

These are starting points. Remove what your integration does not use; add what a particular call asks for in `requiredScope`.

### CRM contact sync

- [`account:read`](https://docs.filmo.io/rest-api/guides/scopes#account:read)
- [`contacts:read`](https://docs.filmo.io/rest-api/guides/scopes#contacts:read)
- [`contacts:write`](https://docs.filmo.io/rest-api/guides/scopes#contacts:write)
- Optional: [`custom-fields:read`](https://docs.filmo.io/rest-api/guides/scopes#custom-fields:read) to map custom fields, and [`tags:read`](https://docs.filmo.io/rest-api/guides/scopes#tags:read) and [`tags:write`](https://docs.filmo.io/rest-api/guides/scopes#tags:write) to tag what you import.

### Web form that creates an opportunity

- [`account:read`](https://docs.filmo.io/rest-api/guides/scopes#account:read)
- [`contacts:read`](https://docs.filmo.io/rest-api/guides/scopes#contacts:read) to look the person up
- [`contacts:write`](https://docs.filmo.io/rest-api/guides/scopes#contacts:write) to create them
- [`crm:read`](https://docs.filmo.io/rest-api/guides/scopes#crm:read) to read the pipelines
- [`crm:write`](https://docs.filmo.io/rest-api/guides/scopes#crm:write) to create the opportunity
- The CRM operations also need the CRM add-on on the account.

### Availability and reservations on a booking site

- [`account:read`](https://docs.filmo.io/rest-api/guides/scopes#account:read)
- [`availability:read`](https://docs.filmo.io/rest-api/guides/scopes#availability:read)
- [`items:read`](https://docs.filmo.io/rest-api/guides/scopes#items:read)
- [`contacts:read`](https://docs.filmo.io/rest-api/guides/scopes#contacts:read) and [`contacts:write`](https://docs.filmo.io/rest-api/guides/scopes#contacts:write)
- [`orders:read`](https://docs.filmo.io/rest-api/guides/scopes#orders:read) and [`orders:write`](https://docs.filmo.io/rest-api/guides/scopes#orders:write) to create and reserve the order

### Inventory sync

- [`account:read`](https://docs.filmo.io/rest-api/guides/scopes#account:read)
- [`items:read`](https://docs.filmo.io/rest-api/guides/scopes#items:read), which also covers serialized items
- Optional: [`spaces:read`](https://docs.filmo.io/rest-api/guides/scopes#spaces:read) for warehouses and [`items:write`](https://docs.filmo.io/rest-api/guides/scopes#items:write) only if you write stock data back

### Schedule sync

- [`account:read`](https://docs.filmo.io/rest-api/guides/scopes#account:read)
- [`schedule:read`](https://docs.filmo.io/rest-api/guides/scopes#schedule:read)
- Optional: [`tasks:read`](https://docs.filmo.io/rest-api/guides/scopes#tasks:read), [`planner:read`](https://docs.filmo.io/rest-api/guides/scopes#planner:read), [`staff:read`](https://docs.filmo.io/rest-api/guides/scopes#staff:read) and [`vehicles:read`](https://docs.filmo.io/rest-api/guides/scopes#vehicles:read) if you also need tasks, assignable resources, staff or vehicles

### Invoices to accounting

- [`account:read`](https://docs.filmo.io/rest-api/guides/scopes#account:read)
- [`invoices:read`](https://docs.filmo.io/rest-api/guides/scopes#invoices:read), which covers invoices, their lines and credit notes
- [`treasury:read`](https://docs.filmo.io/rest-api/guides/scopes#treasury:read) for payments
- [`contacts:read`](https://docs.filmo.io/rest-api/guides/scopes#contacts:read) for the customer's tax identifier
- [`invoices:write`](https://docs.filmo.io/rest-api/guides/scopes#invoices:write) only if you mirror invoices issued by your ERP with [`PUT /api/v1/invoices/external/{originSystem}/{originExternalId}`](https://docs.filmo.io/rest-api/endpoint-reference/invoices/create-or-update-the-mirror-of-an-externally-issued-invoice)

Every scope, with the operations that need it, is on the [scopes page](https://docs.filmo.io/rest-api/guides/scopes).

---

Canonical URL: https://docs.filmo.io/rest-api/guides/authentication  
Last updated: 2026-10-09