Authentication
Every credential Halo issues, which to use when, the header they go in, what scopes do, and how an API key acts as its person.
Each credential opens its own routes and no others, so the one you need follows from what you're building.
The credentials
| Credential | Looks like | Opens | Made where | Lasts |
|---|---|---|---|---|
| API key | hak_… | /v1/developer/* on api.usehalo.net | The portal | 90 days by default, at most 365 |
| MCP token | hpat_… (personal), hmat_… (OAuth) | /mcp on mcp.usehalo.net | Halo Data, under Account, AI tools, or an app's sign-in | Personal: up to a year. OAuth: an hour at a time, for up to 90 days |
| Embed secret | hes_… | Signs the JWTs that open one report through /v1/public/* | Halo Data, under Settings, Embedding | Until rotated. A token it signs lasts at most 24 hours |
| Link token | hlp_… (public), hle_… (embed) | One report through /v1/public/* | The report's Share dialog | Until it's turned off or reaches its expiry |
| Ingest token | hci_… | POST /v1/public/crm/ingest and POST /v1/public/crm/stats, for one webhook source | Halo CRM, under Sources | Until you make a new one |
| dbt token | hdbt_… | /v1/dbt/artifacts, to send a dbt project from CI | Halo Data, on the connection, under Post from CI | Until it's revoked |
Halo signs webhooks with a secret too. A webhook secret, whs_…, opens nothing: you check a request's signature with it.
The extranet, where a business looks after its record, signs people in with emailed links. Its routes are for its own pages, and aren't an API to build on.
None opens another's routes. A session cookie gets 401 on /v1/developer/*, an API key gets nothing on /v1/orgs/*, /mcp or the Console, and a personal token gets 401 on /v1/developer/*.
Which to use when
| You're building | Use |
|---|---|
| Code on your server that reads datasets, charts or reports | An API key |
| An AI tool or agent that reads data as someone | An MCP token: see Connect a client |
| Code that reads your websites or directory | An API key: see Read websites and the directory |
| Your own system adding to the directory | An ingest token: see Send listings to Halo |
| Something that reacts when the directory changes | A webhook: see Webhooks |
| A report inside your own product, with each reader seeing their own rows | A JWT signed with the embed secret: see Signed embeds |
| A report anyone with the address can open | A link: see Public links and snapshots |
Choose an integration compares them in more detail.
The header
Send an API key on every request, in the Authorization header:
GET /v1/developer/whoami HTTP/1.1
Host: api.usehalo.net
Authorization: Bearer hak_…The API reads nothing else: no cookies, and no key in the address. A request with access_token, api_key, apikey, key or token in its query string gets 400 key_in_query before anything looks at the value. URLs reach logs, histories and other sites' Referer headers, so roll the key if a URL with it may have been logged.
Keep keys on a server. Any origin may call the Developer API without credentials, so a browser would let you, but anyone who loads the page can then read the key. For a browser, use a signed embed.
Every answer carries Cache-Control: no-store and an X-Request-Id. Quote the id if you contact Halo about a request.
Scopes
A key carries some of three scopes. Left out when it's made, it gets all of them. Everything they allow reads, and nothing writes.
| Scope | Opens |
|---|---|
datasets.read | GET /v1/developer/datasets, GET /v1/developer/datasets/{id} |
data.query | POST /v1/developer/datasets/{id}/query, GET /v1/developer/charts/{id}/data |
content.read | GET /v1/developer/charts, GET /v1/developer/charts/{id}, GET /v1/developer/reports, GET /v1/developer/reports/{id}, and websites, listings and businesses |
GET /v1/developer/whoami needs no scope. A request outside the key's scopes gets 403 insufficient_scope.
Scopes only narrow what the person's role allows. A key with data.query whose person can't export still gets 403 export_not_allowed. MCP connections use the same three names: see The MCP server.
How a key acts as its person
A key is one person in one organization. It never carries authority of its own.
It reads what they can read. Requests go through the same services as Halo Data, with the person's access levels and row policies.
It follows them as they are now. Every request loads their account, membership, role and organization again, so a role change applies on the next call.
It ends with their access. Changing their password or two-factor, or leaving the organization, revokes it. See What stops a key.
It remembers how they signed in. If the organization later stops allowing that sign-in method, the key stops too.
It stays in one organization. To read from two, make a key in each.
Requests are logged and audited against the person. Keys owned by an organization rather than a person are planned.
Read more in The security model.