This section explains how to use OAuth 2.0 to allow Sage Accounting users to authorize your app to access their data without sharing their actual login details.

With every API request, you must supply a valid Access Token within the Authorization header:

Authorization: Bearer ‹‹Access Token››

An Access Token belongs to a single user account in Accounting. This means that a user can access all the data of their related businesses (a user might have access to one or more businesses associated to their lead business).

When using the Accounting API with an API client, you can select the business you want to read or amend data by providing the X-Business header in each request that specifies the Business ID. The X-Business header is optional – if not set, the API will return data of the user’s lead business.


Obtain an Access Token

The steps outlined here explain how to obtain the access token and how to use the refresh token to get a new access token if the current one has expired.

1. Authorization request

Redirect to the Sage Accounting authorization server https://www.sageone.com/oauth2/auth/central?filter=apiv3.1 with the relevant URL query parameters:

Required params
client_id The ID of your app.
response_type The type of response. This is always code.
redirect_uri Your app callback URL. This is where your users will be sent after authorizing. You have to use a callback URL that has been configured when registering the app.
Optional params
scopes Lets you specify the type of access to allow. Can be readonly or full_access.
country If you already know the country of your business, you can pass a country parameter to simplify the auth flow by not showing the list of available countries. Valid values are
ca (for Canada)
de (for Germany)
es (for Spain)
fr (for France)
gb (for United Kingdom)
ie (for Ireland)
us (for United States)
filter You should always set this filter to apiv3.1 to only see countries that support API v3.1. This parameter is ignored if a country is provided.
state A string used to protect against CSRF attacks. Although state is optional, we recommend including this. This should be a string that cannot be guessed.
Note: If the value of the state parameter returned in the response does not match the state that was provided in the original request, it does not originate from the original request and you must not continue.
code_challenge This PKCE parameter is a hashed or unhashed random string with a length between 43 and 128. See RFC 7636 Section 4.2 for detailed instructions how to create a value for this attribute.
code_challenge_method This PKCE parameter contains the name of the hash function used to create the code_challenge. Only S256 or plain are valid. You must use S256 unless your client is unable to compute SHA256 hashes.
Example Authorization Request
GET https://www.sageone.com/oauth2/auth/central?filter=apiv3.1&response_type=code&client_id=4b6xxxxxxx710&redirect_uri=https://myapp.com/auth/callback&scope=full_access&state=random_string

When this endpoint is hit, the user is prompted to login to Sage Accounting and asked if they want to authorize your app.

If the user allows access to your app, they are redirected to the callback URL along with an authorization code which can be read from the response:

GET https://myapp.com/auth/callback?code=GB%2F12abcxxxxxxxxxxxxf7d&country=GB&state=random_string
Possible errors

2. Exchange the authorization code for the access token

Note: the authorization code obtained above is for single-use only and expires after 60 seconds.

To exchange the authorization code for an access token and resource_owner_id, you should now make a POST request with Content-Type: application/x-www-form-urlencoded to the token endpoint:

POST https://oauth.accounting.sage.com/token
Required parameters
client_id Your app Client ID. You can get this value from the App Details.
client_secret Your app Client Secret. You can get this value from the App Details.
code The authorization code provided in the response from the previous step.
grant_type This must be authorization_code.
redirect_uri Your app callback URL. You must provide this value exactly as configured in the App Details, you must not add any additional params.
Optional params
code_verifier This is the original (not hashed) random string, that was generated by the client before requesting an authorization code. If the autorization code was requested with PKCE parameter, the code_verifier is required for requesting an access token. See section 4.5 of RFC 7636 for details.
Example request
POST https://oauth.accounting.sage.com/token
Content-Type: application/x-www-form-urlencoded

client_id=4b6xxxxxxx710
&client_secret=iNuxxxxxxxxxxtm9
&code=12axxxxxxxxxxxxf7d
&grant_type=authorization_code
&redirect_uri=https://myapp.com/auth/callback

The response includes the access token:

