Authentication
The Scalar360 API uses OAuth 2.0. Your app gets a short-lived access token for each user, scoped to what it needs.
1. Register your client
Open Super Intelligence → Developer API → API Console in Scalar360 and choose Add client:
| Client type | Use it for | How users authorise |
|---|---|---|
| Server-based application | Web apps and services that other Scalar360 users connect to | A consent screen (authorization code flow) |
| Self client | Your own scripts, back-office jobs and integrations for your organisation | You generate a grant code in the API Console |
You receive a Client ID and a Client Secret. The secret is shown once — store it securely on your server and never in a browser or mobile app. You can regenerate it at any time.
Server-based applications also register one or more authorized redirect URIs (https://, or http://localhost for development).
2a. Server-based applications: get a grant code
Send the user to the authorization endpoint:
GET https://api.scalar360.com/oauth/v2/auth
?response_type=code
&client_id={client_id}
&scope=Scalar360.invoices.READ,Scalar360.customers.ALL
&redirect_uri={redirect_uri}
&access_type=offline
&state={random_value}| Parameter | Description |
|---|---|
response_type | Always code. |
client_id | Your Client ID. |
scope | Comma-separated scopes. |
redirect_uri | Must exactly match a registered redirect URI. |
access_type | offline to also receive a refresh token; online (default) for a one-hour access token only. |
state | Recommended. Returned unchanged so you can match the response to the request and prevent CSRF. |
The user signs in to Scalar360 (if needed), sees the app name, the organisation and the access requested, and chooses Accept or Deny. Scalar360 then redirects to your redirect URI:
{redirect_uri}?code={grant_code}&state={random_value}
{redirect_uri}?error=access_denied&error_description=The+user+declined+access&state={random_value}The grant code is valid for 10 minutes and can be used once.
2b. Self clients: generate a grant code
On your self client choose Generate code, select the scopes, a time duration (3, 5, 7 or 10 minutes) and an optional description. Copy the code and exchange it before it expires. Self-client codes always come with a refresh token.
3. Exchange the grant code for tokens
curl -X POST "https://api.scalar360.com/oauth/v2/token" \
-d "grant_type=authorization_code" \
-d "client_id={client_id}" \
-d "client_secret={client_secret}" \
-d "redirect_uri={redirect_uri}" \
-d "code={grant_code}"redirect_uri is required for server-based applications and omitted for self clients. Client credentials can also be sent with HTTP Basic authentication.
{
"access_token": "s360_api_at_…",
"refresh_token": "s360_api_rt_…",
"scope": "Scalar360.invoices.READ,Scalar360.customers.ALL",
"api_domain": "https://api.scalar360.com",
"token_type": "Bearer",
"expires_in": 3600
}refresh_token is only returned for access_type=offline and self clients.
4. Call the API
curl "https://api.scalar360.com/api/v1/customers" \
-H "Authorization: Scalar360-oauthtoken {access_token}"The Bearer scheme works too. Use the api_domain from the token response as the base URL.
5. Refresh the access token
Access tokens expire after one hour. Exchange the refresh token for a new access token whenever you get a 401 INVALID_OAUTHTOKEN or before the hour is up:
curl -X POST "https://api.scalar360.com/oauth/v2/token" \
-d "grant_type=refresh_token" \
-d "client_id={client_id}" \
-d "client_secret={client_secret}" \
-d "refresh_token={refresh_token}"{
"access_token": "s360_api_at_…",
"scope": "Scalar360.invoices.READ,Scalar360.customers.ALL",
"api_domain": "https://api.scalar360.com",
"token_type": "Bearer",
"expires_in": 3600
}6. Revoke a token
curl -X POST "https://api.scalar360.com/oauth/v2/token/revoke" -d "token={refresh_token}"Revoking a refresh (or access) token ends that whole authorisation immediately. Users can also revoke an app in Developer API → Authorized apps, and deleting a client revokes all of its tokens.
Organisation context
A token belongs to the user, and works in every organisation that user is an active member of. Choose the organisation for each call with the organization_id query parameter (or the X-Scalar360-Organization-Id header). Leave it out and the call runs in the organisation the token was authorised in, so existing integrations keep working.
curl "https://api.scalar360.com/api/v1/organizations" \
-H "Authorization: Scalar360-oauthtoken $ACCESS_TOKEN"curl "https://api.scalar360.com/api/v1/invoices?organization_id=$ORGANIZATION_ID&page=1&pageSize=20" \
-H "Authorization: Scalar360-oauthtoken $ACCESS_TOKEN"Every call runs with the user's role, module permissions and plan in that organisation — an admin in one organisation who has limited access in another gets the limited access there. An organisation the user isn't an active member of returns 403 ORGANIZATION_ACCESS_DENIED; a malformed id returns 400 INVALID_ORGANIZATION_ID.
Token endpoint errors
| HTTP | error | Meaning |
|---|---|---|
| 401 | invalid_client | Unknown Client ID or wrong Client Secret. |
| 400 | invalid_code | The grant code or refresh token is invalid, expired, already used or revoked. |
| 400 | invalid_redirect_uri | redirect_uri does not match the authorization request. |
| 400 | unsupported_grant_type | Use authorization_code or refresh_token. |
{ "error": "invalid_code", "error_description": "The grant code is invalid, expired or already used" }