Authentication
The credentials FurniOps accepts, which one to use for what, how the mode of a request is decided, and what every credential can never do.
Every request to https://api.furniops.com carries one bearer credential:
Authorization: Bearer <credential>There are two families of credential. Pick by who is acting:
| You are building | Use | Lives | Page |
|---|---|---|---|
| Your own server, script or sync job | An API key (fo_live_…, fo_rk_live_…) | Until you revoke it, or its expiry | API keys |
| An AI agent that runs unattended | A restricted API key made for an AI agent | Until you revoke it, or its expiry | API keys |
| An app your organisation's members sign in to | An OAuth app (web, single-page or native) and the authorization code flow with PKCE | Access token 10 minutes, refreshed | OAuth apps |
| A server that calls the API as your organisation, without a key to store | An OAuth app of the machine kind, and client credentials | Access token 10 minutes | OAuth apps |
| A terminal session | furniops login (the device flow) | Refreshed while you use it | CLI |
| An MCP client (Claude, Cursor, ChatGPT…) | FurniOps sign-in from the client, or an agent key | Access token 10 minutes | MCP server |
Keys are the simplest and the most common. OAuth tokens are for acting as a person, within that person's role, or for a server that should hold a client secret it can rotate rather than a key.
What every credential shares
- One organisation. A key belongs to the organisation that made it; an OAuth token is bound to one organisation when it is issued. No request is ever ambiguous about whose data it reads.
- Permissions. Every operation in the API reference names the scopes it
needs, such as
products:read. A credential holds a set of them; a missing one answers403permission_denied. The catalogue is on API keys. - Never more than a person. A credential never holds a permission the member who made it (or the member it acts for) does not hold. An OAuth token's permissions are its scopes intersected with the member's role at that moment, so a role change applies to the next token.
- People-only actions stay with people. No key and no OAuth app can manage API keys, OAuth apps, members, the organisation's settings, billing, single sign-on or the AI agents' settings. Those are done in the dashboard, by a signed-in member. A leaked credential can never mint another one or change who controls the organisation.
Live and test
Each organisation has live data and a sandbox (test mode). Which one a request acts on depends on the credential:
| Credential | Mode |
|---|---|
| API key | Fixed by its prefix: fo_test_ and fo_rk_test_ are test, the others live. |
| OAuth access token | Live, unless the request sends FurniOps-Mode: test. |
curl https://api.furniops.com/v1/products \
-H "Authorization: Bearer $FURNIOPS_ACCESS_TOKEN" \
-H "FurniOps-Mode: test"A key that names the other mode in FurniOps-Mode is refused with 400
invalid_request, never silently ignored, so a request cannot act in a
mode you did not mean. The header is read only after the credential is accepted. The SDK sends it
from its mode option and the CLI from --mode or FURNIOPS_MODE. Everything a request creates
belongs to its mode; Live and test lists what is kept apart.
When a credential is refused
| Answer | Means | Do |
|---|---|---|
401 authentication_required | No credential, or one that cannot be used: malformed, unknown, revoked, expired, an expired access token, or a key used from an address its access policy does not allow. The answer is the same for every reason, so a probe learns nothing. | Check the credential is whole and current. Refresh an access token. Check your server's address is in the key's policy. |
403 permission_denied | The credential is valid but lacks the scope, or the member's role does not allow the action. | Use a credential that holds the scope. |
403 feature_unavailable | The organisation's plan does not include what the request needs. | See Plans and features. |
400 invalid_request on FurniOps-Mode | A key named the other mode, or the value is not exactly live or test. | Leave the header out for a key, or use a key of the mode you want. |
Every request an organisation makes (refusals included, except a 401, which has no
organisation) appears in its request logs with the credential that made
it.
Plans and features
What your organisation's plan decides, what feature_unavailable means, how fat webhooks fall back to thin, and how long the change feed and delivery log reach back.
API keys
Secret and restricted keys, the key wizard, every scope a key can hold, templates, access policies, expiry, rotation, keys for AI agents, suspicious-activity reviews and what to do when a key leaks.