---
updatedAt: 2026-04-28T00:00:16.000Z
---

Fetch the complete documentation index at: https://docs.gusto.com/embedded-payroll/llms.txt. Use this file to discover all available pages before exploring further. Append .md to any documentation page URL to get its markdown version.

# Authentication and authorization

Gusto Embedded supports two types of tokens used throughout development:

* `system-level`
* `company-level`

System access tokens allow you to carry out application-level actions such as:

* [Creating a partner-managed company](doc:ref:post-v1-partner-managed-companies)
* [Viewing invoices](https://docs.gusto.com/embedded-payroll/reference/get-invoices-invoice-period)
* [Getting all events](https://docs.gusto.com/embedded-payroll/reference/get-events)

Company access tokens allow you to carry out company-level actions, such as:

* [Getting company employees](get-v1-companies-company_id-employees)
* [Submitting a payroll for a company](https://docs.gusto.com/embedded-payroll/reference/put-v1-companies-company_id-payrolls-payroll_id-submit)

Each partner-managed company has a single access token that cannot be used to access other companies.

## System access tokens

Starting with version `v2024-04-01`, partner API tokens are deprecated in favor of system access tokens. These tokens improve security and allow you to:

* Manage multiple applications per organization
* Perform system-level actions like creating partner-managed companies and subscribing to webhooks

<Callout icon="📘" theme="info">
  The token will expire after two hours. Unlike company access tokens, you can request additional system tokens anytime, even if a previous one is still active. Because of this, you don’t need to store the token unless you prefer to reuse it.
</Callout>

To get a system access token, send a POST request to the [/oauth/token](https://docs.gusto.com/embedded-payroll/reference/oauth-access-token) endpoint with `grant_type` set to `system_access`.

```curl
curl --location --request POST 'https://api.gusto-demo.com/oauth/token' \
--header 'Content-Type: application/json' \
--data-raw '{
  "client_id": "{{client_id}}",
  "client_secret": "{{client_secret}}",
  "grant_type": "system_access"
}'
```

Here’s what the response looks like:

```json
{
  "access_token": "PF9RH-QVnURJAY9-CHX0CC71HOPq7rClhJTdLdZOLt0",
  "token_type": "Bearer",
  "created_at": 1728518070,
  "expires_in": 7200
}
```

To use the token, add it as a bearer token in the Authorization header:

```
Content-Type: application/json

Authorization: Bearer PF9RH-QVnURJAY9-CHX0CC71HOPq7rClhJTdLdZOLt0
```

## Company access tokens

To call the API on behalf of a company, you need a company-level access token. You can get one by creating a partner-managed company or going through the in-app authorization code flow when migrating to embedded payroll.

When you create a partner-managed company, you'll receive:

* `Access_token`
* `Refresh_token`
* `expires_in` (how many seconds before the token expires)
* `company_uuid`

```json
{
  "access_token": "JKrGqRyrYfY1PB0YhsuRkbrrWBJ5iSUODDNA28D3yMc",
  "refresh_token": "T0jHy4Oc3FWi7hDPEMPdpbGiLpB0rWeb1ZJOJVB36oU",
  "company_uuid": "d525dd21-ba6e-482c-be15-c2c7237f1364",
  "expires_in": 7200
}
```

<Callout icon="📘" theme="info">
  Tokens use URL-safe base64 encoding.
</Callout>

### Refreshing access tokens

Access tokens expire after two hours. If expired, API calls will return a `401 Unauthorized` error.

To refresh a token, send a POST request to the [/oauth/token](https://docs.gusto.com/embedded-payroll/reference/oauth-access-token)  endpoint with:

* `client_id` and `client_secret` from your [application](https://dev.gusto.com/applications)
* `refresh_token`
* `grant_type` set to `refresh_token`

```curl
curl --location --request POST 'https://api.gusto-demo.com/oauth/token' \
--header 'Content-Type: application/json' \
--data-raw '{
  "client_id": "{{client_id}}",
  "client_secret": "{{client_secret}}",
  "refresh_token": "T0jHy4Oc3FWi7hDPEMPdpbGiLpB0rWeb1ZJOJVB36oU",
  "grant_type": "refresh_token"
}'
```

You’ll receive a new access and refresh token:

```json
{
  "access_token": "737HdeXfIqgx-NfaUFRuhV7JDe6ns6ptanJSMuQzjlc",
  "token_type": "bearer",
  "expires_in": 7200,
  "refresh_token": "iEjL96L9Pndwmi-xVX3Q-xbrvvhnjHYGX87sopgGJ8E"
}
```

When you use the new access token in a Gusto API call, your previous refresh token will be revoked.

#### Token management tips

Access and refresh token pairs are tied to a resource, typically a company. Here’s an example structure for storing Gusto tokens:

![ER](https://lucid.app/publicSegments/view/0b198659-cf46-48f9-ae4a-a4f3f773dba5/image.png "Example")

* `company_uuid`: the Gusto company ID. You might link this to your own companies table.
* `access_token_expiration`: based on the `expires_in` value. Since the default is 7200 seconds (2 hours), set your expiration check slightly earlier, for example, "`expires_in` - 60."

To prevent token refresh race conditions, lock the row when refreshing tokens. Here’s one way to handle refresh logic:

1. Confirm the access token needs refreshing by checking if `access_token_expiration` is in the past or if a `401` error was returned
2. Lock the row for the `company_uuid`
3. Refresh the token
4. Update the access and refresh tokens and reset the expiration timestamp
5. Unlock the row
6. Use the new access token
7. Make sure all processes use the most recent token