> For the complete documentation index, see [llms.txt](https://herd-security.gitbook.io/herd-security-docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://herd-security.gitbook.io/herd-security-docs/okta/okta-group-and-app-sync.md).

# Okta Group & App Sync

This guide will show how to sync your current Okta groups into Herd.

## Overview

Okta group sync allows for current Okta customers to pull all user group information from their current Okta tenant into Herd. Allowing for organizations to assign trainings, campaigns, events, and stacks, to groups instead of individual users.

Herd can authenticate to your Okta tenant in one of two ways:

* **API service app (recommended)** — an OAuth 2.0 API Services application in your Okta org. Herd holds a private signing key and you register only the public key in Okta. No admin account is involved and nothing expires unexpectedly.
* **API token (legacy)** — a classic Okta API token created by an administrator. Existing connections keep working unchanged; nothing forces a migration.

Both methods appear as tabs under **Okta Configuration Settings** on the Okta integration tile. Pick one and follow the matching section below.

{% hint style="info" %}
Directory sync is a different integration from [Okta Single Sign-On](/herd-security-docs/okta/okta-single-sign-on.md), and it uses a different Okta application. Sync brings your workforce into Herd as **learners**, with no dashboard access, so they can receive trainings and simulations — this is the right way to bring everyone in. SSO only handles sign-in to the Herd web app; whether a sign-in also gives someone the Herd app is your [app-access policy](/herd-security-docs/users/users/app-access.md). Most organizations want both: sync for the workforce, SSO assigned to the handful of people who use the dashboard.
{% endhint %}

## Prerequisites

In order to complete this setup, you'll need:

* Administrator access to your Okta tenant. For the service app you need to be able to create an **API Services** app integration; for the legacy token, permission to create API tokens.
* Access to the Herd admin console with the **Integrations** permission (admins always have it).

## Find Your Okta Domain

The easiest way to find your Okta domain is to look in the URL when logged in to the Okta admin console. It's typically formatted as `company-url.okta.com` (or `company-url.oktapreview.com`). Enter it without `https://`.

## Open the Okta configuration in Herd

1. Log in to the Herd admin console.
2. Select **Settings** in the left side toolbar, then choose **Integrations** in the Settings sidebar — the **Okta** card lives in the **Identity** section.
3. Under **Okta Integration**, open **Okta Configuration Settings**.
4. Choose the **API service app (recommended)** tab or the **API token (legacy)** tab.

<figure><img src="https://2286452423-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FrNdsHrfHsFuaMzCbOD5q%2Fuploads%2FikDvGGZgvutAwEB5GXny%2FScreenshot%202025-09-10%20at%2016.28.27.png?alt=media&amp;token=d5007046-2ab8-4fa5-b62c-54d99a2ac897" alt=""><figcaption></figcaption></figure>

***

## Option A: Connect with an API service app (recommended)

Herd walks you through three numbered steps on the **API service app (recommended)** tab. You'll switch between Herd and the Okta Admin Console as you go.

### Step 1: Signing key

1. In Herd, enter your **Okta Domain**, then click **Generate signing key**. Herd creates an RSA-2048 key pair for your organization; the private key stays encrypted in Herd and is never shown.
2. Herd displays the **Public key (JWK)** and its **Key ID (kid)**. Use **Copy public key** to copy it.
3. In the Okta Admin Console, go to **Applications → Create App Integration** and choose the **API Services** type. Name it something like "Herd".
4. On the new app, open the **Client Credentials** section, set client authentication to **Public key / Private key** with keys saved in Okta, add a key, and paste the JWK you copied from Herd.

### Step 2: Okta API scopes to grant

On the app's **Okta API Scopes** tab click **Grant** for each scope, then under **Admin roles** assign at least **Read-Only Administrator**. Every scope is read-only except the optional `okta.eventHooks.manage`, which lets Herd register its own [event hook](#real-time-directory-changes-okta-event-hook) and nothing else.

| Scope                    | Required                      | Used for                                                                                                                                                                    |
| ------------------------ | ----------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `okta.users.read`        | Yes                           | Directory users, their profiles and enrolled MFA factors (user sync, risk posture)                                                                                          |
| `okta.groups.read`       | Yes                           | Groups and group memberships (group sync, sync scope)                                                                                                                       |
| `okta.apps.read`         | Yes                           | App inventory and app assignments (technology profile, AI-app access)                                                                                                       |
| `okta.userTypes.read`    | Optional                      | Directory user types (service-account detection)                                                                                                                            |
| `okta.schemas.read`      | Optional                      | User profile schema (work-information attribute remapping)                                                                                                                  |
| `okta.eventHooks.manage` | Optional, unticked by default | Registers Herd's event hook, so directory changes arrive in seconds. The app also needs an admin role that may manage event hooks. You can set the hook up by hand instead. |

The same list appears as checkboxes in Herd, with **Copy scopes** to copy them in one go. Untick any optional scope you did not grant in Okta: Okta refuses the whole token request if Herd asks for a scope the app hasn't been granted.

### Step 3: Client ID

1. Copy the **Client ID** from the app's **General** tab (under **Client Credentials**) and paste it into Herd.
2. Click **Save & test connection**. Herd requests a token from Okta and reads one user to prove the key, scopes, and admin role all line up before anything is stored. If the test fails, nothing is saved and the message from Okta is shown so you can fix the app.

{% hint style="info" %}
**Rotating the key.** Click **Rotate signing key** on the tile at any time. Okta will reject Herd until you register the new public key on the API Services app, so register it and save again straight away.
{% endhint %}

{% hint style="warning" %}
If the API Services app is configured in Okta to require DPoP (Demonstrating Proof of Possession) on token requests, Herd reports this when you save rather than connecting. Turn that setting off on the app before connecting.
{% endhint %}

***

## Option B: Connect with an API token (legacy)

### Setup Okta Token

1. Log in to your organization's Okta tenant as an administrator.
2. Find *Security* -> *API* -> *Tokens*

<figure><img src="https://2286452423-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FrNdsHrfHsFuaMzCbOD5q%2Fuploads%2Fi3jMw5WT9BFkL7UTVb1F%2FScreenshot%202025-09-10%20at%2016.20.50.png?alt=media&amp;token=60732e8e-74c6-4804-8b2e-319af3604e7d" alt=""><figcaption></figcaption></figure>

3. Create a new token with the following permissions:

For Group Sync:

* `okta.users.read`
* `okta.groups.read`

For App Sync:

* `okta.apps.read`

For Work-Information Attribute Mapping (optional):

* `okta.schemas.read`

{% hint style="info" %}
Herd also reads each user's MFA factors via `/users/{id}/factors`, on [its own schedule](#what-else-touches-your-okta-tenant) rather than during the directory sync. That request is covered by the `okta.users.read` scope. No additional scope is required.
{% endhint %}

{% hint style="warning" %}
An Okta API token inherits the privileges of the admin account that creates it, and it never expires.

A **Read-Only Administrator** account covers everything Herd syncs — users, groups and app assignments. It does **not** carry `okta.schemas.read`, and Okta does not expose that permission when building a custom admin role either, so in practice a token used for [work-information attribute mapping](#work-information-attribute-mapping) must be created by a **Super Administrator**.

Attribute mapping is optional. If you don't need it, a Read-Only Administrator token is sufficient and everything else — group sync, app sync, user sync, auto-assignment — works normally. If you would rather not tie Herd to an admin's account at all, use the [API service app](#option-a-connect-with-an-api-service-app-recommended) instead.
{% endhint %}

<figure><img src="https://2286452423-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FrNdsHrfHsFuaMzCbOD5q%2Fuploads%2FdVCIkDsr796DodXmRtHY%2FScreenshot%202025-09-10%20at%2016.21.13.png?alt=media&amp;token=324ce172-6737-433a-9dd2-fde36209120a" alt=""><figcaption></figcaption></figure>

4. Save the API token to share with Herd.

### Share Your Okta Token & Domain In Herd

1. On the **API token (legacy)** tab, place your Okta domain in the **Okta Domain** box.
2. Place your Okta API token in the **Okta API Token** box.
3. Click **Save Okta Configuration**. Credentials are verified against Okta before they are saved.

***

## Sync Okta Group & Apps

By selecting **Sync Users & Groups**, Herd will pull user and group information from Okta into Herd. Groups will be automatically attached to respective users. In order to use the security advisor feature, you'll need to sync apps as well.

***

## How often Herd syncs

Once Okta is connected there is nothing to schedule. After the first sync, Herd re-syncs your Okta directory automatically **every 2 hours**, at the top of every even hour (Pacific time), picking up users whose profile changed since the last run and refreshing group memberships. Each completed run updates the **Last synced** time on the Okta Integration tile and appears in the Activity feed on the Dashboard.

Two things stretch beyond that baseline, both to keep Herd well inside Okta's API rate limits:

* **Group memberships on large directories.** Re-reading membership is the expensive part of a sync, and its cost grows with the number of groups. A directory of a few hundred groups refreshes memberships every 2 hours; larger directories (thousands of groups, or many very large groups) are spread out in 2-hour steps, up to every 12 hours at most. [Scoping the sync](#sync-scope-syncing-only-selected-okta-groups) to the groups you actually use brings it back to every 2 hours, since only in-scope groups count.
* **A weekly full pass.** Once a week Herd re-reads every user and every group's membership in full rather than only what changed, so anything the incremental runs can't see (for example last sign-in times) is refreshed.

Click **Sync Users & Groups** on the tile to run a sync right away, for example after changing the sync scope or attribute mapping.

### Real-time directory changes (Okta event hook)

With an Okta event hook connected, a new hire, a deactivation or a profile change in Okta reaches Herd in seconds instead of at the next 2-hourly sync. Herd treats each event only as a pointer: it re-reads the named user (and, for membership changes, the named group's members) from Okta and applies it the same way the scheduled sync does, including your [sync scope](#sync-scope-syncing-only-selected-okta-groups). The 2-hourly sync keeps running as before.

The **Real-time directory changes** block on the Okta tile shows its status:

* "Not connected. Directory changes reach Herd at the next scheduled sync (every 2 hours)."
* "Waiting for Okta: the hook has not delivered to Herd with its current secret yet."
* "Connected. Last directory change applied …" (or "No directory change has arrived in the last 7 days").

To turn it on, click **Connect event hook** and confirm. Herd says what it is about to do in your Okta org before it does it, and issues a new secret:

* **Automatic.** If your service app has been granted `okta.eventHooks.manage` (and has an admin role that may manage event hooks), or your legacy API token belongs to an admin who may, Herd creates the hook in Okta and asks Okta to verify it. This is the one change Herd makes in Okta.
* **By hand.** Otherwise, or if Okta refuses, Herd shows the **Endpoint URL**, **Authentication field**, secret and **Events** to use. In the Okta Admin Console, open **Workflow → Event Hooks**, create an event hook (or edit Herd's) with those values, subscribe it to the events listed, then verify it. The secret is shown once.

**Reconnect event hook** issues a new secret. The current secret keeps working until the new one is in use, and for 15 minutes at most; tick **The current secret may have leaked: stop accepting it as soon as I reconnect** to cut it off at once. If you created the hook by hand, paste the new secret into it in Okta and keep every event it already sends.

{% hint style="info" %}
Herd removes its event hook only when you point the connection at another Okta org. To remove it otherwise, delete it in the Okta Admin Console under **Workflow → Event Hooks**.
{% endhint %}

### What else touches your Okta tenant

If your security team reviews Okta's own API logs, two other Herd activities show up there besides the directory sync:

* **MFA factor collection, every 6 hours.** Herd reads the enrolled factors of up to 500 users per run, starting with the users it checked longest ago, so a typical tenant is fully refreshed about once a day and a very large one within a few days. This feeds the MFA signals in risk scoring; it never changes anything in Okta.
* **App sync is manual.** Herd reads your Okta applications only when someone in Herd starts an app sync, for example with the **Sync Apps** button on the tile. It is not on a schedule, so run it again when you want the app list in Herd to reflect changes in Okta.

The nightly risk recompute (dormant accounts, MFA posture) works from what the sync has already stored and makes no requests to Okta.

***

## Validating the connection

Once Okta credentials are configured, the Okta Integration tile shows a live connection status and a **Test connection** button — Herd probes your Okta tenant and confirms the credentials authenticate and can read users. For a service app, the test mints a fresh token first, so it also proves the public key is still registered and the granted scopes match. Credentials are also validated at save time, with specific guidance when something's off (wrong domain format, an expired token, or credentials whose admin role lacks read access — a **Read-Only Administrator** role or better is required for the user, group and app reads).

The same check reports whether Herd can read your Okta **user profile schema**. If it can't, Herd saves the credentials and syncs as normal, and notes on the tile that work-information attribute mapping is unavailable until the credentials are updated (a Super Administrator token, or the `okta.schemas.read` scope on a service app).

***

## Sync scope: syncing only selected Okta groups

By default Herd syncs your entire directory: every user and every group. If you only want part of your directory in Herd (for example a single "all employees" group in a tenant that also holds hundreds of service or partner groups), you can scope the sync to specific Okta groups.

Once Okta is connected, the Okta Integration tile shows the current scope (**Sync scope: entire directory (all users and groups)** by default) with an **Edit sync scope** button.

{% hint style="warning" %}
A scoped sync is a statement about who belongs in Herd. Members of the selected groups are provisioned automatically, and groups outside the scope are removed from Herd. Users who leave every selected group are **excluded by sync scope: hidden, not deleted**. Herd previews exactly this impact before anything is written.
{% endhint %}

**What "excluded by sync scope" means.** An excluded user leaves rosters, counts, new assignments and campaigns, but keeps their existing assignments, group memberships and history. Adding them back to a selected group restores them on the next sync. To see who is excluded, open **Users** and choose **Excluded by a sync** under **Filters** (see [Users & Groups](/herd-security-docs/users/users.md#searching-and-filtering)). Excluded admins and operators keep their dashboard access and admin notifications, since exclusion only affects who is treated as a learner. A user who is removed from Okta altogether is still deprovisioned as before.

{% hint style="info" %}
If the selected groups resolve to no members at all, the scoped sync now fails before writing anything, instead of excluding everyone.
{% endhint %}

1. Click **Edit sync scope** to open the **Okta sync scope** dialog. Use **Search groups** to filter by group name and tick the groups to keep in sync. Leaving every group unticked syncs the whole directory.
2. Click **Review changes**. Herd works out what applying the scope would do to your current data and shows it: how many users are in the selected groups, how many users would be excluded by sync scope, and which groups would be removed. A sample of the affected users and groups is listed; counts are always exact.
3. Click **Apply scope** to save, or **Back** to change the selection. Nothing is written until you apply.

To return to a full-directory sync, open the dialog, click **Clear selection** and then **Clear scope**. The next sync covers the whole directory again and brings back anyone a scoped sync excluded.

Sync scope is available on every plan. The same picker is on the [Microsoft Entra ID](/herd-security-docs/microsoft-entra-id-azure-ad/azure-active-directory-user-and-group-sync.md#sync-scope-syncing-only-selected-groups) and [Google Workspace](/herd-security-docs/google-workspace/google-workspace.md#sync-scope-syncing-only-selected-units-and-groups) tiles.

***

## Work-information attribute mapping

{% hint style="warning" %}
This screen needs permission to read your Okta user profile schema: the `okta.schemas.read` scope on a service app, or a legacy API token created by a **Super Administrator** (a Read-Only Administrator token does not carry it). Without it, the modal explains that instead of listing attributes. Sync and auto-assignment are unaffected, and every field keeps its default attribute.
{% endhint %}

By default, Herd reads each user's work information from Okta's standard profile attributes — `department`, `division`, `title`, `organization`, `userType`, `manager`, `managerId`, `employeeNumber`, and `costCenter`. If your Okta profile stores these under **custom attributes**, remap them: on the Okta Integration tile, click **Edit attribute mapping**.

The **Work information attribute mapping** modal lists each Herd field with a dropdown of every attribute in your live Okta profile schema (custom attributes included), so you can point, say, Herd's **Department** at your `org_dept` custom attribute. Saving a change triggers a full re-sync that backfills every existing user from the new attributes. **Reset to defaults** restores the standard mapping.

These fields flow everywhere work information is used — user profiles, the Department column and filters, manager linking and escalations, dynamic group rules, and [custom message variables](/herd-security-docs/settings/settings/general-and-approvals.md#custom-variables-directory-lookup-variables).
