*REST API › Guides*

# Multi-account integrations

> Give each customer account its own credential, store it with that account's timezone and currency, and treat a failing credential as the state of one connection, not of your whole product. OAuth is available for a server you run, and API keys are simpler for server-to-server work.

This guide is for teams that build a product, such as a CRM, an accounting package, a booking site or an inventory tool, that connects to many rental companies. That is the case the API was designed for: third-party integrations. If you integrate with one company's account, the quickstart and the sync guide are enough.

## One credential per customer account

A credential belongs to one Filmo subscription, so one credential can never serve two customers. You can obtain it in two ways.

|  | API key | OAuth connection |
| --- | --- | --- |
| Who creates it | An administrator of the customer, in the API and MCP settings of the app (Ajustes → API y MCP). | An administrator of the customer, on the consent screen. That is where the subscription is chosen. Only read permissions start selected; write permissions, `workflows:run` and `ai:ask` start unselected even if you ask for them, and the administrator turns on the ones they accept. |
| Lifetime | Until it is revoked, or until the expiry you set when you created it. It has no expiry by default. | The access token lasts 1 hour. The refresh token lasts 30 days, counted again at every rotation, so a connection that is refreshed at least every 30 days lives on. |
| Permissions | The scopes chosen when it was created. | The scopes actually granted, which may differ from the ones requested (fewer, or extra ones the administrator ticked). |
| Revocation | In the same settings (Ajustes → API y MCP). It stops working at once. | The same screen, where it appears as a single connected integration. The access token stops working at once and the refresh token can no longer be exchanged. |
| Fit | Simplest for server-to-server integrations: one secret per customer, nothing to refresh. | Works for REST from your server, at the price of a refresh cycle and a consent by an administrator of each account. |

