Docs menu

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:

  1. Create a second key with the same scopes.
  2. Deploy your integration with the new key and confirm it works (the new key’s last used time starts moving).
  3. 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.
  4. 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.