Skip to main content

About Okta

Okta is a cloud identity and access management (IAM) platform for managing users, groups, and application access. Serval connects to Okta using OAuth 2.0 client credentials against an Okta API Services app. Once connected, Serval can:
  • Keep your Okta users, groups (with members), applications, and sign-in activity in sync with Serval in the background.
  • Grant and revoke access by adding users to and removing them from Okta groups.
  • Call any Okta Management API endpoint from workflows, including a set of prebuilt Okta workflows you can install.
Authentication: OAuth 2.0 client credentials. The Serval integration from the Okta Integration Network (OIN) uses the Client ID and Client Secret generated during installation. A custom Okta API Services app must use a private key JWT (private_key_jwt). Data sync: Scheduled background syncs of users, groups and memberships, applications and their assigned groups, and last-login activity. Optionally, Okta event hooks push user and group changes to Serval in real time.
This page covers the Okta API integration. To let your users sign in to Serval with Okta, see Okta SSO (SAML) - that’s a separate setup.

Get your credentials

Serval connects to an Okta API Services app using OAuth 2.0 client credentials. Depending on your Serval deployment, the Okta connect dialog offers Use Serval App (install Serval from the Okta Integration Network, which generates a Client ID and Client Secret for you) and Custom Application (create your own Okta API Services app using an RSA private key). In both cases you must grant the required scopes on the Okta app and assign the app an admin role so those scopes can actually act on resources.
A custom API Services app requesting okta.* management scopes from Okta’s Org Authorization Server must use private_key_jwt. Client ID and Client Secret authentication is not supported for this flow. See Okta’s OAuth service app setup guide.

Connect in Serval

Enter your credentials into the Serval connect form. For an OIN installation, use the generated Client Secret. For a custom API Services app, use the RSA private JWK. Serval automatically selects the authentication method based on what you paste into the Client Secret field.
1

API Domain (required)

Your Okta domain without “https://” - for example, “yourcompany.okta.com”.
Don’t paste the admin console URL. If your console shows “yourcompany-admin.okta.com”, strip the “-admin” and enter “yourcompany.okta.com”. An invalid domain shows: “Must be a valid Okta domain (e.g., yourcompany.okta.com)”.
2

Client ID (required)

The OAuth 2.0 client ID from your Okta application - exactly 20 alphanumeric characters. An invalid value shows: “Must be a valid Okta client ID (20 alphanumeric characters)”.
3

Client Secret (required)