The token OAuth issues is a short-lived API key that works on the whole REST API and on MCP, and it travels the same way, as a Bearer token. A third-party server can use it today: it registers as a public client, sends the administrator through the authorization code flow with PKCE, and exchanges the code for tokens. OAuth is viable now, and a key is simpler when no person is in front of the flow. See [OAuth](https://docs.filmo.io/rest-api/guides/oauth) for the flow.

- There is no `client_credentials` grant, no confidential client and no client secret. Your client is public: it registers dynamically and authenticates with PKCE alone.
- Every connection needs an administrator of that account to consent. You cannot connect an account without one.
- A grant covers one subscription, which the administrator chooses while consenting, and the granted scopes may differ from the ones you requested (fewer, or extra ones the administrator ticked). A customer with several billing accounts needs one grant, or one key, for each of them.
- A subscription can have 50 keys that are not revoked (expired ones count, and so do OAuth connections); the limit blocks creating a new key by hand.

Keep recommending one key or one connection per customer, and never share a credential between two accounts or two environments.

## What to store for each customer

The first call of any integration is [`GET /api/v1/account`](https://docs.filmo.io/rest-api/endpoint-reference/account/get-the-account), with the scope [`account:read`](https://docs.filmo.io/rest-api/guides/scopes#account:read). Call it when a customer connects and keep what it returns.

| Store | Why |
| --- | --- |
| The credential, encrypted | A key, or the access and refresh tokens of an OAuth connection. It is shown only once. |
| `timezone` | Bare days in filters are read in this timezone, and you need it to show times to people the way the customer sees them. |
| `currency` | The account's currency. A document may carry its own. |
| `apiKey` | The key's id, name and granted scopes. Compare the scopes with the ones you need whenever a call answers 403. |
| `addons` | A resource behind an add-on that is not listed answers 402. |
| The state of the connection | Active, or needs attention, with the time of the last good call and the last `X-Request-Id` you saw. |
| One watermark per list | See the sync guide. Watermarks belong to the customer, not to your product. |

## Tenancy across a customer's accounts

What a credential can see depends on how Filmo models each resource ([Accounts and tenancy](https://docs.filmo.io/concepts/accounts-and-tenancy)). Two rules matter for an integration.

| Semantics | Resources | What the credential sees |
| --- | --- | --- |
| Inherited from the parent account | Contacts, projects, tags and custom fields, and reading an order by id. | A billing account also sees these resources of its parent account. A full account, or a subscription outside any organization, sees its own. |
| Strict subscription equality | Everything else, including items, spaces, invoices, quotes, tasks, vehicles, staff, subrents, purchase orders, contracts, the order list and availability. | Exactly the subscription that owns the credential. |

Nothing widens between sibling billing accounts, nor from a parent to its billing accounts, and no operation crosses data between organizations. So, to read the orders and invoices of every billing account of one organization, connect each of them. When you merge their data, deduplicate the inherited resources by id: the same contact appears through every billing account that inherits it.

## Request budget

Each key has 120 requests per minute, and the three availability queries share a further 30 per minute on the same key. The limits are the same for every plan ([Rate limits](https://docs.filmo.io/rest-api/guides/rate-limits)). A credential is the unit of the limit, so give each customer its own queue, read `RateLimit-Remaining` and `RateLimit-Reset` to pace it, and wait `Retry-After` seconds after a 429. Workers that serve the same customer share one budget.

OAuth has its own limits, counted per IP address and not per credential. Registering a client allows 5 requests per minute per IP, and the token endpoint 120 per minute per IP, so the refreshes of all your customers share that budget when they leave from the same server. A 429 from an OAuth endpoint carries `Retry-After`: wait that long, and decide by the status and the header, not by the body, because the body does not always name the limit.

## OAuth: refreshing without breaking the connection

This section applies if you use OAuth connections. With API keys there is nothing to refresh.

The access token expires in 1 hour and the refresh token rotates on every use: each exchange returns a new refresh token, and the previous access token stops working at once. If a worker presents a refresh token that was already rotated, Filmo answers with the same pair when the request comes from the same client within 10 seconds of the rotation. After that window, reusing it revokes the whole chain, the access token and every refresh token, and answers `invalid_grant`. The customer then has to consent again. Two workers that refresh at the same moment usually receive the same pair, but a worker that arrives later than 10 seconds destroys the connection for everyone, so serialize the refresh anyway.

OAuth endpoints answer in the flat OAuth format, `{ "error", "error_description" }`, not in the problem format of the rest of the API. The `error_description` is in Spanish: branch on `error` and show your own text.

1. **Serialize the refresh per customer** Take a lock for that customer's connection around the whole refresh: a database row lock, an advisory lock, or a lock with an expiry in a shared store. Never refresh outside it.
2. **Read the stored tokens after you get the lock** Another worker may have refreshed while you waited. If the stored access token is still valid, use it and release the lock.
3. **Refresh with the stored refresh token** Call `POST /api/v1/oauth/token` with `grant_type=refresh_token`, the `refresh_token` and your `client_id`. The response carries a new `refresh_token`, valid for another 30 days.
4. **Persist both new tokens before anything else** Write the new access token and the new refresh token to storage before you use the access token and before you release the lock. If you lose the new refresh token, the connection is lost.
5. **Refresh before expiry, not on a 401** Refresh a few minutes before the hour is up, so that request threads rarely race for the lock.

Treat `invalid_grant` as final: the refresh token is expired, revoked or already used, or the administrator who consented is no longer an administrator. Do not retry it. Mark the connection as needing attention and ask the customer's administrator to consent again.

## When a customer's connection stops working

Handle each failure as the state of one connection: stop calling with that credential, keep the watermarks, tell the customer's administrator, and resume after they reconnect. Do not retry in a loop.

| Response | Meaning | What to do |
| --- | --- | --- |
| 401 `api_key_invalid`, `api_key_revoked`, `api_key_expired` | The key is not recognised (a key copied wrongly or truncated lands here too: check your value first), was revoked or has expired. | Check that the value you send is complete. If it is, ask the administrator for a new key, or to consent again. For an OAuth connection, a 401 `api_key_expired` means the access token ran out: refresh it before you conclude that anything is wrong. |
| 401 `api_key_owner_inactive`, `api_key_owner_not_admin` | The key is fine; the person who owns it left the account, was deactivated, or stopped being an administrator. | Ask an administrator who is still one to reissue the key, or to consent again. The same applies to OAuth connections. |
| 401 `unauthorized` | The `Authorization: Bearer` header is missing. | Fix your request. Nothing is wrong on the customer's side. |
| 403 `insufficient_scope` | The credential lacks the scope named in `requiredScope`. | Ask for a new key or a new consent with that scope. |
| 403 `forbidden` | The owner of the credential lacks a permission that the operation requires. | An administrator of the account grants it. |
| 402 `addon_required` | The customer's account has not contracted the add-on named in `requiredAddon`. | Turn the feature off for that customer and say why. |
| 429 `rate_limited` | The key's quota ran out. | Wait `Retry-After` seconds (the problem body repeats the value in `retryAfter`). |

## Handover checklist for the customer's administrator

Give this list to the administrator who connects your product. Fill in the scopes from your own needs, and ask for no more.

1. **Choose a dedicated owner** A key acts as the administrator who creates it, and what it writes is attributed to that person. It hangs from that administrator, who already has a seat in the account, and it stops working if that person leaves or stops being an administrator. Create it from an administrator account that will last, not from a person who may move on.
2. **Open the API and MCP settings** In the app it is Ajustes → API y MCP. Only administrators can create keys.
3. **Create one key for the integration** Name it after your product and give it only the scopes you list, for example [`account:read`](https://docs.filmo.io/rest-api/guides/scopes#account:read) plus the read and write scopes of the resources you sync. The scopes are in the [scopes reference](https://docs.filmo.io/rest-api/guides/scopes).
4. **Hand it over once, through a secure channel** The key is shown only once. Receive it in your onboarding form or a shared secrets manager, never by email or chat.
5. **Explain revocation** Revoking the key in the same settings cuts access at once. Tell the administrator what stops working when they do, and what to do if the owner of the key leaves.

## Development accounts

Filmo offers no sandbox and no test key, and this guide describes no partner or developer account type. Develop and run your tests against a separate Filmo account that holds fictitious data (contacts at `example.com`, made-up item names), with its own key stored apart from any customer's. A free trial of 14 days, with no card, can serve for that. Never develop against a customer's account. Writes fire the account's event workflows, so check which are active before you write; see [Testing](https://docs.filmo.io/rest-api/guides/testing) and the sync guide.

If you are building a product for many Filmo customers and want to talk to Filmo about it, see [Support](https://docs.filmo.io/further-reading/support).

---

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