Authentication and authorization
Gusto Embedded supports two types of tokens used throughout development:
system-levelcompany-level
System access tokens allow you to carry out application-level actions such as:
Company access tokens allow you to carry out company-level actions, such as:
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
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.
To get a system access token, send a POST request to the /oauth/token endpoint with grant_type set to system_access.
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:
{
"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_tokenRefresh_tokenexpires_in(how many seconds before the token expires)company_uuid
{
"access_token": "JKrGqRyrYfY1PB0YhsuRkbrrWBJ5iSUODDNA28D3yMc",
"refresh_token": "T0jHy4Oc3FWi7hDPEMPdpbGiLpB0rWeb1ZJOJVB36oU",
"company_uuid": "d525dd21-ba6e-482c-be15-c2c7237f1364",
"expires_in": 7200
}
Tokens use URL-safe base64 encoding.
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 endpoint with:
client_idandclient_secretfrom your applicationrefresh_tokengrant_typeset torefresh_token
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:
{
"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:

company_uuid: the Gusto company ID. You might link this to your own companies table.access_token_expiration: based on theexpires_invalue. 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:
- Confirm the access token needs refreshing by checking if
access_token_expirationis in the past or if a401error was returned - Lock the row for the
company_uuid - Refresh the token
- Update the access and refresh tokens and reset the expiration timestamp
- Unlock the row
- Use the new access token
- Make sure all processes use the most recent token
Updated about 1 month ago