Authentication
All programmatic access to the oHallo API uses API keys. Keys are scoped to your account and carry explicit permissions that control which endpoints you can call.
API key format
Section titled “API key format”API keys follow a structured format that identifies the environment:
sf_live_v1_a3Bx9kLmP2qR7wYz... (production)sf_test_v1_j8Hn4vCf6dWs1xTe... (sandbox)| Prefix | Environment |
|---|---|
sf_live_v1_ | Production: real data, real conversations |
sf_test_v1_ | Sandbox: safe for development and testing |
The full key is a cryptographically random string. Only the SHA-256 hash is stored on the server side. The raw key is shown exactly once at creation, so store it securely.
Creating an API key
Section titled “Creating an API key”- Open the oHallo dashboard and navigate to Settings > API Access.
- Click + New API key.
- Enter a descriptive name (e.g., “Reporting script”, “CRM sync”).
- Select the scopes you need (see the full list below).
- Optionally restrict the key to a specific brand or workspace.
- Optionally set an expiration date.
- Click Create and copy the key immediately. It will not be shown again.
Using the key
Section titled “Using the key”Pass the key in the Authorization header as a Bearer token:
curl https://api.ohallo.eu/api/conversations \ -H "Authorization: Bearer sf_live_v1_a3Bx9kLmP2qR7wYz4nDfGhJkQpStUvWx"In TypeScript:
const response = await fetch("https://api.ohallo.eu/api/conversations?workspaceId=ws_abc123", { headers: { "Authorization": "Bearer sf_live_v1_a3Bx9kLmP2qR7wYz4nDfGhJkQpStUvWx", "Content-Type": "application/json", },});
const data = await response.json();Scopes
Section titled “Scopes”Each API key carries a set of scopes that determine which endpoints it can access. Request only the scopes you need.
| Scope | Access |
|---|---|
conversations:read | List and read conversations, messages, attachments, attention items, consumption metrics, and agent activity |
conversations:write | Reply, compose, resolve, take over, release, assign, unassign, approve guardrail-gated actions, resolve attention items |
kb:read | List, search, and view pending knowledge base entries (including widget management reads) |
kb:write | Create, update, approve, reject, and delete KB entries; manage KB widgets |
policy:read | List, search, and view pending policy entries |
policy:write | Create, update, approve, reject, and delete policy entries |
contacts:read | List, search, and view contacts; read contact-form widgets |
contacts:write | Create, update, delete contacts; link contacts to accounts; manage contact-form widgets |
accounts:read | List, search, and view accounts; list account contacts |
accounts:write | Create and update accounts |
agents:read | List platform and custom agent definitions and their versions; list the models an agent can use |
agents:write | Create, update, and delete custom agents; update agent instructions; restore agent versions. Creating an agent, giving an agent MCP connections, and restoring a version also take mcp:write |
channels:read | List a workspace’s channels and read a channel’s configuration |
channels:write | Create, update, and delete channels; store channel credentials; test connectivity; retry provisioning |
calendar:read | List calendar connections, calendars, and bookings |
calendar:write | Update or delete calendar connections |
quality:read | Read CSAT statistics, circuit breakers, CSAT config, and entity-level quality scores |
quality:write | Update CSAT config and resolve circuit breakers |
brands:read | List and read brands; list a brand’s workspaces (together with workspaces:read) |
brands:write | Create, update, and delete brands; upload a brand’s logo and the assistant’s sender image |
workspaces:read | List and read workspaces |
workspaces:write | Create, update, and delete workspaces |
cases:read | List and read cases, case events, and case types |
mcp:read | List and read MCP connections, their tools, their guardrails, and their version history |
mcp:write | Register, test, change, and delete MCP connections; classify tools, set the assurance they require, and manage their guardrails; assign tools to agents, create an agent or give it connections, and restore an agent version (each together with agents:write) |
Scopes by use case
Section titled “Scopes by use case”The same key works over the REST API and over the oHallo MCP server, so these sets hold for both.
| Use case | Scopes |
|---|---|
| Read conversation data for reporting | conversations:read, contacts:read, accounts:read |
| Manage conversations and CRM data | conversations:read, conversations:write, contacts:read, contacts:write, accounts:read |
| Sync knowledge base entries from documentation | kb:read, kb:write |
| Write knowledge base entries and policies, and review proposals | kb:read, kb:write, policy:read, policy:write |
| Set up a chat or email channel | channels:read, channels:write |
| Set up an agent: find the MCP connection and its tools, choose a model, create the agent | mcp:read, agents:read, agents:write, mcp:write |
| Register an MCP connection, govern its tools, and assign them to agents | mcp:read, mcp:write, agents:write |
| Set up an account, from brand and workspaces to channels, knowledge, policies and agents | brands:read, brands:write, workspaces:read, workspaces:write, channels:read, channels:write, kb:read, kb:write, policy:read, policy:write, mcp:read, mcp:write, agents:read, agents:write |
Channels, knowledge base entries and policies belong to a workspace. Listing the workspaces to find one’s id takes workspaces:read; on the MCP server, get_context returns the workspaces a key reaches, whatever its scopes.
A key restricted to a brand or workspace also needs its restriction to cover what it acts on; see Scoping keys to brand or workspace.
Scope enforcement
Section titled “Scope enforcement”Scope checks apply to programmatic API-key callers only. Endpoints that accept both schemes (dual auth) do not gate dashboard JWT sessions on scopes. People signed in to the oHallo dashboard act with their role rather than scopes: owners and admins create, change, and delete brands, workspaces, channels, agents and MCP connections. The granular scopes documented above govern what an API-key integration can do.
Scoping keys to brand or workspace
Section titled “Scoping keys to brand or workspace”By default, an API key has access to all data within your account. You can restrict a key to a specific scope:
| Scope level | Effect |
|---|---|
| Organisation-wide (default) | Key can access data across all brands and workspaces |
| Brand | Key can only access data in workspaces belonging to that brand |
| Workspace | Key can only access data in that specific workspace |
This is useful when you have multiple integrations or partners that should only see a subset of your data.
Brands and workspaces with a restricted key
Section titled “Brands and workspaces with a restricted key”| Action | Organisation-wide | Brand | Workspace |
|---|---|---|---|
| List and read brands | All brands | Its brand | Its workspace’s brand |
| Create a brand | Yes | No | No |
| Update a brand, upload its images | Any brand | Its brand | No |
| Delete a brand without workspaces | Any brand | No | No |
| List and read workspaces | All workspaces | The brand’s workspaces | Its workspace |
| Create a workspace | Under any brand | Under its brand | No |
| Update a workspace | Any workspace | The brand’s workspaces | Its workspace |
| Delete a workspace | Any workspace | The brand’s workspaces | No |
A brand or workspace outside the key’s restriction answers 404, as if it did not exist. An action the restriction does not cover, on a brand or workspace the key can see, answers 403 with "code": "key_lock".
MCP connections with a restricted key
Section titled “MCP connections with a restricted key”An MCP connection carries its own scope, set when it is registered: the whole organisation, one brand, or one workspace. A key reaches a connection when its restriction and the connection’s scope agree.
| Action | Organisation-wide | Brand | Workspace |
|---|---|---|---|
| List and read connections | All connections | Its brand’s, and those of its brand’s workspaces | Its workspace’s, and its brand’s |
| Register an organisation-wide connection | Yes | No | No |
| Register a connection under a brand | Under any brand | Under its brand | No |
| Register a connection in a workspace | In any workspace | In a workspace of its brand | In its own workspace |
| Change a connection: instructions, tool risk, assurance, guardrails, assignments, test, restore | Any connection | Its brand’s | Its own workspace’s |
| Delete a connection | Any connection | No | No |
A connection outside the key’s restriction answers 404; an action the restriction does not cover, on a connection the key can see, answers 403 with "code": "key_lock". An organisation-wide connection sits above every restriction, so a restricted key neither sees nor registers one.
oHallo’s own system connections, which carry the knowledge base, policies, the calendar and the web reader, are configured by oHallo. A key does not change their instructions or their tools’ risk classifications, and does not restore or delete one. What the account decides about them stays open to a key: guardrails, the assurance a tool requires, and which agents may call its tools.
Agents and channels with a restricted key
Section titled “Agents and channels with a restricted key”A channel belongs to its workspace. A restricted key lists, reads, creates, changes and deletes the channels of the workspaces its restriction covers, and any other channel answers 404.
An agent serves wherever its MCP connections are active. A restricted key reads every agent. It changes an agent only when every connection the agent uses is inside its restriction, and receives 403 with "code": "key_lock" otherwise, and it gives an agent only connections its restriction covers. Assigning a connection’s tools to an agent changes the agent too, so a restricted key assigns them only to an agent whose every connection is inside its restriction, and another agent answers 403 with "code": "key_lock". It detaches an agent from any connection inside its restriction. Deleting an agent and restoring an agent version take a key without a restriction. Who can change an agent lists the scopes each agent action needs.
Key expiration
Section titled “Key expiration”API keys can optionally have an expiration date. After expiry, any request using the key returns 401 Unauthorized. Non-expiring keys remain valid until manually revoked.
Set an expiration when creating keys for:
- Temporary integrations or proof-of-concept projects
- Third-party partners with time-limited access
- Compliance policies that require key rotation
Security best practices
Section titled “Security best practices”Rotate keys regularly. Create a new key, update your integration, then revoke the old key. There is no downtime: both keys work simultaneously during the transition.
Use minimum scopes. Grant only the permissions your integration actually needs. A reporting tool should not have conversations:write access.
Never expose keys in client-side code. API keys are server-side credentials. Do not embed them in browser JavaScript, mobile apps, or public repositories. If you need client-side access, proxy requests through your own backend.
Store keys in a secrets manager. Use your platform’s secrets management (a managed secrets vault, or environment variables in CI/CD) rather than hardcoding keys in source files.
Monitor key usage. The dashboard shows the last-used timestamp for each key. Revoke keys that have not been used recently or that belong to decommissioned integrations.
Error responses
Section titled “Error responses”401 Unauthorized
Section titled “401 Unauthorized”Returned when the API key is missing, malformed, expired, or revoked.
{ "error": "Unauthorized"}403 Forbidden
Section titled “403 Forbidden”Returned when the API key is valid but does not have the required scope for the endpoint. missing_scopes names the scopes the key lacks.
{ "error": "Forbidden", "missing_scopes": ["conversations:write"]}For example, calling POST /api/conversations/{id}/reply with a key that only has conversations:read scope returns 403.
A key restricted to a brand or workspace receives 403 with a code when its restriction does not cover the action, for example a brand-restricted key creating a brand:
{ "error": "This API key's brand or workspace restriction does not allow this action", "code": "key_lock"}