OAuth 2.0 authorization

Sage Intacct uses the OAuth 2.0 authorization standard to allow REST API clients to obtain secure access to our web services on behalf of specific resource owners. Client applications can use the authorization code or the client credentials grant type depending on the type of access required.

Authorization code grant type

The authorization code grant type is supported for applications in which a user can click a button or link to grant the application access to their data in Sage Intacct. The OAuth 2.0 authorization code flow involves the following steps:

  1. The user signs into your application and clicks a link to authorize your application to access their Sage Intacct data.
  2. A Sage Intacct log in screen is displayed and the user logs in.
  3. The user is prompted to grant your application access to their data and must click Accept .
  4. The authentication server responds by redirecting the user to the registered callback URI with an authorization code in the query string.
  5. Your application captures the authorization code and uses it to request an access token.
  6. Your application receives the access token and includes it in the headers of all requests to the REST API.

Authorization code request

To request an authorization code, send a GET request to the following endpoint with the listed parameters:

Endpoint: https://api.intacct.com/ia/api/v1/oauth2/authorize

Parameter
Value
response_type code
client_id Your application's client ID
redirect_uri The redirect URI set for your application in the Sage App Registry
state A random value to use to validate the response
scope Use offline_access if you want to get a refresh token in addition to an access token

Sample authorization code request:

Copy
Copied
    https://api.intacct.com/ia/api/v1/oauth2/authorize?response_type=code&client_id=*********&redirect_uri=https://mysite.com/callback.php&state=123456&scope=offline_access

Sample redirect URI response with code and state in query string:

Copy
Copied
https://mysite.com/callback.php?code=g0ZGZmNjVmOWIjNTk2NTk4ZTYyZGI3&state=123456

Validate that the response is authentic by verifying that the state value matches the value sent in the request. Extract the authorization code from the response, then include it in a request for an access token.

Access token request

To request an access token, send a POST request to the following endpoint with the listed parameters:

https://api.intacct.com/ia/api/v1/oauth2/token

Parameter
Value
grant_type authorization_code
code The authorization code from the previous response
redirect_uri The redirect URI set for your application in the Sage App Registry
client_id Your application's client ID
client_secret Your application's client secret

Sample access token request:

Copy
Copied
curl --location --request POST 'https://api.intacct.com/api/v1/oauth2/token' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'grant_type=authorization_code' \
--data-urlencode 'code=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjbGllbnRJZCI6IjkyZDRkY2Y0MTRlNmEyYzNkNzhkLlNhZ2VfSW50YWNjdF9VSS5hcHAuc2FnZS5jb20iLCJjbnlJZCI6Im9hdXRoMiIsImF1dGh6Q29kZSI6IjQ2MTkyMTYxNGZmMTM5ODcwMWQxY2UzOTdjZjI5M2M3ZWUwNDY5MjUiLCJ1c2VySWQiOiJBZG1pbiJ9.49P6x_nOqwTe5_Cr-MiMYTI2q9KOsvlyIGzTQgk7nc4' \
--data-urlencode 'redirect_uri=https://mysite.com/callback.php' \
--data-urlencode 'client_id=*********' \
--data-urlencode 'client_secret=*********'

Sample response, if a refresh token was requested in the authorization code request:

Copy
Copied
{
  "token_type": "Bearer",
  "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL2FwaS5pbnRhY2N0LmNvbSIsImlhdCI6MTcyNjIxODkyMSwiZXhwIjoxNzI2MjIyNTIxLCJjbGllbnRJZCI6ImQ0ZjJiNmIzMTgxNzRiOWE2MGE3LklOVEFDQ1QuYXBwLnNhZ2UuY29tIiwiY255SWQiOiJvYXV0aDJfbWFpbjIiLCJjbnlLZXkiOiI0NTIwODgwMiIsInVzZXJJZCI6IkFkbWluIiwidXNlcktleSI6IjEiLCJzZXNzaW9uSWQiOiJ1MHRGV1UtdEpRTjNjSnlNeTRBVHV0MzhBM1p3bkx0TFJWbE5YUEhoZDNDY2pNdUFFN3MwRlFOMyIsImVudGl0eUtleSI6IjQiLCJlbnRpdHlJZCI6IkNlbnRyYWwgUmVnaW9uIn0.bWD3UmTeKa1Y-R-ZJDg1NwaZcAfcvpeBxWZALNQkZFQ",
  "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjbGllbnRJZCI6IjkyZDRkY2Y0MTRlNmEyYzNkNzhkLlNhZ2VfSW50YWNjdF9VSS5hcHAuc2FnZS5jb20iLCJjbnlJZCI6Im9hdXRoMiIsInJlZnJlc2hUb2tlbiI6IjEwNjlmMjkzN2E1YTgwNGY2MzM4MDQyZWNhMTQyYTMyMTcxYjBhZTgifQ.qlXFKgGpEIJXY2CnEyuedS9IQqRpQP52IhQULzNSF_w",
  "expires_in": 43200
}