This field is masked. For an OIN installation, paste the Client Secret generated during installation. For a custom API Services app, paste the complete RSA private JWK object generated in Okta. The value must begin with {; do not paste a PEM key beginning with -----BEGIN PRIVATE KEY-----.
When editing an existing connection, you can leave this field blank to keep the stored secret. The stored secret is always shown masked.
4

Scopes (required)

A comma-separated list of OAuth scopes, pre-filled with the default set. Remove any you don’t need, and make sure every scope listed here is also granted on the Okta app.
An empty list is rejected. Each scope must follow the expected format, otherwise you will see: “Each scope must follow the format okta.<resource>.<operation> or okta.<resource>.<subresource>.<operation> (e.g., okta.users.read, okta.accessRequests.catalog.read). SSF scopes are ssf.<operation> (e.g., ssf.read, ssf.manage).”

Verifying the connection

After connecting, review List Okta Apps, List Okta Users, and List Okta Groups for the syncs you plan to use. Manage Okta Groups verifies write access by creating and deleting a temporary group. If you intentionally omitted a scope, its check reports “Missing required scope in Serval: <scope>”; that failure does not prevent features covered by your other scopes from working.
When a check fails, Serval adds context to the message:
  • If Serval couldn’t get a token at all: “Serval could not obtain an Okta access token with the stored credentials — the Okta API itself was never called.”
  • On a permission error: “The correct scope is configured in Serval, but the OAuth application may need admin consent or additional admin roles in Okta.”
  • On an Okta server error: “Okta returned a server error. This is likely a temporary issue with Okta’s API - please try again later.”
For common Okta error codes, the check also suggests a fix:
If every check fails, the token request itself is usually failing - most often because a scope in your Serval list isn’t granted on the Okta app (invalid_scope), so Okta refuses the whole token. If only Manage Okta Groups fails, the app’s admin role is probably too narrow to change groups.

What the Okta integration enables

Scopes and what they unlock

Serval requests every scope in the connect form’s Scopes field when it gets a token, and each one must also be granted on the Okta app. The default set is okta.apps.read, okta.apps.manage, okta.users.read, okta.users.manage, okta.groups.read, okta.groups.manage, and okta.logs.read. Remove the ones you don’t need. You can add other Okta scopes (the field suggests known ones as you type) to use more of the Okta API from custom workflows.
A scope alone isn’t enough. Scopes control which parts of the API Serval may call; the admin role assigned to the Okta app controls which resources it can act on. For example, a read-only admin role lets syncs work but blocks group membership changes.

What Serval syncs

Each sync has its own toggle on your Okta integration’s Settings tab in Serval. Only organization admins can change the User Sync and Last Login Sync settings. Manager mapping: Okta has no standard manager field, so Serval reads the manager from the user profile. It takes the manager ID from managerId, manager_id, or manager (in that order), and the manager email from managerEmail, ManagerEmail, manager_email, or managerEmailAddress, falling back to a manager ID field that contains an email address. To change how users sync, install the Okta User Ingestion workflow and attach it to your Okta integration as a custom user sync. Groups you just created: When you pick a linked Okta group for a role and the group isn’t listed yet, use the “Group missing? Import it straight from your identity provider:” option to import it immediately instead of waiting for the next sync. The lookup matches groups whose name starts with what you typed.

Installable workflows

The Okta MFA Push Approval workflow works as an approval procedure that approves a request when the user accepts an Okta Verify push.

Real-time sync with event hooks

By default, Serval learns about changes in Okta on the sync schedule above, so a change can take hours to appear. With an Okta event hook configured, Okta notifies Serval when a user or group changes, and Serval re-fetches just that user or group - changes typically appear in Serval within seconds. This is optional and additive: the scheduled syncs keep running either way and catch anything the hook misses. Event hooks use the scopes your user and resource syncs already use; no extra scopes are needed. One hook carries both user and group events, but Serval only acts on events for syncs you’ve turned on - user events need User Sync, group events need Resource Sync.
If you don’t see the Webhooks section described below on your Okta integration’s Settings tab, turn on User Sync or Resource Sync first - the section only appears once one of them is on. If it’s still missing, contact support@serval.com.
1

Copy the webhook details from Serval

In Serval, go to Integrations, open your Okta integration, and switch to the Settings tab. The Webhooks section shows a Webhook URL and a Secret. Copy both.
You can re-copy the Secret from this page at any time - unlike Okta, Serval doesn’t hide it after setup.
2

Create the event hook in Okta

In the Okta admin console, go to Workflow → Event Hooks and choose Create Event Hook. Give it a name (for example, “Serval real-time sync”) and fill in:
  • URL: the Webhook URL copied from Serval.
  • Authentication field: X-Webhook-Secret
  • Authentication secret: the Secret copied from Serval.
  • Custom header fields: leave empty.
Paste the Secret exactly as copied - no quotes and no trailing whitespace. Serval compares the value character-for-character and rejects anything that doesn’t match.
3

Subscribe to events

The Subscribe to events box starts empty. Add the events below (API event type in parentheses).User events - Serval re-fetches the user:
  • User created (user.lifecycle.create)
  • User activated (user.lifecycle.activate)
  • User deactivated (user.lifecycle.deactivate)
  • User suspended (user.lifecycle.suspend)
  • User unsuspended (user.lifecycle.unsuspend)
  • User’s Okta profile updated (user.account.update_profile)
Group events - Serval re-fetches the group and its members:
  • Group created (group.lifecycle.create)
  • Group profile updated (group.profile.update)
  • User added to group (group.user_membership.add)
  • User removed from group (group.user_membership.remove)
Directory group events - add these if you import groups from Active Directory or LDAP, because Okta logs directory changes under different event types:
  • Group imported from a directory (system.import.group.create)
  • Group deleted from a directory (system.import.group.delete)
  • Group imported or updated from an app (app.user_management.user_group_import.upsert_success)
  • Directory group member imported or removed (app.user_management)
How Serval handles these events:
  • Events that name one user or group refresh exactly that user or group. Any other user.* or group.* event you subscribe to works the same way.
  • Directory group deletes remove the group from Serval only after Okta confirms it no longer exists.
  • App import events (app.user_management and app.user_management.user_group_import.upsert_success) that don’t name a single group are batched for about 30 seconds, then Serval runs one incremental sync of recently changed groups and their members. Only successful events count; ones Okta marks as failed are ignored.
  • Directory import create and delete events (system.import.group.create and system.import.group.delete) must name the group as a UserGroup target. If the target is missing, nothing refreshes until the next scheduled group sync.
  • Native group deletion (group.lifecycle.delete) isn’t acted on from the hook; the next scheduled group sync removes the group.
  • Directory group renames (system.import.group.update) can’t be sent through Okta event hooks. Serval picks them up on the next incremental group sync.
Event hooks speed things up but don’t guarantee that every Active Directory or LDAP change shows up immediately; the scheduled syncs reconcile everything. If you created your hook before adding directory events, add those subscriptions in Okta - updating Serval doesn’t change an existing hook.
4

Verify the hook

Save the hook and click Verify. Okta sends a one-time verification challenge to the Webhook URL, which Serval answers automatically.
The hook shows as “Verified” in Okta. From this point on, changes flow to Serval as they happen.

Gotchas and troubleshooting

A custom API Services app requesting okta.* management scopes cannot authenticate to the Org Authorization Server with a Client ID and Client Secret. In the app’s Client Credentials settings, select Public key / Private key, generate and save an RSA key, and paste the complete private JWK object into Serval’s Client Secret field. When editing an existing Serval connection, do not leave the field blank: a blank value preserves the previously stored client secret.
Scopes in the Serval form are only the scopes Serval requests. They must also be granted on the Okta API Services app, and the app must be assigned an admin role (or a custom role plus resource set) that covers the resources involved. Grant the missing scope on the app’s Okta API Scopes tab and check its admin role.
Serval requests all listed scopes in one token request. If any of them isn’t granted on the Okta app, Okta rejects the token (invalid_scope) and every sync, health check, and workflow fails. Grant the scope in Okta, or remove it from the Serval scope list.
Before changing a group membership, Serval looks up the user in Okta by their Serval email address. “No user found with email: <email>” means no Okta user matches, and “Multiple users found with email: <email>” means the lookup matched more than one user. Make sure the user’s email in Serval matches their Okta profile email.
Serval only sends requests to “okta.com” and “oktapreview.com” hosts. The connect form accepts an “okta-emea.com” domain, but requests to it fail when they run. Use an “okta.com” or “oktapreview.com” domain.
The API Domain field expects a bare host such as “yourcompany.okta.com”. Don’t include “https://”, and don’t copy the admin console URL - “yourcompany-admin.okta.com” is wrong; strip the “-admin” to get “yourcompany.okta.com”. The domain is used for both the token request and API calls, so a wrong value breaks everything downstream.
Serval decides between the plain-secret and private-key methods by whether the Client Secret value is a key in JSON form. Paste the complete RSA private JWK object into the Client Secret field. It must begin with { and include the private-key parameters and the kid matching the public key registered on the same Okta app. A PEM value beginning with -----BEGIN PRIVATE KEY----- is treated as a plain client secret and can produce an HTTP 401 (invalid_client) response. A non-RSA JWK fails token generation with “key is not an RSA private key”. If the Okta interface exposes only PEM, contact support@serval.com without attaching the key.
Serval doesn’t support DPoP. In the API Services app’s general settings, uncheck “Require Demonstrating Proof of Possession (DPoP) header in token requests”. Health checks flag this case with invalid_dpop_proof.
When you edit the connection, Scopes and API Domain are required, but you can leave the Client Secret blank to keep the stored one. Paste a new value only when rotating it. If Okta reports invalid_client after you change the Client ID, re-enter both the Client ID and the secret.
Removing the Okta connection deletes the stored credentials from Serval, but client credentials can’t be revoked remotely. To fully cut off access, deactivate the Serval app in Okta or rotate its secret or key.
Okta enforces per-endpoint rate limits. Serval paces group-membership and app-group syncs and automatically retries throttled requests, but heavy custom workflows can still hit Okta’s limits.
For polling queries (sortOrder=ASCENDING with no until), Okta’s /api/v1/logs endpoint keeps returning a “next” link, even when no new events are available. A one-time export should stop when it receives an empty page; a continuous poller should wait and resume from that link. Bounded queries with both since and until have a final page with no “next” link. Always follow the returned links rather than constructing page cursors yourself.
Check the URL first: it must be the exact Webhook URL copied from the Webhooks section of your Okta integration’s Settings tab in Serval. A 404 during verification almost always means the URL is wrong or truncated.
  • Confirm the hook is subscribed to the events listed in Real-time sync with event hooks - a verified hook with no matching subscriptions delivers nothing useful.
  • Confirm User Sync (for user events) or Resource Sync (for group events) is on in Serval. Events for a sync that’s off are accepted but ignored.
  • Check Okta’s System Log for failed event hook deliveries. If deliveries fail with HTTP 401, the secret doesn’t match: re-copy the Secret from Serval and update the hook’s authentication secret.
The scheduled syncs keep data correct in the meantime.

Need help? Contact support@serval.com for assistance with your Okta integration.