Authentication
Every request carries one API key as a Bearer token. The key decides which company's data you see and what you may do.
Sending the key
GET /v1/invoices HTTP/1.1
Host: api.officehero.io
Authorization: Bearer oh_live_a1b2c3d4e5f6_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
A key is oh_live_, a 12-character prefix, _, and a 43-character secret.
The prefix is not secret: it identifies the key in the list under
Settings → API keys and is what you quote to support. The secret is shown
once, when the key is created; OfficeHero stores only a hash of it.
There are no test or sandbox keys. Every key works against your real company data, so give each one only the scopes it needs.
Creating a key
Tenant owners and admins (the identity.api_keys.admin permission) create keys
in the Fleet, HR or Finance app under Settings → API keys:
- Name — what the key is for. You will see it in the key list and audit log.
- Scopes — the permissions the key carries. A request to an
endpoint whose scope the key lacks is refused with
403. - Expires — optional. After this time the key stops working.
The key list shows each key’s prefix, scopes, creation time, last used time (updated at most once a minute) and status (active, expired, revoked). The first time a new key is used, the company’s admins are notified.
int:* — every scope, every webhook event family, every webhook endpoint of
the company — is not offered in Settings → API keys. Ask
support if your integration needs it.
When authentication fails
A missing, malformed, unknown, expired or revoked key gets the same response, so the API never tells a caller which of those it was:
{
"type": "https://officehero.io/errors/unauthorized",
"title": "Unauthorized",
"status": 401,
"detail": "Authentication required. Provide a valid API key.",
"detail_key": "errors.integrations.unauthorized"
}
Check, in order: the header is exactly Authorization: Bearer <key>; the key
was copied whole; it has not expired or been revoked in Settings → API keys.
Replacing a key
Revoking a key takes effect immediately, so replace it in this order:
- Create a second key with the same scopes.
- Deploy your integration with the new key and confirm it works (the new key’s last used time starts moving).
- If the old key owns webhook endpoints, create them again with the new key and delete the old ones. Revoking a key — or letting it expire — pauses its endpoints, and they cannot be resumed while it is revoked or expired.
- Revoke the old key.
Personal data the API does not return
An employee’s name and e-mail address, a leave request’s free-text reason
and a shared document’s recipients are always null (or []) over REST —
the same data over webhooks needs an explicit personal-data permission. See
Data conventions.
Keeping keys safe
- Keep keys on your servers: a secret manager or environment variable. Never put a key in a browser page, a mobile app or a public repository.
- Use one key per integration and environment, so you can revoke one without touching the others.
- Prefer keys with an expiry date, and with read-only scopes when you only read.
- If a key may have leaked, revoke it at once in Settings → API keys.
“Try it” on this site is the one exception to “never in a browser”: the key
you paste into the API reference stays in that tab’s memory,
is sent only to api.officehero.io, and is gone when you reload. Use a
dedicated, short-lived key for it.
Other credentials
The Integrations API accepts only oh_live_ keys. Tokens for the
MCP server and OAuth apps start with oht_ and do
not work here — and API keys do not work there.