---
name: hibob-oauth-app
description: Establishes which HiBob (Bob) credential a connector can actually get and obtains it — the per-customer service user ID and token that any integration can use today, or the partner-gated OAuth 2.0 client ID and secret that only approved HiBob Marketplace and technology partners are issued — with the service-user permission-group runbook, field-level permissions and the silent-omission trap that returns 200 OK with missing data, token lifetimes and rotation, sandbox, rate limits and the WAF block on repeated 401s. Use when asked to get HiBob OAuth credentials, register a Bob app in the HiBob Developer Portal, become a HiBob Marketplace partner, walk a customer through creating a Bob service user, or fix a Bob auth error like an empty employee list, missing fields on a 200, a 403 on every call, or a refresh token that stopped working. For any other vendor's developer portal, use that vendor's skill instead.
---

# HiBob (Bob) API Credentials

**Read this first: HiBob has two live credential models, and the one a multi-tenant platform is most likely allowed to
use is not OAuth.**

1. **Service user ID + token, over HTTP Basic auth.** Created by a Bob admin **inside each customer's own Bob
   account**, in the Service Users section. Needs no relationship with HiBob at all. HiBob's own wording: service users
   "aren't tied to actual employees and can't log in to Bob. They exist only to authenticate API requests using their
   ID and token." This is a **per-customer credential** — there is no tenant discovery, no consent screen and no
   refresh. For a platform connecting many customers' Bob accounts, the deliverable per customer is an **admin
   runbook** (§3), not a registration.
2. **OAuth 2.0 authorization code, with a client ID and secret issued by HiBob.** HiBob states it plainly and
   repeatedly: "OAuth 2.0 app integrations are available for approved HiBob Marketplace and technology partners only."
   There is no self-serve registration form. The Developer Portal at `developers.hibob.com` is reached by **invite**
   — HiBob's own quick start says to "Create a Developer Portal account at https://developers.hibob.com using the
   received invite", after your team signs the T&Cs. If the ask is "get HiBob OAuth credentials this week" and no
   partnership exists, the honest answer is that it is not available, and the run should stop at §5.

A third model, **API Access Tokens**, is dead. HiBob removed the ability to create them on 2024-06-15 and stopped
supporting them on 2024-10-31. Any runbook that tells a customer to open **System Settings > REST API** and generate a
token is describing a tile that no longer exists.

Note the tension, and say it out loud rather than resolving it silently: HiBob's API reference tells "Marketplace
partners **and third-party vendors**: Use OAuth for partner integrations" — while OAuth is gated on partnership.
HiBob's own migration guide, however, explicitly contemplates a customer's **vendor-built custom integration** running
on a service user, and tells customers to ask that vendor to move to "the Basic authorization method". So the
service-user route is documented and legitimate for a non-partner vendor; the OAuth route is where you end up if you
want a Marketplace listing and a consent flow instead of an admin runbook.

Three things cost real time whichever model you land on. **A service user starts with no permissions at all** and a
credential with the wrong permission group returns `200 OK` with fields quietly missing, or an empty response — never
an auth error (§4); this is the single most misdiagnosed Bob problem. **`POST /people/search` has no pagination** and
is capped at 50 requests per minute, so a naive full-directory sync is both a huge payload and a rate-limit problem
(§10). And **repeated `401`/`403` responses get your egress IP blocked by HiBob's WAF for five minutes** — on a
multi-tenant platform, one customer's bad credential can take out every other customer's sync (§10).

## Inputs to collect before you start

Ask in one batch. Never invent.

| Input | Notes |
| --- | --- |
| **Which credential model** | Per-customer service users, partner OAuth, or both (§1) |
| **Partner status** | Is there an accepted HiBob Marketplace/technology partnership and Developer Portal access? (§5) |
| **New app or edit to an existing one** | A new client ID orphans every existing OAuth installation (§2) |
| **App name, icon (160×160, 1:1), description, "Built by"** | Shown in the Bob Marketplace listing (§6) |
| **Installation mode** | "Install from Bob Marketplace" or "Install from your landing page" — this decides whether you get `state` back (§6) |
| **Redirect URI(s)** | Every callback host, exact strings; must work for logged-out users (§7) |
| **Scope set** | The exact HiBob scope strings the connector needs (§8) |
| **Which Bob admin** at each customer | Only a Bob admin can create a service user and its permission group (§3) |
| **Which employee fields the integration reads** | Drives the permission group, and the sensitive-field View+Edit rule (§4) |
| **Does the customer use Bob Sandbox?** | A separate paid environment on a different API host (§10) |

## Quick Start

1. Establish which credential model is actually reachable (§1). Most wrong turns start here.
2. If per-customer service users: decide reuse vs. a fresh credential per customer (§2), then run the **admin
   runbook** with each customer's Bob admin (§3).
3. Pin the permission group to the exact fields you read, including the sensitive-field rule (§4).
4. If partner OAuth: apply to the partner program — this gates the Developer Portal, the app, and everything after
   it (§5).
5. Create the app in the Developer Portal: basic info, installation mode, scopes, redirect URI, webhooks (§6).
6. Register **every** callback host, exactly (§7).
7. Select scopes from HiBob's published mapping, minimum set only (§8).
8. Capture development credentials, then production credentials at go-live; know what rotation breaks (§9).
9. Check rate limits, the WAF block and sandbox before promising a sync cadence (§10).
10. Verify end to end against a real tenant (§11) and hand off without committing a secret (§12).