Refresh token request

To request a refresh token, send a POST request to the following endpoint with the listed parameters:

https://api.intacct.com/ia/api/v1/oauth2/token

Parameter
Value
grant_type refresh_token
refresh_token The refresh token from the previous response
client_id Your application's client ID
client_secret Your application's client secret
entity_id (optional) The ID of the entity that you want the user to have access to. Default is a top-level access token.

Sample refresh token request:

Copy
Copied
curl --location --request POST 'https://api.intacct.com/api/v1/oauth2/token' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'grant_type=refresh_token' \
--data-urlencode 'refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjbGllbnRJZCI6IjkyZDRkY2Y0MTRlNmEyYzNkNzhkLlNhZ2VfSW50YWNjdF9VSS5hcHAuc2FnZS5jb20iLCJjbnlJZCI6Im9hdXRoMiIsInJlZnJlc2hUb2tlbiI6IjEwNjlmMjkzN2E1YTgwNGY2MzM4MDQyZWNhMTQyYTMyMTcxYjBhZTgifQ.qlXFKgGpEIJXY2CnEyuedS9IQqRpQP52IhQULzNSF_w' \
--data-urlencode 'client_id=92d4dcf414e6a2c3d78d.app.sage.com' \
--data-urlencode 'client_secret=a55a58f1aeaf09116cbe1bf28025c183e778268c'

Sample response:

Copy
Copied
{
  "token_type": "Bearer",
  "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL2FwaS5pbnRhY2N0LmNvbSIsImlhdCI6MTcyNjIxODkyMSwiZXhwIjoxNzI2MjIyNTIxLCJjbGllbnRJZCI6ImQ0ZjJiNmIzMTgxNzRiOWE2MGE3LklOVEFDQ1QuYXBwLnNhZ2UuY29tIiwiY255SWQiOiJvYXV0aDJfbWFpbjIiLCJjbnlLZXkiOiI0NTIwODgwMiIsInVzZXJJZCI6IkFkbWluIiwidXNlcktleSI6IjEiLCJzZXNzaW9uSWQiOiJ1MHRGV1UtdEpRTjNjSnlNeTRBVHV0MzhBM1p3bkx0TFJWbE5YUEhoZDNDY2pNdUFFN3MwRlFOMyIsImVudGl0eUtleSI6IjQiLCJlbnRpdHlJZCI6IkNlbnRyYWwgUmVnaW9uIn0.bWD3UmTeKa1Y-R-ZJDg1NwaZcAfcvpeBxWZALNQkZFQ",
  "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjbGllbnRJZCI6IjkyZDRkY2Y0MTRlNmEyYzNkNzhkLlNhZ2VfSW50YWNjdF9VSS5hcHAuc2FnZS5jb20iLCJjbnlJZCI6Im9hdXRoMiIsInJlZnJlc2hUb2tlbiI6IjEwNjlmMjkzN2E1YTgwNGY2MzM4MDQyZWNhMTQyYTMyMTcxYjBhZTgifQ.qlXFKgGpEIJXY2CnEyuedS9IQqRpQP52IhQULzNSF_w",
  "expires_in": 43200
}

Client credentials grant type

The client credentials grant type is supported for applications needing direct access without user interaction.

To use the client credentials grant type, the client application must be associated with a Web Services user and explicitly authorized in your company's configuration:

  1. Create a Web Services user:
    • If you already have a Web Services user defined for your integration, you can skip this step.
    • Log in to your company and navigate to Company > Admin > Web Services Users > Add .
    • Create a new Web Services user and assign the necessary permissions or role.
  2. Authorize the client application:
    • Navigate to Company > Setup > Company > [Edit] > Security > Authorized Client Applications > Add .
    • Enter the Client ID for your application.
    • Enter the Web Services User ID created in step 1. This field is case-sensitive, so ensure you enter it exactly as it was created.

After these steps are completed, your client application is authorized to request client credentials tokens.

To request client credentials tokens, send a POST request to the following endpoint with the listed parameters, including either username or session_id:

https://api.intacct.com/ia/api/v1/oauth2/token

