PHPackages                             boldlygrow/okta-api-client - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [API Development](/categories/api)
4. /
5. boldlygrow/okta-api-client

ActiveLibrary[API Development](/categories/api)

boldlygrow/okta-api-client
==========================

Okta API Client for Laravel

4.2(1y ago)00MITPHPPHP ^8.0

Since Feb 1Pushed 1mo agoCompare

[ Source](https://github.com/boldlygrow/okta-api-client)[ Packagist](https://packagist.org/packages/boldlygrow/okta-api-client)[ Docs](https://gitlab.com/provisionesta/okta-api-client)[ RSS](/packages/boldlygrow-okta-api-client/feed)WikiDiscussions main Synced 1w ago

READMEChangelogDependencies (7)Versions (8)Used By (0)

Okta API Client
===============

[](#okta-api-client)

\[\[*TOC*\]\]

Overview
--------

[](#overview)

The Okta API Client is an open source [Composer](https://getcomposer.org/) package for use in Laravel applications for connecting to Okta for provisioning and deprovisioning of users, groups, applications, and other related functionality.

Please use at your own risk and create merge requests for any bugs that you encounter.

### Problem Statement

[](#problem-statement)

Instead of providing an SDK method for every endpoint in the API documentation, we have taken a simpler approach by providing a universal `ApiClient` that can perform `GET`, `POST`, `PUT`, and `DELETE` requests to any endpoint that you find in the [Okta API documentation](https://developer.okta.com/docs/reference/core-okta-api/).

This builds upon the simplicity of the [Laravel HTTP Client](https://laravel.com/docs/10.x/http-client) that is powered by the [Guzzle HTTP client](http://docs.guzzlephp.org/en/stable/) to provide "last lines of code parsing" for Okta API responses to improve the developer experience.

The value of this API Client is that it handles the API request logging, response pagination, rate limit backoff, and 4xx/5xx exception handling for you.

### Example Usage

[](#example-usage)

```
use BoldlyGrow\Okta\ApiClient;

// Get a list of records
// https://developer.okta.com/docs/reference/api/groups/#list-groups
$groups = ApiClient::get('groups');

// Search for records with a specific name
// This example uses positional arguments
// https://developer.okta.com/docs/reference/core-okta-api/#filter
// https://developer.okta.com/docs/reference/api/groups/#list-groups-with-search
$groups = ApiClient::get('groups', [
    'search' => 'profile.name eq "Hack the Planet Engineers"'
]);

// Search for users with a specific
// This example uses positional arguments
// https://developer.okta.com/docs/reference/api/users/#list-users-with-search
$users = ApiClient::get('users', [
    'search' => 'profile.firstName eq "Dade"'
]);

// Get a specific record
// https://developer.okta.com/docs/reference/api/groups/#get-group
$group = ApiClient::get('groups/00g1ab2c3D4E5F6G7h8i');

// {
//     +"id": "0og1ab2c3D4E5F6G7h8i",
//     +"created": "2023-01-01T00:00:00.000Z",
//     +"lastUpdated": "2023-02-01T00:00:00.000Z",
//     +"lastMembershipUpdated": "2023-03-15T00:00:00.000Z",
//     +"type": "OKTA_GROUP",
//     +"profile": {
//         +"name": "Hack the Planet Engineers",
//         +"description": "This group contains engineers that have proven they are elite enough to hack the Gibson.",
//     },
// }

$group_name = $group->data->profile->name;
// Hack the Planet Engineers

// Create a group
// https://developer.okta.com/docs/reference/api/groups/#add-group
// This example uses named arguments
$group = ApiClient::post(
    uri: 'groups',
    data: [
        'profile' => [
            'name' => 'Hack the Planet Engineers',
            'description' => 'This group contains engineers that have proven they are elite enough to hack the Gibson.'
        ]
    ]
);

// Update a group
// https://developer.okta.com/docs/reference/api/groups/#update-group
// This example uses named arguments
$group_id = '00g1ab2c3D4E5F6G7h8i';
$group = ApiClient::put(
    uri: 'groups/' . $group_id,
    data: [
        'profile' => [
            'description' => 'This group contains engineers that have liberated the garbage files.'
        ]
    ]
);

// Delete a group
// https://developer.okta.com/docs/reference/api/groups/#remove-group
$group_id = '00g1ab2c3D4E5F6G7h8i';
ApiClient::delete('groups/' . $group_id);
```

### Issue Tracking and Bug Reports

[](#issue-tracking-and-bug-reports)

We do not maintain a roadmap of feature requests, however we invite you to contribute and we will gladly review your merge requests.

Please create an [issue](https://github.com/boldlygrow/okta-api-client/issues) for bug reports.

### Contributing

[](#contributing)

Please see [CONTRIBUTING.md](CONTRIBUTING.md) to learn more about how to contribute.

### Maintainers

[](#maintainers)

NameGitLab HandleEmail[Jeff Martin](https://www.linkedin.com/in/jeffersonmmartin/)[@jeffersonmartin](https://github.com/jeffersonmartin)`jeff [at] boldlygrow [dot] us`### Contributor Credit

[](#contributor-credit)

- Dillon Wheeler
- Jeff Martin

Installation
------------

[](#installation)

### Requirements

[](#requirements)

RequirementVersionPHP`^8.0`Laravel`^8.0`, `^9.0`, `^10.0`, `^11.0`, `^12.0`, `^13.0`OAuth 2.0 authentication requires no additional Composer packages; the JWT client assertion is signed with the `openssl` extension. This package does not integrate with any secrets manager, so there is no cloud SDK dependency. Fetching the signing key from a secrets manager, if you use one, is done in your own application code (see [Private Key Storage](#private-key-storage)).

### Upgrade Guide

[](#upgrade-guide)

See the [changelog](https://github.com/boldlygrow/okta-api-client/tree/main/changelog) for release notes.

### Add Composer Package

[](#add-composer-package)

```
composer require boldlygrow/okta-api-client:^5.0

```

If you are contributing to this package, see [CONTRIBUTING.md](CONTRIBUTING.md) for instructions on configuring a local composer package with symlinks.

### Publish the configuration file

[](#publish-the-configuration-file)

**This is optional**. The configuration file specifies which `.env` variable names that that the API connection is stored in. You only need to publish the configuration file if you want to rename the `OKTA_API_*` `.env` variable names.

```
php artisan vendor:publish --tag=okta-api-client

```

Authentication
--------------

[](#authentication)

The client supports two authentication methods. It uses OAuth 2.0 automatically when an OAuth `client_id` is configured, and otherwise falls back to a legacy SSWS API token. New integrations should use OAuth 2.0.

MethodCredentialBest forOAuth 2.0 (`private_key_jwt`)Client ID + private keyNew integrations, least privilege, automatic rotationSSWS API token (legacy)Static API tokenExisting deployments, quick local testing### Environment Variables

[](#environment-variables)

Add the connection variables to your `.env` file. You can add these anywhere in the file on a new line.

For OAuth 2.0:

```
OKTA_API_URL="https://mycompany.okta.com"
OKTA_API_CLIENT_ID="0oaExampleClientId"
OKTA_API_KEY_ID="the-registered-kid"

# Provide the signing key one of two ways (see Private Key Storage):
# a path to a PEM file (local development or a mounted secret) ...
OKTA_API_PRIVATE_KEY_PATH="/var/secrets/okta/private-key.pem"
# ... or an inline PEM string (often resolved from a secrets manager in code).
# OKTA_API_PRIVATE_KEY=
```

There is no scope environment variable. The scope is supplied per request as the `scope` argument (see [Scopes](#scopes)). For production, the private key is commonly resolved from a secrets manager and passed in the connection array rather than set in the environment (see [Private Key Storage](#private-key-storage)).

For the legacy SSWS token:

```
OKTA_API_URL="https://mycompany.okta.com"
OKTA_API_TOKEN="S3cr3tK3yG03sH3r3"
```

If you have your connection secrets stored in your database or a secrets manager, you can override the `config/okta-api-client.php` configuration or provide a connection array on each request. See [connection arrays](#connection-arrays) to learn more.

#### URL

[](#url)

Each Okta customer is provided with a subdomain for their company. This is sometimes referred to as a tenant or `${yourOktaDomain}` in the API documentation. You can also use an Okta Preview instance.

If you're just getting started, it is recommended to use a free [Okta developer account](https://developer.okta.com/signup/).

```
OKTA_API_URL="https://mycompany.okta.com"

OKTA_API_URL="https://mycompany.oktapreview.com"

OKTA_API_URL="https://dev-12345678.okta.com"
```

### OAuth 2.0 for Okta

[](#oauth-20-for-okta)

Okta requires the `private_key_jwt` client authentication method for access tokens that carry management API scopes. Client ID and client secret is **not** supported for reading users, groups, or apps. A client secret only works against a custom authorization server for custom scopes, which cannot read Okta resources. This client therefore authenticates with a Client ID and a private key, never a secret.

#### Create the service app

[](#create-the-service-app)

1. In the Okta Admin Console, go to **Applications &gt; Create App Integration**.
2. Select **API Services** as the sign-in method and click **Next**.
3. Enter an app name (for example "Provisionr integration") and click **Save**.

This creates the app in Client Credentials mode. The next sections register your signing key and grant the scopes and admin role. The scopes you grant are the hard ceiling on what any token can do; a narrower scope requested at runtime can never exceed the grant, so grant only what the integration needs.

#### Generate and register a signing key

[](#generate-and-register-a-signing-key)

OAuth for Okta uses an RSA public/private key pair instead of a shared secret. You keep the **private key** (this package signs a JWT with it on every token request), and you register the matching **public key** with the Okta app so Okta can verify that signature. RS256 is the default algorithm.

There are two ways to get the key pair. Choose one.

##### Option A: Let Okta generate the key pair (quickest, testing)

[](#option-a-let-okta-generate-the-key-pair-quickest-testing)

1. In your API Services app, open the **General** tab. In the **Client Credentials** section, click **Edit**.
2. For **Client authentication**, select **Public key / Private key**.
3. In the **Public Keys** section, click **Add key**, then **Generate new key**.
4. Okta shows the new key pair. Click **PEM** to view the private key in PEM format, then **Copy to clipboard**. This is the **only** time Okta shows the private key, so save it now. Okta does not store it.
5. Click **Done**, then **Save**.
6. In the **Public Keys** table, note the **Key ID (KID)** for the key you just added. You will set this as `OKTA_API_KEY_ID`.

Okta recommends this option for testing only, because Okta generated (and briefly held) the private key. For production, generate the key yourself so the private key never leaves your control. Use Option B.

##### Option B: Generate your own key pair (recommended for production)

[](#option-b-generate-your-own-key-pair-recommended-for-production)

The package ships an Artisan command that generates an RSA key pair, writes the private key, and prints the public JWK ready to paste into Okta. This is the simplest path and needs no OpenSSL CLI or extra tooling:

```
php artisan okta:jwk --generate --out=okta-private-key.pem

```

This writes `okta-private-key.pem` (the **private key**, `0600`) and prints the public JWK. Keep the private key secret: store it in your secrets manager and pass it in at runtime, or point `OKTA_API_PRIVATE_KEY_PATH` at the file. Never commit it. The printed JWK looks like this (your `n` and `kid` will differ):

```
{
  "kty": "RSA",
  "n": "0vx7agoebGcQSuu…",
  "e": "AQAB",
  "use": "sig",
  "alg": "RS256",
  "kid": "NzbLsXh8uDCcd-6MNwXF4W_7noWXFZAfHkxZsRGC9Xs"
}
```

If you already have a public key PEM (for example one generated by your own key management process), convert it to a JWK instead of generating a new pair:

```
php artisan okta:jwk okta-public-key.pem

```

To generate the pair with OpenSSL directly and convert it, all three steps are equivalent:

```
openssl genrsa -out okta-private-key.pem 2048
openssl rsa -in okta-private-key.pem -pubout -out okta-public-key.pem
php artisan okta:jwk okta-public-key.pem

```

The conversion is also available programmatically as an action, so you can build the JWK inside your own provisioning or key-rotation code:

```
use BoldlyGrow\Okta\PublicKeyJwk;

$jwk = PublicKeyJwk::fromPemFile('okta-public-key.pem');
// or
$jwk = PublicKeyJwk::fromPem($publicKeyPemString);
```

Then register the public key:

3. In your API Services app, open the **General** tab. In **Client Credentials**, click **Edit**, set **Client authentication** to **Public key / Private key**, then in **Public Keys** click **Add key**. Paste the entire JWK JSON, click **Done**, then **Save**.
4. In the **Public Keys** table, copy the **Key ID (KID)** that Okta shows for the key. Set `OKTA_API_KEY_ID` to that value.

#### Finish the app configuration

[](#finish-the-app-configuration)

Regardless of which option you chose:

1. On the **Okta API Scopes** tab, click **Grant** for each scope your integration needs, for example `okta.users.read`, `okta.groups.read`, and `okta.apps.read`.
2. On the **Admin roles** tab, assign a least-privilege role such as `Read-only Administrator`. Do not assign `Super Administrator`.
3. On the **General** tab, copy the **Client ID** and set it as `OKTA_API_CLIENT_ID`.
4. Store the private key per the [Private Key Storage](#private-key-storage) section, and set `OKTA_API_KEY_ID` to the Key ID from the previous step.

The `kid` is included in the signed assertion header so Okta knows which registered public key to verify against. Setting `OKTA_API_KEY_ID` is required once the app has more than one registered key, and harmless to always set.

#### Scopes

[](#scopes)

The scope is provided per request as the `scope` argument. There is no scope default in config or the environment, so each OAuth call declares exactly the authority it needs.

```
use BoldlyGrow\Okta\ApiClient;

// Requests only okta.users.read for this call
$users = ApiClient::get(uri: 'users', scope: 'okta.users.read')->data;

// Requests only okta.groups.read for this call
$groups = ApiClient::get(uri: 'groups', scope: 'okta.groups.read')->data;
```

Pass more than one scope as a space-delimited string when a single call needs multiple:

```
$response = ApiClient::get(uri: 'users/' . $id . '/appLinks', scope: 'okta.users.read okta.apps.read')->data;
```

If the `scope` argument is omitted while OAuth credentials are configured, the client throws a `ScopeException`. The legacy SSWS token path ignores the `scope` argument entirely.

Each token is cached for its full lifetime (about 59 minutes) per unique scope set, so requesting scopes per call mints a few more tokens per hour but does not shorten caching.

#### Private Key Storage

[](#private-key-storage)

The private key is provided in one of two ways. This package does not integrate with any secrets manager; fetching the key from a vault or secrets manager is your application's responsibility, which keeps the package independent of your storage choice and free of cloud SDK dependencies.

FieldValueUse`private_key` (`OKTA_API_PRIVATE_KEY`)An inline PEM stringProduction (resolved from your secrets manager at runtime), tests, CI`private_key_path` (`OKTA_API_PRIVATE_KEY_PATH`)A path to a PEM file (leading `~` expanded)Local development, or a secret mounted as a fileProvide one or the other. `private_key` takes precedence when both are set.

##### Resolve the key from a secrets manager (connection array)

[](#resolve-the-key-from-a-secrets-manager-connection-array)

Fetch the PEM from wherever you store it and pass it as `private_key` in a per-request connection array. This is the recommended pattern for multi-tenant applications, since each tenant can supply its own key. The example below uses Google Secret Manager, but any source works.

```
use BoldlyGrow\Okta\ApiClient;
use Google\Cloud\SecretManager\V1\Client\SecretManagerServiceClient;
use Google\Cloud\SecretManager\V1\AccessSecretVersionRequest;

// Fetch the PEM in your own code. Swap this for Vault, AWS Secrets Manager,
// a mounted file, an HTTP call, etc. Cache it as appropriate for your app.
$client = new SecretManagerServiceClient();
$name = $client->secretVersionName('my-gcp-project', 'okta-mycompany-private-key', 'latest');
$privateKey = $client->accessSecretVersion(
    (new AccessSecretVersionRequest())->setName($name)
)->getPayload()->getData();

$connection = [
    'url' => 'https://mycompany.okta.com',
    'client_id' => '0oaExampleClientId',
    'key_id' => 'the-registered-kid',
    'private_key' => $privateKey,
];

$users = ApiClient::get(uri: 'users', scope: 'okta.users.read', connection: $connection)->data;
```

##### Resolve the key in the published config

[](#resolve-the-key-in-the-published-config)

If you use a single service app for the whole application, publish the config and set the key there instead of passing a connection array on every call. Either point the environment variable at a mounted file:

```
OKTA_API_PRIVATE_KEY_PATH="/var/secrets/okta/private-key.pem"
```

Or resolve it dynamically in the published `config/okta-api-client.php`, since a config file is plain PHP:

```
// config/okta-api-client.php (after php artisan vendor:publish --tag=okta-api-client)
'private_key' => app(\App\Support\OktaKeyResolver::class)->pem(),
```

Loading a secret in the config file runs on every request and during `config:cache`, so have your resolver cache the value (and be aware `config:cache` freezes whatever it returns at build time). Passing the key in the connection array is usually the cleaner choice when the value is dynamic or per-tenant.

### Legacy SSWS API Token

[](#legacy-ssws-api-token)

See the Okta documentation for [creating an API token](https://developer.okta.com/docs/guides/create-an-api-token/main/).

An API token uses the permissions of the user it belongs to, so create a dedicated service account (bot) user for production use cases. Assign the `Read-only Administrator` role and add custom permissions as needed. For safety, do not grant the `Super Administrator` role. Tokens that are inactive for 30 days without API calls automatically expire.

Set the `OKTA_API_TOKEN` in your `.env` file:

```
OKTA_API_TOKEN="S3cr3tK3yG03sH3r3"
```

> **Internal Developer Note:** The API token is automatically prefixed with `SSWS ` when used by the API Client. It does not need to be included when defining the variable value.

### Connection Arrays

[](#connection-arrays)

The variables that you define in your `.env` file are used by default unless you set the connection argument with an array. A connection array contains the `url` and either an OAuth `client_id` (with key location fields) or a legacy `token`.

> **Security Warning:** Do not commit a hard coded API token or private key into your code base. This should only be used with dynamic variables that are stored in your database or secrets manager.

OAuth 2.0 connection array:

```
$connection = [
    'url' => 'https://mycompany.okta.com',
    'client_id' => '0oaExampleClientId',
    'key_id' => 'the-registered-kid',
    'private_key' => $privateKeyPem, // an inline PEM string (see Private Key Storage)
    // or, instead of private_key:
    // 'private_key_path' => '/var/secrets/okta/private-key.pem',
];
```

The scope is not part of the connection array. It is passed per request as the `scope` argument.

Legacy SSWS connection array:

```
$connection = [
    'url' => 'https://mycompany.okta.com',
    'token' => 'S3cr3tK3yG03sH3r3',
];
```

Passing a connection array per request is the recommended pattern for multi-tenant applications, where each tenant has its own `client_id` and key. The OAuth token cache is keyed per connection and scope set, so tenants never share a token.

```
use BoldlyGrow\Okta\ApiClient;

class MyClass
{
    private array $connection;

    public function __construct($connection)
    {
        $this->connection = $connection;
    }

    public function getGroup($group_id)
    {
        return ApiClient::get(
            connection: $this->connection,
            uri: 'groups/' . $group_id,
            scope: 'okta.groups.read'
        )->data;
    }
}
```

### Security Best Practices

[](#security-best-practices)

#### Least Privilege

[](#least-privilege)

Grant the service app only the scopes it needs and assign a read-only admin role. The app's granted scopes are the real least-privilege boundary; requesting narrower scopes per call is defense in depth on top of that grant, not a replacement for it.

#### No Shared Credentials

[](#no-shared-credentials)

Do not reuse an OAuth app or API token created for another purpose. Create a dedicated service app (or token) per use case so that revoking a compromised credential does not affect unrelated systems.

#### Credential Storage

[](#credential-storage)

Do not add your API token or private key to any `config/*.php` files that are committed to your repository (secret leak).

In production, store the private key in a secrets manager or vault and resolve it at runtime rather than committing it or baking it into an image. This package does not fetch the key for you; retrieve it in your own code and pass it as `private_key` in the connection array, or point `private_key_path` at a mounted secret file. For local development, keep the key file outside the repository. All `.env` values should remain in the `.gitignore`-excluded `.env` file.

#### Rotation

[](#rotation)

OAuth service apps support multiple registered public keys, so you can add a new key, start signing with its `kid`, and retire the old key with no downtime. To roll a key, register the new public key in Okta, then update the private key you supply (the connection array value, the file at `private_key_path`, or `OKTA_API_PRIVATE_KEY`) and set `OKTA_API_KEY_ID` to the new `kid`. Because the token cache key includes the `kid` and a fingerprint of the inline key, changing either takes effect immediately; a rotated file at the same `private_key_path` is picked up within the token cache lifetime (at most about 59 minutes).

API Requests
------------

[](#api-requests)

You can make an API request to any of the resource endpoints in the [Okta REST API Documentation](https://developer.okta.com/docs/reference/core-okta-api/).

**Just getting started?** Explore the [applications](https://developer.okta.com/docs/reference/api/apps), [groups](https://developer.okta.com/docs/reference/api/groups/), and [users](https://developer.okta.com/docs/reference/api/users/) endpoints.

EndpointAPI Documentation`apps`[List applications](https://developer.okta.com/docs/reference/api/apps/#list-applications)`apps/{id}`[Get application](https://developer.okta.com/docs/reference/api/apps/#get-application)`apps/{id}/users`[List users assigned to application](https://developer.okta.com/docs/reference/api/apps/#list-users-assigned-to-application)`apps/{id}/groups`[List groups assigned to application](https://developer.okta.com/docs/reference/api/apps/#list-groups-assigned-to-application)`groups`[List groups](https://developer.okta.com/docs/reference/api/groups/#list-groups)`groups/{id}`[Get group](https://developer.okta.com/docs/reference/api/groups/#get-group)`groups/{id}/users`[List group members](https://developer.okta.com/docs/reference/api/groups/#list-group-members)`users`[List users](https://developer.okta.com/docs/reference/api/users/#list-users)`users/{id}`[Get user](https://developer.okta.com/docs/reference/api/users/#get-user)`users/{id}/appLinks`[Get applications assigned to user](https://developer.okta.com/docs/reference/api/users/#get-assigned-app-links)### Dependency Injection

[](#dependency-injection)

If you include the fully-qualified namespace at the top of of each class, you can use the class name inside the method where you are making an API call.

```
use BoldlyGrow\Okta\ApiClient;

class MyClass
{
    public function getGroup($group_id)
    {
        return ApiClient::get('groups/' . $group_id)->data;
    }
}
```

If you do not use dependency injection, you need to provide the fully qualified namespace when using the class.

```
class MyClass
{
    public function getGroup($group_id)
    {
        return \BoldlyGrow\Okta\ApiClient::get('groups/' . $group_id)->data;
    }
}
```

### Class Instantiation

[](#class-instantiation)

We transitioned to using static methods in v4.0 and you do not need to instantiate the ApiClient class.

```
ApiClient::get('groups');
ApiClient::post('groups', []);
ApiClient::get('groups/00g1ab2c3D4E5F6G7h8i');
ApiClient::put('groups/00g1ab2c3D4E5F6G7h8i', []);
ApiClient::delete('groups/00g1ab2c3D4E5F6G7h8i');
```

### Named vs Positional Arguments

[](#named-vs-positional-arguments)

You can use named arguments/parameters (introduced in PHP 8) or positional function arguments/parameters.

It is recommended is to use named arguments if you are specifying request data and/or are using a connection array. You can use positional arguments if you are only specifying the URI.

Learn more in the PHP documentation for [function arguments](https://www.php.net/manual/en/functions.arguments.php), [named parameters](https://php.watch/versions/8.0/named-parameters), and this helpful [blog article](https://stitcher.io/blog/php-8-named-arguments).

```
// Named Arguments
ApiClient::get(
    uri: 'groups'
);

// Positional Arguments
ApiClient::get('groups');
```

### GET Requests

[](#get-requests)

The endpoint starts without a leading `/` after `/api/v1/`. The Okta API documentation provides the full endpoint, so remove the `/api/v1/` when copy and pasting the endpoint.

See the [List all groups](https://developer.okta.com/docs/reference/api/groups/#list-groups) API documentation as reference for the examples below.

With the API Client, you use the `get()` method with the endpoint `groups` as the `uri` argument.

```
ApiClient::get('groups');
```

You can also use variables or database models to get data for constructing your endpoints.

```
// Get a list of records
// https://developer.okta.com/docs/reference/api/groups/#list-groups
$records = ApiClient::get('groups');

// Use variable for endpoint
$endpoint = 'groups';
$records = ApiClient::get($endpoint);

// Get a specific record
// https://developer.okta.com/docs/reference/api/groups/#get-group
$group_id = '0og1ab2c3D4E5F6G7h8i';
$record = ApiClient::get('groups/' . $group_id);

// Get a specific record using a variable
// This assumes that you have a database column named `api_group_id` that
// contains the string with the Okta ID `0og1ab2c3D4E5F6G7h8i`.
$okta_group = \App\Models\OktaGroup::where('id', $id)->firstOrFail();
$record = ApiClient::get('groups/' . $okta_group->api_group_id);
```

#### GET Requests with Query String Parameters

[](#get-requests-with-query-string-parameters)

The second positional argument or `data` named argument of a `get()` method is an optional array of parameters that is parsed by the API Client and the [Laravel HTTP Client](https://laravel.com/docs/10.x/http-client#get-request-query-parameters) and rendered as a query string with the `?` and `&` added automatically.

##### API Request Filtering

[](#api-request-filtering)

The Okta API uses `profile` child arrays for several resources. Most metadata that you define for a user or group will be in the profile. When searching for values, you use dot notation (ex. `profile.name`) to access to these attributes. Learn more in the [filter](https://developer.okta.com/docs/reference/core-okta-api/#filter) documentation. You will see references to `filter` and `search`, however it is recommended to use `search` for all queries.

##### API Response Filtering

[](#api-response-filtering)

You can also use [Laravel Collections](https://laravel.com/docs/10.x/collections#available-methods) to filter and transform results, either using a full data set or one that you already filtered with your API request.

See [Using Laravel Collections](#using-laravel-collections) to learn more.

##### Search for Records with Specific Name

[](#search-for-records-with-specific-name)

>

```
// Named Arguments
$records = ApiClient::get(
    uri: 'groups',
    data: ['search' => 'profile.name eq "Hack the Planet Engineers"']
);

// Positional Arguments
$records = ApiClient::get('groups', [
    'search' => 'profile.name eq "Hack the Planet Engineers"'
]);

// This will parse the array and render the query string
// https://mycompany.okta.com/api/v1/groups?search=profile.name+eq+%22Hack%20the&%20Planet%20Engineers%22
```

##### List all deprovisioned users

[](#list-all-deprovisioned-users)

>

```
$records = ApiClient::get(
    uri: 'users',
    data: ['search' => 'status eq "DEPROVISIONED"']
);

// This will parse the array and render the query string
// https://mycompany.okta.com/api/v1/groups?search=status+eq+%22DEPROVISIONED%22
```

##### List all users in a specific department

[](#list-all-users-in-a-specific-department)

>

```
$records = ApiClient::get(
    uri: 'users',
    data: ['search' => 'profile.department eq "Engineering"']
);

// This will parse the array and render the query string
// https://mycompany.okta.com/api/v1/groups?search=profile.department+eq+%22Engineering%22
```

### POST Requests

[](#post-requests)

The `post()` method works almost identically to a `get()` request with an array of parameters, however the parameters are passed as form data using the `application/json` content type rather than in the URL as a query string. This is industry standard and not specific to the API Client.

You can learn more about request data in the [Laravel HTTP Client documentation](https://laravel.com/docs/10.x/http-client#request-data).

```
// Create a group
// https://developer.okta.com/docs/reference/api/groups/#add-group
$record = ApiClient::post(
    uri: 'groups',
    data: [
        'profile' => [
            'name' => 'Hack the Planet Engineers',
            'description' => 'This group contains engineers that have proven they are elite enough to hack the Gibson.'
        ]
    ]
);
```

### PATCH Requests

[](#patch-requests)

> Partial updates are not supported on all endpoints. For example, they are supported on the users endpoint, but not on the groups endpoint. For endpoints that don't support partial updates, you will need to provide all of the attributes (ex. the entire profile). This may require fetching the record and overriding the value of the specific key in the array and passing the entire array back to the API client `data` argument.

The `patch()` method is used for updating one or more attributes on existing records. A patch is used for partial updates. If you want to update and replace the attributes for the **entire** existing record, you should use the [put() method](#put-requests).

You need to ensure that the ID of the record that you want to update is provided in the first argument (URI). In most applications, this will be a variable that you get from your database or another location and won't be hard-coded.

```
// Update a group
// https://developer.okta.com/docs/reference/api/groups/#update-group
$group_id = '00g1ab2c3D4E5F6G7h8i';
$record = ApiClient::put(
    uri: 'groups/' . $group_id,
    data: [
        'profile' => [
            'description' => 'This group contains engineers that have liberated the garbage files.'
        ]
    ]
);
```

> **Internal Developer Note:** The Okta API does not support PATCH requests and uses non-standard POST requests for partial updates. The `patch()` method is used in the Okta API Client for improved developer experience, and we use the Laravel HTTP Client `post()` method behind the scenes. You can use the `post()` method in the Okta API Client for updating records without any issues, this is just an overlay to comply with industry conventions for using `PATCH`.

### PUT Requests

[](#put-requests)

The `put()` method is used for updating and replacing the attributes for an **entire** existing record. If you want to update one or more attributes **without updating the entire existing record**, use the [patch() method](#patch-requests). For most use cases, you will want to use the `patch()` method to update records.

You need to ensure that the ID of the record that you want to update is provided in the first argument (URI). In most applications, this will be a variable that you get from your database or another location and won't be hard-coded.

```
// Update a group
// https://developer.okta.com/docs/reference/api/groups/#update-group
$group_id = '00g1ab2c3D4E5F6G7h8i';
$record = ApiClient::put(
    uri: 'groups/' . $group_id,
    data: [
        'profile' => [
            'name' => 'Hack the Planet Engineers',
            'description' => 'This group contains engineers that have revealed to the world their elite skills.'
        ]
    ]
);
```

### DELETE Requests

[](#delete-requests)

The `delete()` method is used for methods that will destroy the resource based on the ID that you provide.

Keep in mind that `delete()` methods will return different status codes depending on the vendor (ex. 200, 201, 202, 204, etc). Okta's API will return a `204` status code for successfully deleted resources. You should use the `$response->status->successful` boolean for checking results.

```
// Delete a group
// https://developer.okta.com/docs/reference/api/groups/#remove-group
$group_id = '00g1ab2c3D4E5F6G7h8i';
$record = ApiClient::delete('groups/' . $group_id);
```

### Class Methods

[](#class-methods)

The examples above show basic inline usage that is suitable for most use cases. If you prefer to use classes and constructors, the example below will be helpful.

```