## Platform state (verified 2026-09-20 — re-verify before trusting)

If the Developer Portal, Bob's Service Users screen or the docs do not look like this, stop and report what you
actually see rather than clicking on.

- **OAuth 2.0 is partner-gated, not self-serve.** "OAuth 2.0 app integrations are available for approved HiBob
  Marketplace and technology partners only." Customer-built integrations are told to use service users. There is no
  public app-registration form.
- **The Developer Portal is invite-based.** HiBob's quick start: open a Bob **partner** account at `app.hibob.com`,
  then create a Developer Portal account at `developers.hibob.com` "using the received invite". Your team must first
  sign the T&Cs. Technical contact `partnersupport@hibob.io`; business contact `partnerships@hibob.io`
  (HiBob's API Terms give `partnerships@hibob.com` for the "become a partner" route).
- **The partner application is a form on HiBob's site.** `https://www.hibob.com/partner/` lists four programs; the
  Tech Marketplace one ("Have a key workplace tool… Apply to join our Marketplace") links to a hosted application
  form. **HiBob publishes no tier table, no mutual-customer bar, no fee and no review SLA** for it — do not invent
  one, and plan in months.
- **Marketplace apps must use OAuth only.** The Developer Portal quick start: "Use only OAuth 2.0. Other
  authentication methods are not allowed for Marketplace apps."
- **API Access Tokens are gone** — creation removed 2024-06-15, support ended 2024-10-31. Service users replaced them,
  and the stated reason is that the old tokens inherited a specific employee's permissions.
- **Token lifetimes are short and the docs contradict themselves on one of them.** Access token: **5 minutes**.
  Refresh token: the "Token management best practices" section says **30 days**; the token-response table on the same
  page says the refresh token "will expire if it isn't used in 60 days". **Treat 30 days as the number**, refresh well
  inside it, and do not build on the 60.
- **Refresh tokens rotate.** HiBob's FAQ: "Each successful call to the refresh endpoint returns a new `refresh_token`,
  and the previous one stops working once you've used it."
- **`state` is not returned in "Install from Bob Marketplace" mode.** HiBob: "in that mode Bob starts the OAuth flow
  directly — you never send the initial authorization request, so there is nothing to attach `state` to and none is
  returned on your callback." `state` exists only in "Install from your landing page" mode. Any connector whose
  callback routes on `state` must register the landing-page mode (§6).
- **There is no uninstall webhook.** "HiBob does not send a separate uninstall webhook or notification." You detect an
  uninstall when a refresh returns `401`.
- **Scopes and audience are locked at install.** "Updating app permissions (re-consent) is not supported. Uninstall
  and reinstall is required to change scopes or people data access." New custom fields inside an already-approved
  scope are included automatically.
- **Field-level permissions have rolled out to nearly all customers.** The permission section formerly called
  **People** is now **People's data > People's fields**, and access can be granted per field rather than per category.
  Sensitive fields (SSN, full date of birth, age) still require **both View and Edit** to be returned by the API.
- **Bob's MCP server now uses OAuth on the signed-in Bob user's permissions**, and its documentation moved to the Bob
  Help Center. That is a separate end-user product surface, **not** a credential route for a server-to-server
  connector. Do not present it as one.
- **The Help Center (`help.hibob.com`) blocks automated fetching** (403 to scripted requests on 2026-09-20). The
  customer-facing articles — *Manage service users*, *Create a service user permission group*, *View API audit logs* —
  are linked from HiBob's developer docs and are readable in a browser. Cite them by title; a human must open them.

## 1. Which credential, and who issues it

| Credential | Who issues it | How it is sent | Reach |
| --- | --- | --- | --- |
| **Service user ID + token** | A Bob admin, inside the customer's own Bob account | HTTP Basic: `Authorization: Basic base64(<ID>:<token>)` against `https://api.hibob.com/v1/` | Exactly one Bob company. Data limited by the permission group's **Features**, **People's fields** and **Access data for** settings (§4) |
| **OAuth 2.0 access token** | HiBob, to approved partners, as `client_id` + `client_secret`; the customer then installs the app | `Authorization: Bearer <access_token>`, same API host | One Bob company per installation, keyed by `companyId`. Limited by approved **scopes** *and* the **audience** the customer picked at install |
| **API Access Token** | — | — | **Dead.** Creation removed 2024-06-15, support ended 2024-10-31 |

Two consequences worth stating before anyone starts building:

- **Neither credential is multi-tenant by itself.** A service user reaches one company because it lives in that
  company. An OAuth installation reaches one company because HiBob issues one token pair per install, and puts
  `companyId` in the access token as the tenant key. The difference is who does the work: with service users the
  customer's admin does it and emails you two strings; with OAuth the customer clicks Install and your callback does it.
- **Only OAuth gets you a Marketplace listing.** If the goal is "appear in Bob's Marketplace so customers can install
  us in two clicks", that is the partner program (§5) and nothing else will do.

## 2. Reuse or create new

**Partner OAuth.** A new app means a **new client ID**, and every existing installation is bound to the old one —
every customer uninstalls and reinstalls. Reuse the existing app for: adding a redirect URI, changing scopes (note
that a scope change still forces every *customer* to reinstall — §Platform state), rotating a compromised secret, or
diagnosing a failure. Register a new app only for a genuinely separate product or a deliberate migration. Say which
path you are taking before touching the portal.

**Service users.** Much gentler: each customer's credential is independent, so regenerating one affects exactly one
tenant. Prefer **one dedicated service user per integration per customer**, with its own permission group — HiBob
recommends exactly this ("create a dedicated permissions group for each service user"). Never ask a customer to reuse
the service user their payroll vendor already has; you inherit its permissions and its blast radius.

## 3. The customer runbook — creating a service user

This is the deliverable for each customer on the service-user model. Give it to their **Bob admin**; you cannot do any
of it from outside. HiBob's own five steps, with the parts that actually go wrong called out.

1. **Create the service user.** In Bob, go to the **Service Users** configuration page — HiBob's docs say only that
   "Service Users are created and managed in the Service Users section in Bob" and link the Help Center article
   *Manage service users* for the click path; a path of Settings → Integrations → Automation → Service Users is
   reported from the field but is **not** confirmed on a HiBob page, so have the admin confirm what they see. Create a
   new service user and **copy the ID and the token immediately** — HiBob: "You can only copy the token when it's
   generated. If lost, generate a new one and update your code accordingly." Give it a description naming your
   integration; the Service Users table shows creator, last session, last token refresh and assigned permission
   groups, and exports to CSV/XLSX.
2. **Create a dedicated permission group** and add the service user to it. **By default, service users have no access
   permissions.** A brand-new group has none either. A credential created and handed over at this point authenticates
   perfectly and returns nothing useful.
3. **Grant permissions**, in three separate places (§4 explains the traps):
   - **Features** — switches on whole areas of Bob (for example **Features > Reports**, or **People > Add new people
     to the company** if you create employees).
   - **People's data > People's fields** — **View** / **Edit** per category *or* per field, and **View history** on the
     category row only.
   - **People's data > Access data for** — *whose* records the service user may see. Default is all **active**
     employees, via a **Lifecycle status equals Employed** condition. To read terminated or former employees the admin
     must **Select people by condition** and remove that condition.
4. **Test** from HiBob's API reference "Try It!" pane: pick the Base URL (Production or Sandbox), choose **Basic**
   auth, put the **service user's ID in the username field and the token in the password field**. HiBob's own note on
   that page is the tell: "If you receive an **empty response** it likely means the permissions do not allow you to
   access the data you are trying to fetch."
5. **Hand over the ID and the token.** They are equivalent to a password — see §12 for how not to leak them.

**What to tell the customer about lifetime.** HiBob documents **no expiry** for a service user token: it works until
someone refreshes it or deletes the service user. Deleting the service user breaks every integration using it. The
Service Users table records a "last token refresh" date, so rotation is a deliberate admin action, not a clock. Ask
for a named owner at the customer, because an admin quietly deleting an unlabelled service user is a common cause of a
connection that "suddenly stopped".

**A service user is not an employee and consumes no licence** — that is HiBob's stated reason for the model, and it is
the right answer when a customer objects to "creating another user".

## 4. Permissions: the section that prevents the misdiagnosis

**Bob answers with `200 OK` and simply leaves out what the credential may not see.** HiBob's People read contract
names this **silent omission** and tabulates it:

| Situation | HTTP | Result |
| --- | --- | --- |
| Field requested, service user lacks category or field permission | `200` | Field **absent** |
| Field requested, unknown or invalid field ID | `200` | Field **absent** |
| `fields[]` empty or omitted | `200` | **Default field set** — categories `root`, `about`, `employment`, `work`, still subject to permissions |
| Filter `fieldPath` not `root.id`/`root.email`, wrong operator, empty values | `400` | Error — filters fail loudly, fields do not |

So: **fields fail silently, filters fail loudly.** After every `200`, compare what you asked for against what came
back. HiBob's own checklist: verify the field ID in metadata → verify its `categoryId` → verify **View** on that
category or field under **People's fields** → verify **Access data for** covers that employee.

Five rules that follow, in the order they bite:

1. **The default field set is small.** `POST /people/search` with no `fields[]` returns only the `root`, `about`,
   `employment` and `work` categories. To read basic employee data at all, the permission group needs **View** on
   those four categories (or the specific fields inside them). Anything else — personal contact details, payroll,
   custom tables — is a separate grant. HiBob's Help Center article on this is *Permissions for Default Employee
   Fields in People Search API*.
2. **Field-level, not category-level.** Permissions now live under **People's data > People's fields**; categories are
   containers and act as defaults, and a single field can be opened without opening its category. Ask customers for
   the **exact fields** you read, not whole categories — HiBob asks partners to write setup instructions that way.
3. **Sensitive fields need View *and* Edit.** SSN, full date of birth, age and similar are enforced by hard-coded
   logic: "the service user must have both **View and Edit** on that field" for the value to come back over the API.
   A read-only integration still has to ask for Edit on those fields, which is a conversation to have up front rather
   than in an incident. During migration, a service user that had View but not Edit on them lost access entirely.
4. **History is a separate permission.** **View** returns the current effective row; **View history** (available on
   the **category** row only, never per field) is what unlocks the historical table endpoints — work, employment,
   salaries, lifecycle.
5. **Employee-table endpoints tell you what they hid.** When columns are filtered, the response carries an
   `X-Has-Restricted-Columns: true` header and a `restricted_columns` object splitting them into
   `no_view_permission` and `no_view_history_permission`. Read it — it is the one place Bob distinguishes "no value"
   from "not allowed". The people search endpoints do **not** give you this; there you are on your own with the
   checklist above.

**Field IDs are stable identifiers, not paths.** `work.department` looks like `category.field` but an admin can move
that field to another category and the ID will not change — only `categoryId` in metadata does. Always resolve the
category from `GET /company/people/fields`, never from the ID string, before telling a customer which box to tick.

**On OAuth the same problem arrives wearing a different hat.** Scopes are only the first gate; the customer also picks
an **audience** at install — All company, select by condition, or select by name. HiBob's guidance to partners is
blunt: "A request to list employees may return a subset of the total number of employees. Never assume a successful
API response contains the full organization," and an employee outside the audience returns `404` (or `403`) even with
valid scopes. The audience cannot be changed without uninstall and reinstall, so tell customers in your install
instructions which setting your product needs.

## 5. Becoming a partner (the only route to OAuth)

`https://www.hibob.com/partner/` carries four programs; the one that leads to OAuth is **Tech Marketplace**
("Have a key workplace tool that helps organizations operate more efficiently and scale in snap? Apply to join our
Marketplace"), which links to a hosted application form. HiBob's API Terms add: "If you would like to become an
official partner of HiBob, please contact partnerships@hibob.com, or submit a 'Become a partner' form on our website."

What the run needs to know:

- **HiBob publishes no eligibility bar, no fee, no timeline.** No mutual-customer minimum, no security-questionnaire
  list, no SLA appears on the public pages. Do not invent any of it, and do not fill in revenue, volume, security or
  compliance claims on the user's behalf — collect the questions and hand them back (§Stop and ask).
- **Acceptance is what unlocks everything else**: a Bob **partner company** at `app.hibob.com`, a Developer Portal
  invite, the T&Cs, and with them the app, the scopes, the test-install flow and eventually the listing.
- **The API Terms are worth reading before signing anything.** They license API use "in your test environment for
  internal development and testing" and state you "shall have a 'read only' access" absent express permission,
  reserve the right to set and change rate limits without notice, and require notification of a security incident
  within 48 hours. Anything your product does that writes to Bob is a point to confirm, not to assume.

Anything a human must do — signing T&Cs, accepting the invite, opening the partner Bob company, submitting the
certification video — hand back rather than looping.

**If this session has no browser automation** (the usual case for a CLI or cloud run), do not pretend to click. Give
the user an exact ordered click path with the literal values to paste — the redirect URIs from §7 and the scope
strings from §8 — or a ready-to-send application body for §5, and continue once they report back with the client ID.

## 6. Create the app in the Developer Portal

Sign in at `developers.hibob.com`, then **My apps → Create app**. What the form asks, and what carries more weight
than it looks:

- **App icon** (160×160 px preferred, 1:1), **App name** as it appears in the Marketplace, **App description**
  ("1–2 sentences"), **Built by**.
- **Installing your app** — the installation mode, and **this is the decision to get right the first time**:

| Mode | What happens when the customer clicks Install in Bob Marketplace | `state` |
| --- | --- | --- |
| **Install from Bob Marketplace** | The OAuth consent flow starts in Bob; Bob calls your redirect URI with a code | **Not returned** — you never sent the authorize request |
| **Install from your landing page** | Bob sends the customer to your HTTPS landing page; after your onboarding you start OAuth from your **App Installation URL** | **Returned**, because you sent it |

  A connector that binds the callback to a pending connection via `state` — which is how most multi-tenant platforms
  route a callback to the right customer — **must** use *Install from your landing page*. In Marketplace mode you have
  to exchange the code, store the tokens against `companyId`, and attach them to a customer account after the user
  signs in on your side. Both modes still list your app in the Marketplace.
- **OAuth → redirect URI** (§7) and **Manage scopes** (§8). "Expand each topic and select the actions your app needs";
  each scope "includes current and future data".
- **Webhooks** (optional): endpoint URL, a **Validate** click that must get a `200` back, and a generated **Secret**
  to copy. Bob auto-subscribes each installing company to the app's configured webhooks during authorization. Partner
  webhook delivery is a different model from customer webhooks — route events by `companyId`.
- **Test**: **OAuth → Test your app installation → Test from Bob Marketplace → Install** verifies consent and redirect;
  for the full journey, your partner Bob company shows Developer Portal apps under **Bob products → Marketplace →
  Manage apps → In development** with a **Dev mode** badge. Dev-mode apps are visible **only on your partner company**.
  Uninstall during development is a button in the same portal panel; in production customers uninstall in Bob.
- **Submit for technical review**: HiBob asks for a certification video, "5–6 minutes", demo data only, with voiceover
  or captions, on an unlisted link, **recorded within 14 days of submission**, showing the OAuth install flow for your
  mode, the initial data sync, the relevant lifecycle or event-driven flows, and the uninstall.

## 7. Redirect URIs

HiBob's description: the redirect URI "is where the authorization server sends the user after they approve the
installation in Bob", and **it must work for users who are not logged in yet** — Bob may land a customer on your
callback before they have an account with you. The authorization `code` expires in **5 minutes**, so exchange it
server-side immediately, store the tokens keyed by `state` (landing-page mode) or by `companyId`, and do the account
linking afterwards.

Register **every** callback host your platform serves. For Unified.to these are one per data center; confirm the
current list with the platform owner rather than assuming:

```
https://api.unified.to/oauth/code          # us (default)
https://api-eu.unified.to/oauth/code       # eu
https://api-au.unified.to/oauth/code       # au
https://api-dev.unified.to/oauth/code      # dev
```

Notes that each cost a cycle:

- The same `redirect_uri` must appear in the installation URL **and** in the `authorization_code` token exchange,
  where it is a required body parameter. HiBob does not publish its matching rules — assume exact string matching, no
  trailing-slash forgiveness.
- **Whether one app accepts several redirect URIs is not documented publicly.** The portal field is described in the
  singular. If it turns out to accept only one and your platform needs four, that is a finding to report — a separate
  app per data center, or a single shared callback host, is an architecture decision, not something to improvise.
- Missing the redirect URI in the portal produces a named error at install time:
  `hibob.marketplace.error.bad.request.redirect.url.missing` (§11).

## 8. Scopes

Scopes are **space-separated** in the installation URL. HiBob's own example query string spells the parameter
`scope=<space-separated-list-of-scopes>` while the parameter table immediately below it says `scopes` is required —
**the documentation is inconsistent on the same page**. Send what the portal's generated App Installation URL contains
and verify against a real install rather than reasoning about it; sending both keys is a defensible belt-and-braces
move, but test it.

HiBob publishes a complete **Scopes mapping to endpoints** page — read the row for each endpoint you actually call.
The families an HRIS/ATS/time-off connector reaches for:

| Scope | Covers |
| --- | --- |
| `employee_data:read` / `employee_data:write` | `/v1/profiles`, people search, custom tables, employment data; write covers profiles, work history, training, avatars |
| `employee_data.history:read` | Bulk people endpoints — employment, lifecycle, work history |
| `employee_data.sensitive:read` / `:write` / `.history:read` | Bank accounts, salaries, equities, variable pay, payroll history |
| `company.metadata:read` / `:write` | Company lists, field definitions, configuration metadata |
| `employers:read` | Employer API — employers, work locations, their metadata |
| `timeoff:read` / `timeoff:write` / `timeoff.sensitive:read` / `timeoff.calendars:write` | Requests, balances, whosout; private-policy data is its own scope |
| `attendance:write` | Time entries, clock operations, summaries |
| `projects:write` | Attendance projects and project tasks |
| `tasks:write` | Task read **and** write |
| `documents:read` / `documents:write` | Employee documents, folders, eSign |
| `goals:read` / `goals:write` | `goals:read` is only goal-cycles search; **goal and key-result search live under `goals:write`** |
| `hiring:write` / `hiring.integrations:write` | The whole Bob Hiring search surface — job ads, openings, candidates, applications, interviews, evaluations, offers |
| `job_catalog:read`, `job_catalog.skills:read` / `:write` | Job families, roles, profiles, competencies, skills |
| `reports:read`, `workforce_planning:read` / `:write`, `learning.integrations:write`, `payrollhub.*` | Reports, positions and budgets, LMS, payroll hub |

Two traps specific to this catalog:

- **Several read operations only exist under a `:write` scope.** `hiring:write` is documented as covering "Search and
  read hiring data" with no `hiring:read` twin; `tasks:write` covers the GETs; `projects:write` covers project reads;
  goal search sits under `goals:write`. A read-only integration will therefore be asking a customer to approve
  write-shaped scopes. Say so in your install instructions before a security reviewer finds it.
- **Scopes never widen what the audience allows** (§4). Approve the minimum set, and remember that changing it later
  forces every existing customer to uninstall and reinstall.

## 9. Capture the credentials

### Partner OAuth

From the Developer Portal you get **development credentials** first — `client_id`, `client_secret`, and an **App
Installation URL for testing** generated from your app ID, redirect URI and scopes (it already carries `mode=dev`).
At go-live, **OAuth → Production credentials** gives the production **Client ID** and **Client secret** and the
production App Installation URL. Record, alongside them:

- Installation URL parameters: `app_id`, the scope list, `redirect_uri`, optional `state`, and `mode=dev` **only** for
  development credentials.
- Token and refresh endpoint: `POST https://auth.app.hibob.com/oauth2/v1/apps/token`, `Content-Type:
  application/x-www-form-urlencoded`. HiBob recommends HTTP Basic with `client_id:client_secret`; sending them as body
  parameters is also documented.
- API host: `https://api.hibob.com/v1/…` with `Authorization: Bearer <access_token>`.

```
{ "access_token": "...", "token_type": "Bearer", "expires_in": ..., "refresh_token": "...", "id_token": "..." }
```

Four facts to write down where whoever operates this will see them:

- **`app_id` is not `client_id`.** HiBob says so twice. `app_id` appears only in the installation URL and plays no
  role in the token exchange. `client_id` is issued at registration and is what the token endpoint wants.
- **`companyId`, not the installer's email, is the tenant key.** It arrives as a custom claim inside the access token
  and in webhook payloads, and it is stable across re-installs. HiBob: "Do not use the installer's email alone as the
  tenant key."
- **Store the rotated refresh token every time.** If refreshes work for weeks and then start failing with `401` on
  `/token`, you are reusing an old one.
- **Development and production credentials are not interchangeable.** "A token created with development client ID &
  client secret cannot be refreshed with production credentials, and vice-versa."

**Rotating the client secret invalidates every previously issued refresh token, and every customer must reinstall.**
HiBob spells this out under compromised credentials. A compromised access or refresh token affects one company; a
compromised client secret affects all of them, and the instruction is to contact `partnersupport@hibob.io` with the
App ID. Never rotate without explicit go-ahead and a cutover plan.

### Service user

Two strings per customer: the **ID** and the **token**, combined as `ID:token`, Base64-encoded, sent as
`Authorization: Basic <encoded>`. That is the whole scheme — no subdomain, no tenant parameter, no refresh. The API
host is the same `https://api.hibob.com/v1/` for every customer, which means **the credential is the only thing
identifying the tenant**: mixing two customers' service users up is a data-leak-shaped bug with no host to catch it.

The token cannot be re-read after creation. A lost token is replaced, not recovered.

## 10. Rate limits, the WAF, sandbox

**Rate limits are per endpoint, per minute**, and HiBob publishes a table for each module. The ones that shape an HRIS
sync:

| Endpoint | Limit / minute |
| --- | --- |
| `POST /people/search` | 50 |
| `POST /people/{identifier}` | 100 |
| `GET /profiles` | 40 |
| `PUT /people/{identifier}` (update), `POST /people` (create), terminate | 10 |
| Docs API | "No rate limits are implemented at the moment" |

Responses carry `X-RateLimit-Limit`, `X-RateLimit-Remaining` and `X-RateLimit-Reset` (Unix epoch); exceeding a limit
returns `429`, and HiBob's error page shows `Retry-After` alongside the rate-limit headers on that response. Back off
to the reset, do not retry blind.

**`POST /people/search` has no pagination.** No `limit`, no `cursor`, no `next_cursor` — one call returns *every*
employee the credential can see. HiBob's prescribed pattern for large companies is client-side batching: first a
lightweight call with `fields: ["root.id"]` and no filter to discover IDs, then repeated calls with the full field
list and a single `root.id` `equals` filter carrying **50–200 IDs**. Search accepts at most **one** filter object, at
most **400** field IDs, and only `root.id` / `root.email` as `fieldPath` with `equals`. Terminated employees require
`showInactive: true` **and** a permission group whose **Access data for** condition does not restrict to Employed.

Budget accordingly: at 50 calls/minute, a connector that spends two search calls per page of employees tops out at
about 25 pages a minute, before anything else on the account competes for the same budget.

**The WAF block is the multi-tenant hazard.** Separately from rate limits, HiBob blocks the **source IP for 5
minutes** when it sees, within 10 seconds, more than 50 responses of `401` from the same IP, or more than 50 `401`s
against the same Authorization header, or more than 50 `403`s from the same Authorization header. HiBob's instruction
is explicit: "Do **not** retry failed `401` or `403` requests. Instead, stop calling the API for those credentials and
check them with the client." On a platform where hundreds of tenants share an egress IP, a single customer whose
service user was deleted can generate enough `401`s to take every other tenant offline for five minutes. Fail a
connection fast and mark it broken; do not let a retry loop anywhere near a `401`.

**Sandbox is a customer purchase, not a developer perk.** Bob's Sandbox is a separate environment on a separate host —
HiBob's OpenAPI lists `https://api.hibob.com/v1` (Production) and `https://api.sandbox.hibob.com/v1` (Sandbox) — and
"the Sandbox capability is available only for accounts that have purchased Sandbox". A connector that hardcodes the
production host cannot connect a customer who wants to test against their sandbox. For partners, the test environment
is instead your **partner Bob company** in Dev mode (§6); there is no free developer tenant otherwise.

**Audit.** Bob exposes Public API usage in-product ("View API audit logs" in the Help Center). When a customer
disputes what your integration read, that is where the conversation goes, not your logs.

## 11. What this platform's connector expects

**Dated observation — 2026-09-20.** The connector supports **both** credential models. Treat every line here as a
snapshot and **confirm it with the connector's owner before registering anything**; connector configuration changes
more often than a vendor's docs do.

- **Service-user mode** asks the customer for two inputs, labelled *Service User ID* and *Service User Token*, and
  points them at Bob's Settings → Integrations → Automation → Service Users. The two values are sent as HTTP Basic
  with the ID as username and the token as password — which is exactly HiBob's documented `base64(ID:token)` scheme.
  The API host is `https://api.hibob.com/v1/`.
- **OAuth mode** starts installation at `https://app.hibob.com/api/marketplace/apps/install`, with a second
  "Development Environment" variant that appends `mode=dev`, and exchanges and refreshes at
  `https://auth.app.hibob.com/oauth2/v1/apps/token` with a form-encoded POST. It requires a developer-supplied **App
  ID** in addition to client ID and secret, and sends it as `app_id`. It sends `redirect_uri` and `state`, and emits
  the scope list under **both** `scopes` and `scope` with spaces percent-encoded — a direct response to the doc
  inconsistency in §8. It records the access-token lifetime as **5 minutes** and the refresh-token lifetime as **30
  days**, matching HiBob's best-practices section.
- **Its scope map is per object and per direction**, and it already reflects the read-via-`:write` quirk: `hiring:write`
  for every ATS object, `projects:write` for projects and project tasks, `tasks:write` for ticketing objects,
  `goals:write` for goals, plus `employee_data:read`/`:write`, `employee_data.sensitive:read` for bank accounts,
  `employers:read` for company, `company.metadata:read`/`:write`, `timeoff:read`/`:write` and `documents:read`.
- **Employee listing already follows HiBob's prescribed batching**: an ID-only search first, then a filtered detail
  search over that page's IDs with `humanReadable: "APPEND"`. It sets `showInactive` only when terminated data is
  requested.
- **It records a partner-application link and `partnersupport@hibob.io` as the partner contact** — consistent with
  the application route on HiBob's own partner page.
- **Four things to check with the owner before trusting an OAuth install.** (1) Which **installation mode** the
  Developer Portal app is registered under — the connector sends and expects `state`, which HiBob does not return in
  *Install from Bob Marketplace* mode (§6). (2) Whether the **rotated refresh token is persisted on every refresh**
  (§9). (3) Whether `app_id` is kept out of the **token exchange**, where HiBob does not want it (§9). (4) Whether a
  `401` anywhere in the stack can trigger a retry, given the WAF rule (§10).
- **No sandbox host is configured.** A customer on Bob Sandbox (`api.sandbox.hibob.com`) cannot be connected as
  things stand (§10).

## 12. Verify end-to-end

Getting a token back proves almost nothing here. Test what a customer's tenant actually returns.

1. **Authenticate and read one employee.** `POST /people/search` with `fields: ["root.id"]` and no filter. Count the
   results and **compare the count to the customer's headcount**. A short list is a permission-group problem, not an
   empty company.
2. **Ask for every field you map, then diff the response against the request.** Missing keys on a `200` are the
   silent omission (§4). Walk the checklist — metadata field ID → `categoryId` → **View** on that field or category →
   **Access data for**.
3. **Test one sensitive field** if you map any (SSN, full DOB, age). It comes back only with **View and Edit**.
4. **Test terminated employees** with `showInactive: true`, and confirm the permission group's **Access data for**
   condition was loosened — the flag alone is not enough.
5. **Read one employee-table endpoint** and check for `X-Has-Restricted-Columns` / `restricted_columns`.
6. **On OAuth: refresh, then refresh again with the token the first refresh returned.** That is the two-minute test
   for a client that ignores rotation. Then confirm that a job running longer than five minutes re-checks expiry
   *during* the loop, not just before it.
7. **On OAuth: run a full install from a second Bob company**, and confirm your callback routes it to the right
   customer using `companyId` (and `state`, if your mode returns one).
8. **Watch `X-RateLimit-Remaining` during a real sync**, not a single call.

| Symptom | Cause |
| --- | --- |
| `200 OK` but fields you asked for are missing | Silent omission: no **View** on that field/category, or an invalid field ID. Not an error, not a mapping bug (§4) |
| Empty response from a "Try It!" or a first call | HiBob's own note: "the permissions do not allow you to access the data you are trying to fetch" (§3) |
| Employee list is a subset of the company | Service user: **Access data for** scope. OAuth: the **audience** the customer chose at install (§4) |
| Terminated employees missing although `showInactive: true` | The permission group still carries **Lifecycle status equals Employed** (§3) |
| SSN / full date of birth / age always absent | Those fields need both **View and Edit**, even for a read-only integration (§4) |
| Historical table rows empty, current values fine | **View history** on the category was never granted (§4) |
| `403` on every call to one module | Missing scope or Feature permission — **or the source IP is not on the company's IP trust list** (§4, §10) |
| `404` on a specific employee, valid scopes | That employee is outside the installed audience — HiBob documents `404` (or `403`) for this (§4) |
| `404` on a whole module | That module is not purchased/activated for the company (e.g. Time & Attendance, Bob Hiring) |
| `401` on every call, credential looks right | Wrong Basic composition (`ID:token`, in that order), or the service user was deleted/refreshed (§9) |
| All tenants fail for ~5 minutes at once | WAF block from repeated `401`/`403` on a shared egress IP. Stop retrying auth failures (§10) |
| `429` | Per-endpoint per-minute limit. Back off to `X-RateLimit-Reset` (§10) |
| Refresh worked for weeks, now `401` on `/token` | The rotated refresh token was not persisted, or 30 days of inactivity elapsed, or the customer uninstalled — there is no uninstall webhook (§9) |
| Refresh fails well inside 30 days | Development credentials refreshed with production ones, or vice-versa (§9) |
| Long sync fails partway with `401` | The access token expired 5 minutes in; refresh *during* the loop (§9) |
| `hibob.marketplace.error.bad.request.scopes.missing` at install | No scope defined and saved before the installation URL was copied (§8) |
| `hibob.marketplace.error.bad.request.redirect.url.missing` | Redirect URI not defined and saved before copying the URL (§7) |
| `hibob.marketplace.error.not.found.app.not.exists` | `&mode=dev` missing while using development credentials (§9) |
| Bob login screen instead of the consent screen | Not signed in to the test Bob account before starting the install (§6) |
| No `state` on the callback | *Install from Bob Marketplace* mode does not return one — by design (§6) |
| Customer wants different scopes or a wider audience | No in-place update exists; uninstall and reinstall (§Platform state) |
| Customer says the Service Users screen is not there | They are not a Bob admin (§3) |

## 13. Hand off — never commit the secret

- **Do not** write a client secret, a service user token, a refresh token or a webhook secret into source control, a
  test, a fixture, a committed `.env`, a ticket, a PR body or a chat channel. Values go to the human running this, for
  their secret store or console.
- A service user **ID** is an identifier and may be recorded; the **token** is a password. They arrive in the same
  message from the customer — separate them before you paste anything anywhere.
- If a secret has already been pasted into a transcript, say so plainly and say it can be rotated (service user:
  refresh the token; OAuth: rotate in the Developer Portal, accepting that every customer reinstalls).
- If a code change is needed (a callback host, a scope string, a sandbox host), keep it credential-free and say what
  the human must set out of band.
- Close with: which credential model; for OAuth, the app name, `app_id`, client ID, whether these are development or
  production credentials, the installation mode, the exact scope string and the redirect URIs registered; for service
  users, which customer, which Bob admin created it, which permission group and which fields it was granted; the
  rate-limit budget the connector will actually get; and what is still waiting on HiBob or on the customer.

## Stop and ask

Hand back to a human rather than guessing when: **there is no accepted HiBob partnership** and the ask is OAuth
credentials — say so in the first reply, do not start a registration that cannot complete; the partner application
asks for commercial, security or compliance claims; someone proposes moving live per-customer service-user
connections onto partner OAuth (every customer reconnects, and the audience/scope choice becomes theirs); a customer
must grant **Edit** on sensitive fields for a read-only integration and nobody has approved that; the only testable
tenant is a customer's production Bob and the task involves writes; the Developer Portal accepts only one redirect URI
and the platform needs four; the app's installation mode conflicts with how the callback is routed (§6); a customer
uses Bob Sandbox and the connector has no sandbox host; the `state` / `scope` vs `scopes` inconsistencies in HiBob's
docs have to be resolved by testing rather than reading; or the portal, Bob's Service Users screen or the docs do not
match the **Platform state** section.

## References

Verified 2026-09-20 — every URL below returned HTTP 200. The Bob **Help Center** (`help.hibob.com`) returns `403` to
automated requests, so its articles — *Manage service users*, *Create a service user permission group*, *Permissions
for Default Employee Fields in People Search API*, *View API audit logs*, *Use Bob MCP Server* — are cited by title
only and must be opened in a browser; each is linked from the HiBob developer pages below.

- Developer hub — https://apidocs.hibob.com/
- Getting started with Bob's API (customers → service users, partners → OAuth) — https://apidocs.hibob.com/docs/getting-started
- Start developing with the API (module map, `.md` export trick) — https://apidocs.hibob.com/reference/getting-started-with-bob-api
- API Service users (the five-step setup, permission groups, FAQ) — https://apidocs.hibob.com/docs/api-service-users
- Authorization (Basic header construction) — https://apidocs.hibob.com/reference/authorization
- Permissions — https://apidocs.hibob.com/reference/permissions
- Categories and permissions — https://apidocs.hibob.com/docs/categories-and-permissions
- Employee data API: field-level permissions (sensitive fields, `restricted_columns`) — https://apidocs.hibob.com/docs/employee-data-api-field-level-permissions
- People read API contract (silent omission, default field set, no pagination, batching) — https://apidocs.hibob.com/docs/people-read-api-contract
- People API (required permissions, per-endpoint rate limits) — https://apidocs.hibob.com/reference/people
- Search for employees — https://apidocs.hibob.com/reference/post_people-search
- OAuth 2.0 for partners (flow, tokens, rotation, troubleshooting, FAQ) — https://apidocs.hibob.com/reference/oauth-20
- Scopes mapping to endpoints — https://apidocs.hibob.com/reference/scopes-mapping-to-endpoints
- Marketplace partner integrations (OAuth only) — https://apidocs.hibob.com/docs/marketplace-partner-integrations-oauth-only
- Quick start: add and submit an app — https://apidocs.hibob.com/docs/add-and-submit-an-app
- Test apps in Bob Marketplace (Dev mode) — https://apidocs.hibob.com/docs/test-apps-in-bob-marketplace
- Managing customer installations (`companyId`, `state`, uninstall detection) — https://apidocs.hibob.com/docs/managing-customer-installations
- Getting started with partner webhooks — https://apidocs.hibob.com/docs/getting-started-with-partner-webhooks
- Getting started with webhooks (customer model, v2) — https://apidocs.hibob.com/reference/getting-started-webhooks
- Rate limiting (headers, WAF blocking rules) — https://apidocs.hibob.com/reference/rate-limiting
- Rate limiting best practices — https://apidocs.hibob.com/docs/rate-limit
- Error handling (status codes, error shapes, `Retry-After`) — https://apidocs.hibob.com/reference/error-handling
- Testing (Try It!, Production vs Sandbox base URL, empty-response note) — https://apidocs.hibob.com/reference/testing
- Pagination — https://apidocs.hibob.com/reference/pagination-1
- Employer API (required permission vs scope, IP trust list) — https://apidocs.hibob.com/reference/employers
- Transition from API Access tokens (the retired model, and vendor-built integrations on service users) — https://apidocs.hibob.com/docs/transition-from-api-access-tokens
- Enhanced service user management (audit fields on the Service Users table) — https://apidocs.hibob.com/changelog/enhanced-service-user-management
- Changelog (subscribe before trusting any of the above for long) — https://apidocs.hibob.com/changelog
- API Terms of Use (read-only clause, rate-limit discretion, 48-hour incident notice, partner contact) — https://apidocs.hibob.com/docs/api-terms-of-use
- Documentation index for machine reading — https://apidocs.hibob.com/llms.txt
- HiBob partner programs and the Tech Marketplace application — https://www.hibob.com/partner/
- HiBob Developer Portal (invite required) — https://developers.hibob.com/
- Bob (partner company sign-in, Marketplace, Service Users) — https://app.hibob.com
- HiBob feature list, including Sandbox — https://www.hibob.com/features/
- HiBob Marketplace / integrations directory — https://www.hibob.com/integrations/