Parameter
Value
grant_type client_credentials
client_id Your application's client ID
client_secret Your application's client secret
username (optional) User ID to log in as, in userId@companyId|entityId format. Required if not using session_id.
session_id (optional) Valid UI or API session ID. Required if not using username.

Sample client credentials request with username:

Copy
Copied
curl -s --request POST 'https://api.intacct.com/ia/api/v1/oauth2/token' \
 --header 'Content-Type: application/json' \
 --data-raw '{
   "grant_type": "client_credentials",
   "client_id": "*********",
   "client_secret": "*********",
    "username": "Admin@oauth2_main2|Central Region" }'

Sample client credentials request with session_id:

Copy
Copied
curl -s --request POST 'https://api.intacct.com/ia/api/v1/oauth2/token' \
--header 'Content-Type: application/json' \
--data-raw '{
  "grant_type": "client_credentials",
  "client_id": "*********",
  "client_secret": "*********",
  "session_id": "CkAU0b-G_RCd42yvvJvCo7aUEJwmVwcCFNG-xJE6neNsr7ybwuKriRKe" }'

Sample response:

Copy
Copied
{
  "token_type": "Bearer",
  "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL2FwaS5pbnRhY2N0LmNvbSIsImlhdCI6MTcyNjIxODkyMSwiZXhwIjoxNzI2MjIyNTIxLCJjbGllbnRJZCI6ImQ0ZjJiNmIzMTgxNzRiOWE2MGE3LklOVEFDQ1QuYXBwLnNhZ2UuY29tIiwiY255SWQiOiJvYXV0aDJfbWFpbjIiLCJjbnlLZXkiOiI0NTIwODgwMiIsInVzZXJJZCI6IkFkbWluIiwidXNlcktleSI6IjEiLCJzZXNzaW9uSWQiOiJ1MHRGV1UtdEpRTjNjSnlNeTRBVHV0MzhBM1p3bkx0TFJWbE5YUEhoZDNDY2pNdUFFN3MwRlFOMyIsImVudGl0eUtleSI6IjQiLCJlbnRpdHlJZCI6IkNlbnRyYWwgUmVnaW9uIn0.bWD3UmTeKa1Y-R-ZJDg1NwaZcAfcvpeBxWZALNQkZFQ",
  "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL2FwaS5pbnRhY2N0LmNvbSIsImlhdCI6MTcyNjIxODkyMSwiZXhwIjoxNzMzOTk0OTIxLCJjbGllbnRJZCI6ImQ0ZjJiNmIzMTgxNzRiOWE2MGE3LklOVEFDQ1QuYXBwLnNhZ2UuY29tIiwiY255SWQiOiJvYXV0aDJfbWFpbjIiLCJyZWZyZXNoVG9rZW4iOiJiMzdiODg2ODcyNTQwZjk5Yjg2ZWQ4OWFiMjFiMWU0YmNlODMwZjUzIn0.2XgOzUcHQKGG7JkdF6gBHo6vmawld_TeCrjiyAXNBVw",
  "expires_in": 43200
}

Entity-level access

Multi-entity companies have a top-level parent entity with one or more sub-entities underneath. These sub-entities may own their records, and the REST API provides mechanisms to interact with them. You must first obtain a top-level access token as described in Authorization code grant type or Client credentials grant type.

Send a single request to a sub-entity

To access records owned by a sub-entity within the multi-entity company structure, use the X-IA-API-Param-Entity header to specify the entity context, for example Central Region:

Copy
Copied
# Get Vendor List with 'X-IA-API-Param-Entity' Header
curl --request GET 'https://api.intacct.com/ia/api/v1/objects/vendor' \
--header 'Content-Type: application/json' \
--header 'X-IA-API-Param-Entity: Central Region' \
--header 'Authorization: Bearer <top_level_access_token>'

Change to a sub-entity access token

You can also switch from a top-level access token to a sub-entity access token. This allows you to utilize the new access token in subsequent REST API calls for creating and retrieving sub-entity records. To obtain the sub-entity token, use the location_id parameter in the Refresh token request:

Copy
Copied
# Refresh Token to Get Sub-Entity Access Token
curl -k --request POST 'https://api.intacct.com/ia/api/v1/oauth2/token' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'grant_type=refresh_token' \
--data-urlencode 'client_id=*********' \
--data-urlencode 'client_secret=*********' \
--data-urlencode 'refresh_token=<refresh_token>' \
--data-urlencode 'location_id=Central Region'

You can now use the new token to access records specific to the sub-entity.

Test your OAuth 2.0 integration

This section provides the steps and PHP code samples for testing your integration in a development environment. This can be done either locally, using a self-signed SSL certificate and a custom domain mapped to localhost, or with a production domain.