{
  "access_token": "eyJhbGciOiJSUzUxMiIsImtpZCI6IjNwX2VsSXBnQkNwY0dRYkpzNGVUTFlzYWdQVjQ1Z01pU1RidElfSWVxTzA9In0.eyJqdGkiOiJiMWVmZWQ1Zi01YWE2LTQ2YjctYmNkYi05ZDM5NjgzMDFjZWYiLCJpYXQiOjE1Mjc1MTAwMzMsImV4cCI6MTUyNzUxMDMzMywiaXNzIjoib2F1dGguYXdzLnNiYy1hY2NvdW50aW5nLnNhZ2UuY29tIiwic3ViIjoiNDQ0IiwiYXVkIjoiYXBpLnNiYy1hY2NvdW50aW5nLnNhZ2UuY29tIiwiYXpwIjoiMzMzIiwiY291bnRyeSI6InVzIiwic2NvcGVzIjoiYWNjb3VudHM6cncgYWNjb3VudGluZzpydyBjb3JlOnJ3In0.NnE2Wxt5BTZa6m2vh8fcH54gyWRFciZWuVq8KrtbLJLatMUHxQAo4WY3Tht6HmhpI-oq-yxg5LADtvRRDbxFrBTnXOGm6Go3-UdBC6OADo_18PrtpN6e1FiY-BV9k16zJyiB_R8pQXPTVSp_9XXBqpNSJGqJ__sIdYPLx0wrkWorI73DLR_0KSFVs5dFufPpdhmBZ_BhfSd3Fe1B4ErVo8m28jP_6QFNt2ssZAxufidVnk3wXAyqN0fka-tMjK7OrEenGQl_PAVLxhvMhpw6PRDxKYfoPUmmcaqTkZLK7aTFP0m23BtRwSs6ezRqzQzsbUgIWkUTSzh6tfVFDVheEz8tLhctU_OqvcoNmufCMV7kDYQ0_JRcGnMN5MTaMnVG49NQ0sWsmsD9drOfuN6bJeJFu-F5GAL3BAUP6UZcunj0a9I1WAEs0Zf5sEEQkDK2VOJ-mh5JVOkdbUnfverETaI5i0X_kCVgjP1q1Glq5xv8Mm85kO-0kLuO3VOHAM70_ln9e8_gcIIpBuerVFrwFUX1SFBu1JUNWq6_TfIh3WkehkYpK33aQy-dnRg0dgkx_MVyGenIvqvcTGzd9l7SNgqyzoOeY01LJb-NQBKDgGFbyNrUhvud7f4O68AaabDDntSQmlBrxPNqF-bAPYlpb90kD6NKUb9YiM7z6wnij4o",
  "scopes": "full_access",
  "token_type": "bearer",
  "expires_in": 300,
  "refresh_token": "eyJhbGciOiJSUzUxMiIsImtpZCI6IjNwX2VsSXBnQkNwY0dRYkpzNGVUTFlzYWdQVjQ1Z01pU1RidElfSWVxTzA9In0.eyJqdGkiOiI0Yjk1MmQxYy04OGEwLTQxZjUtYjdiNS0wMDVmOTc4ODg5YjYiLCJpYXQiOjE1Mjc1MTAwNjIsImV4cCI6MTUyNzUxMDM2MiwiaXNzIjoib2F1dGguYXdzLnNiYy1hY2NvdW50aW5nLnNhZ2UuY29tIiwic3ViIjoiNDQ0IiwiYXVkIjoiYXBpLnNiYy1hY2NvdW50aW5nLnNhZ2UuY29tIiwiYXpwIjoiMzMzIiwiY291bnRyeSI6InVzIiwic2NvcGVzIjoiYWNjb3VudHM6cncgYWNjb3VudGluZzpydyBjb3JlOnJ3In0.QQZ4bzOnrGUnNjYsJfK_FBNxKz1jGpF0Z-ur4wCoOTERi2W4iosX1N7ksdkRfUKSJlWlOfPp2XNMxszONmqZHczPjmVNUc0er1qo-oDmPJfbcH305sWmMFqspfoaUOCVXu_TcAh5Dz_zBUAznhBGbx603SBidvjMIa9Jncy9SjjYrJqhzz4y1-vgST4hccGCbsORGB_U6yKgfS6bupzoLVDdCj2bsgG4hSiDqagXbpAsoh9vR2UBALsJz8PUEEW7qA81B7SjtLUiHbvzryKhMt-7Q631D2j_iQaGpld1aICMBfAL0h5wiJMjf6r9R-EcJOmSzWtCcTI0y-PJfprinxIo3Mg_sljfoe_0wrEikIWRQIQa3D40nhMnLtqevCftPscQjLO_vf_ERUICHTiJdXiKsSwmUp9EGKnWC_qEKOcVA-o7y_vsAuFODsUvXC9k6Z7sVCg2k37k8r4NqoJ82d_l4ZrgNnvV6VEL2xtuASIA46-MlWWTPTbuQkzyfmVQW1gg50q4tNT8XkvVil6F2rNCiLSA2vhsH8lLu8mcSfCqVaPJh9-XvtjSkKUAtjgA3aa8bJEuUXFRn-U6Z7TyMZGHRZCEg-1IZa49rOQ5FhfvD85MQL466vAdr7X1zwVqxvr1T3UW58NuKMr3FyAWRyQhp_bBB8AXdf4W1BIeYLk",
  "refresh_token_expires_in": 2678400,
  "requested_by_id": "c3c32d1c-41ba-483f-a3ff-49fdb57b9c38"
}
Possible errors

