Skip to content
Developer home

Authentication

  Less than to read

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
  • access_denied: This error occurs when the Sage Accounting business user chooses not to authorize your app.
  • invalid_request: This error occurs when a required parameter is missing, invalid or provided more than once.
  • invalid_scope: This error occurs when the scope provided is invalid or unknown. You should ensure that scope in your request is either readonly or full_access.
  • server_error: This generic error occurs when the authorization server encounters something unexpected that prevents it from fulfilling the request.
  • temporarily_unavailable: This error occurs when the authorization server is unavailable. We recommend waiting for 10 minutes before retrying.
  • unauthorized_client: This error occurs when the client is not authorized to perform this action. This may be due to an incorrect client_id value.
  • unsupported_response_type: This error occurs when the authorization server does not support the method being used to request the code. You should ensure that the response_type in your request is code.

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"
}
  • access_token is used for authenticating API requests. It expires after five minutes.

    Make sure to not share this token and to not store it in an unsafe location.

    Reserve up to 2048 bytes in your data storage for this token.

  • scopes informs you about the scopes, ie. access rights, that have been requested and authorized. Can be full_access and readonly
  • token_type is always bearer
  • expires_in informs you about the lifetime of the access token, which is 300 seconds/5 minutes
  • refresh_token is used to obtain new access and refresh tokens when the access token expired. The refresh token itself expires after 31 days, users will have to freshly authorize your app if you do not refresh your tokens within that time.

    You MUST NOT share this token or store it in an unsafe location, eg. an unencrypted browser session.

    Reserve up to 2048 bytes in your data storage for this token.

  • refresh_token_expires_in informs you about the lifetime of the refresh token, which is 2678400 seconds/31 days.
  • requested_by_id is the ID of the user who authorized your authorization request.
Possible errors
  • invalid_request: This error occurs when a required parameter is missing, invalid or provided more than once.
  • invalid_grant: This error occurs when the grant_type provided is invalid or unknown. The authorization code could be expired (after 60 seconds), already used or otherwise unknown. This error also occurs when the refresh token is expired or revoked.
  • invalid_client: This error occurs when the client cannot be authenticated. For example, if the client_id or client_secret are incorrect or invalid.
  • unauthorized_client: This error occurs when the client is not authorized to use the specified grant_type.
  • unsupported_grant_type: This error occurs when the authorization server does not support the grant_type specified. You should ensure that the grant_type in your request is authorization_code or refresh_token.

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"
}