Prerequisites

For local testing:

  • Self-signed SSL certificate - Required for HTTPS communication.
  • Hosts file update - Map a custom domain (e.g., mycustomdomain.com ) to localhost .
  • Sage app registry - Add the custom domain to your application's callback URI list. See the Get API keys topic for more details.

For testing with a production domain:

  • Valid SSL certificate - Ensure your domain has a valid SSL certificate.
  • Domain configuration - Set up the domain and ensure it points to your server.
  • Sage app registry - Add the production domain to your application's callback URI list.

Test your integration

You can download and extract the OAuth 2.0 example zip file that contains these code samples:

  • auth_flow_sample.php
  • callback.php
  • exchange_token.php

Replace mycustomdomain with your domain name. Provide the client ID and client secret values where needed. Make sure to store your credentials securely and exclude files that contain them from your remote repository.

  1. Implement the OAuth 2.0 authorization flow.

    This script generates an authorization URL and provides a link for obtaining an access token ( auth_flow_sample.php).

    Copy
    Copied
    <?php
    
    define("CALLBACK_URL", "https://mycustomdomain.com/callback.php");
    define("AUTH_URL", "https://api.intacct.com/ia/api/v1-beta2/oauth2/authorize");
    define("CLIENT_ID", "***********");
    define("SCOPE", "offline_access"); // Optional, used to return the refresh token with the access token.
    
    $url = AUTH_URL . "?"
      . "state=active"
      . "&response_type=code"
      . "&client_id=" . CLIENT_ID
      . "&scope=" . SCOPE
      . "&redirect_uri=" . CALLBACK_URL;
    
    ?>
    <a href="<?php echo $url; ?>">Get Access Token</a>
  2. Handle the OAuth 2.0 callback and token exchange.

    This script handles the OAuth 2.0 callback and exchanges the authorization code for an access token (callback.php).

    Copy
    Copied
    <?php
    
    $code = $_GET['code'];
    $postArray = array(
     'grant_type' => 'authorization_code',
     'client_id' => '******',
     'client_secret' => '*******',
     'code' => $code,
     'redirect_uri' => 'https://mycustomdomain.com/callback.php'
    );
    
    $curl = curl_init();
    curl_setopt_array($curl, array(
     CURLOPT_URL => "https://api.intacct.com/ia/api/v1-beta2/oauth2/token",
     CURLOPT_RETURNTRANSFER => true,
     CURLOPT_SSL_VERIFYPEER => false, // Ignore SSL certificate validation
     CURLOPT_POSTFIELDS => http_build_query($postArray),
     CURLOPT_HTTPHEADER => array("Content-Type: application/x-www-form-urlencoded")
    ));
    
    $response = curl_exec($curl);
    $decoded = json_decode($response, true);
    $access_token = $decoded['access_token'];
    $refresh_token = $decoded['refresh_token'];
    curl_close($curl);
    
    echo "Access Token: " . $access_token . "<BR><BR>";
    
    ?>
    Exchage Refresh token for new access token <a href="exchange_token.php?refresh_token=<?php echo $refresh_token; ?>">here</a>.
  3. Exchange the refresh token for a new access token.

    This script exchanges a refresh token for a new access token. Optionally, you can include an entity_id parameter to retrieve an entity-level access token (exchange_token.php).

    Copy
    Copied
    <?php
    
    $refresh_token = $_GET['refresh_token'];
    $postArray = array(
     'grant_type' => 'refresh_token',
     'client_id' => '********',
     'client_secret' => '********',
     'refresh_token' => $refresh_token,
     'entity_id' => '10' // Optional, specify entity_id for entity-level access token. Obtain a top-level access token if entity_id is omitted.
    );
    
    $curl = curl_init();
    curl_setopt_array($curl, array(
     CURLOPT_URL => "https://api.intacct.com/ia/api/v1-beta2/oauth2/token",
     CURLOPT_RETURNTRANSFER => true,
     CURLOPT_SSL_VERIFYPEER => false, // Ignore SSL certificate validation
     CURLOPT_POSTFIELDS => http_build_query($postArray),
     CURLOPT_HTTPHEADER => array("Content-Type: application/x-www-form-urlencoded")
    ));
    
    $response = curl_exec($curl);
    $decoded = json_decode($response, true);
    $access_token = $decoded['access_token'];
    
    curl_close($curl);
    
    echo "New Access Token: " . $access_token;
    
    ?>

Tutorials

See the PHP tutorial or the Node.js tutorial for more details on how to authenticate with the OAuth2 server, and send requests to the REST API.