Renew an Access Token

You can use the refresh token to obtain a new access token if the current one has expired. This means that your users aren’t required to authorize your app every time you request a new token.

Send a POST request with Content-Type: application/x-www-form-urlencoded to the token endpoint https://oauth.accounting.sage.com/token

Required parameters
client_id Your app Client ID.
client_secret Your app Client Secret.
grant_type This must be refresh_token.
refresh_token The refresh token provided in the response from the previous step.
Example request
POST https://oauth.accounting.sage.com/token
Content-Type: application/x-www-form-urlencoded

client_id=4b6xxxxxxx710
&client_secret=iNuxxxxxxxxxxtm9
&grant_type=refresh_token
&refresh_token=eyJxxxxxxxxxxYLk

The response includes the new access token and a new refresh token:

{
  "access_token": "eyJhbGciOiJSUzUxMiIsImtpZCI6IjNwX2VsSXBnQkNwY0dRYkpzNGVUTFlzYWdQVjQ1Z01pU1RidElfSWVxTzA9In0.eyJqdGkiOiJiMWVmZWQ1Zi01YWE2LTQ2YjctYmNkYi05ZDM5NjgzMDFjZWYiLCJpYXQiOjE1Mjc1MTAwMzMsImV4cCI6MTUyNzUxMDMzMywiaXNzIjoib2F1dGguYXdzLnNiYy1hY2NvdW50aW5nLnNhZ2UuY29tIiwic3ViIjoiNDQ0IiwiYXVkIjoiYXBpLnNiYy1hY2NvdW50aW5nLnNhZ2UuY29tIiwiYXpwIjoiMzMzIiwiY291bnRyeSI6InVzIiwic2NvcGVzIjoiYWNjb3VudHM6cncgYWNjb3VudGluZzpydyBjb3JlOnJ3In0.NnE2Wxt5BTZa6m2vh8fcH54gyWRFciZWuVq8KrtbLJLatMUHxQAo4WY3Tht6HmhpI-oq-yxg5LADtvRRDbxFrBTnXOGm6Go3-UdBC6OADo_18PrtpN6e1FiY-BV9k16zJyiB_R8pQXPTVSp_9XXBqpNSJGqJ__sIdYPLx0wrkWorI73DLR_0KSFVs5dFufPpdhmBZ_BhfSd3Fe1B4ErVo8m28jP_6QFNt2ssZAxufidVnk3wXAyqN0fka-tMjK7OrEenGQl_PAVLxhvMhpw6PRDxKYfoPUmmcaqTkZLK7aTFP0m23BtRwSs6ezRqzQzsbUgIWkUTSzh6tfVFDVheEz8tLhctU_OqvcoNmufCMV7kDYQ0_JRcGnMN5MTaMnVG49NQ0sWsmsD9drOfuN6bJeJFu-F5GAL3BAUP6UZcunj0a9I1WAEs0Zf5sEEQkDK2VOJ-mh5JVOkdbUnfverETaI5i0X_kCVgjP1q1Glq5xv8Mm85kO-0kLuO3VOHAM70_ln9e8_gcIIpBuerVFrwFUX1SFBu1JUNWq6_TfIh3WkehkYpK33aQy-dnRg0dgkx_MVyGenIvqvcTGzd9l7SNgqyzoOeY01LJb-NQBKDgGFbyNrUhvud7f4O68AaabDDntSQmlBrxPNqF-bAPYlpb90kD6NKUb9YiM7z6wnij4o",
  "scopes": "full_access",
  "token_type": "bearer",
  "expires_in": 300,
  "refresh_token": "eyJhbGciOiJSUzUxMiIsImtpZCI6IjNwX2VsSXBnQkNwY0dRYkpzNGVUTFlzYWdQVjQ1Z01pU1RidElfSWVxTzA9In0.eyJqdGkiOiI0Yjk1MmQxYy04OGEwLTQxZjUtYjdiNS0wMDVmOTc4ODg5YjYiLCJpYXQiOjE1Mjc1MTAwNjIsImV4cCI6MTUyNzUxMDM2MiwiaXNzIjoib2F1dGguYXdzLnNiYy1hY2NvdW50aW5nLnNhZ2UuY29tIiwic3ViIjoiNDQ0IiwiYXVkIjoiYXBpLnNiYy1hY2NvdW50aW5nLnNhZ2UuY29tIiwiYXpwIjoiMzMzIiwiY291bnRyeSI6InVzIiwic2NvcGVzIjoiYWNjb3VudHM6cncgYWNjb3VudGluZzpydyBjb3JlOnJ3In0.QQZ4bzOnrGUnNjYsJfK_FBNxKz1jGpF0Z-ur4wCoOTERi2W4iosX1N7ksdkRfUKSJlWlOfPp2XNMxszONmqZHczPjmVNUc0er1qo-oDmPJfbcH305sWmMFqspfoaUOCVXu_TcAh5Dz_zBUAznhBGbx603SBidvjMIa9Jncy9SjjYrJqhzz4y1-vgST4hccGCbsORGB_U6yKgfS6bupzoLVDdCj2bsgG4hSiDqagXbpAsoh9vR2UBALsJz8PUEEW7qA81B7SjtLUiHbvzryKhMt-7Q631D2j_iQaGpld1aICMBfAL0h5wiJMjf6r9R-EcJOmSzWtCcTI0y-PJfprinxIo3Mg_sljfoe_0wrEikIWRQIQa3D40nhMnLtqevCftPscQjLO_vf_ERUICHTiJdXiKsSwmUp9EGKnWC_qEKOcVA-o7y_vsAuFODsUvXC9k6Z7sVCg2k37k8r4NqoJ82d_l4ZrgNnvV6VEL2xtuASIA46-MlWWTPTbuQkzyfmVQW1gg50q4tNT8XkvVil6F2rNCiLSA2vhsH8lLu8mcSfCqVaPJh9-XvtjSkKUAtjgA3aa8bJEuUXFRn-U6Z7TyMZGHRZCEg-1IZa49rOQ5FhfvD85MQL466vAdr7X1zwVqxvr1T3UW58NuKMr3FyAWRyQhp_bBB8AXdf4W1BIeYLk",
  "refresh_token_expires_in": 2678400,
  "requested_by_id": "c3c32d1c-41ba-483f-a3ff-49fdb57b9c38"
}

Revoke a Refresh Token

You revoke a refresh token so it is no longer valid for obtaining access tokens. Please note that you cannot revoke access tokens, they are valid during their entire lifetime of five minutes. Once a refresh token is revoked, it cannot be used to obtain a new set of access and refresh tokens, so that the user will need to authorize your app again once the access token expires.

Send a POST request with Content-Type: application/x-www-form-urlencoded to the revoke endpoint https://oauth.accounting.sage.com/revoke

Required parameters
client_id Your app Client ID.
client_secret Your app Client Secret
token The refresh token you wish to revoke.
Example request
POST https://oauth.accounting.sage.com/revoke
Content-Type: application/x-www-form-urlencoded

client_id=4b6xxxxxxx710
&client_secret=iNuxxxxxxxxxxtm9
&token=eyJxxxxxxxxxxYLk

On success, you will see the following response with HTTP status 200:

{
  "success": "ok"
}