Authentication
Choose a service account for your own organization, or an OAuth app for access to individual entities or an entire organization.
Authenticate Digits Connect API calls with a bearer access token. Choose how to obtain that token based on whose data you need and how broadly your integration should connect.
Choose your authentication mode
| Mode | Access | Best for | Authorization |
|---|---|---|---|
| Service account | Your own firm or organization and its clients or entities | Internal tools and automation | Client ID + secret (client_credentials); no user authorization flow |
| App (entity) | An individual entity and its app connection | Integrations that connect one business at a time | User approval via OAuth (authorization_code) |
| App (organization) | An entire organization and its clients or entities | Practice management and firm-wide integrations | User approval via OAuth (authorization_code) |
Set up authentication
Select a mode for its setup steps and example requests. Replace the example credentials, tokens, and IDs with your own values.
API Access in 2 Steps
- Token Request via
https://connect.digits.com/v1/oauth/tokenusingclient_credentials- API calls with the service account's access token, selecting an entity when needed
Access to your own organization
Service accounts belong to your firm or organization. They need no redirect URI, installation, or user consent screen. Developer accounts and sandboxes cannot create service accounts.
1. Create your service account
In your firm's or organization's Developer area, choose Create → Service Account. Enter a name and description, select the permissions (scopes) you need, and save. Copy the client ID and client secret from Credentials.
You need permission to create organization connections. Service accounts have production credentials only. For the ledger example below, enable ledger:read.
2. Request an access token
Send your credentials from your server to the token endpoint:
curl --request POST 'https://connect.digits.com/v1/oauth/token' \
--header 'Content-Type: application/json' \
--data '{
"grant_type": "client_credentials",
"client_id": "your_client_id",
"client_secret": "your_client_secret"
}'Example response:
{
"access_token": "your_access_token",
"expires_in": 3599,
"token_type": "Bearer",
"scope": "ledger:read"
}The token uses the service account's configured scopes. There is no scope parameter in this request and no refresh token. Repeat this request when the access token expires.
3. Call the API
Use the token to read your organization:
curl 'https://connect.digits.com/v1/organization' \
--header 'Authorization: Bearer your_access_token'To read a particular entity's chart of accounts, supply its Digits entity ID:
curl --get 'https://connect.digits.com/v1/ledger/categories' \
--data-urlencode 'entity.id=your_digits_entity_id' \
--header 'Authorization: Bearer your_access_token'The entity must belong to, or be accessible by, your organization. See Selecting an entity below for more details.
API Access in 3 Steps
- User Authorization for an entity via
https://connect.digits.com/v1/oauth/authorize- Token Exchange via
https://connect.digits.com/v1/oauth/token- API calls with the app's access token for the authorized entity
One business at a time
A user authorizes your app for an individual entity. The resulting token identifies that entity and its app connection. Each additional entity needs its own authorization.
1. Configure your app and request authorization
Create an app with Installs Into → Legal Entity. Enable the scopes you need (ledger:read for this example), register your redirect URI, and copy your credentials from Keys. The install location is fixed when you create the app.
Redirect the user to this URL, using your app's client ID and registered redirect URI:
https://connect.digits.com/v1/oauth/authorize?response_type=code&client_id=your_client_id&redirect_uri=https%3A%2F%2Fyourapp.com%2Foauth%2Fcallback&scope=ledger%3Aread&state=your_random_stateGenerate an unpredictable, single-use state for each attempt. To request multiple configured scopes, separate them with spaces and URL-encode the value.
After approval, Digits redirects to your callback:
https://yourapp.com/oauth/callback?code=your_authorization_code&state=your_random_stateVerify that state matches the value stored for the user's session before handling the callback. If the user declines, handle error=access_denied instead of exchanging a code.
2. Exchange the authorization code
On your server, exchange the code once, promptly. Use the same client ID and exact redirect URI as in the authorization request.
curl --request POST 'https://connect.digits.com/v1/oauth/token' \
--header 'Content-Type: application/json' \
--data '{
"grant_type": "authorization_code",
"client_id": "your_client_id",
"client_secret": "your_client_secret",
"code": "your_authorization_code",
"redirect_uri": "https://yourapp.com/oauth/callback"
}'Example response:
{
"access_token": "your_access_token",
"refresh_token": "your_refresh_token",
"expires_in": 3599,
"token_type": "Bearer",
"scope": "ledger:read"
}Store both tokens securely. Use the refresh token to renew access without another user authorization.
3. Call the API for the authorized entity
The token already identifies the entity, so no entity selector is needed:
curl 'https://connect.digits.com/v1/ledger/categories' \
--header 'Authorization: Bearer your_access_token'This token cannot switch to another entity or call organization-level endpoints.
API Access in 3 Steps
- User Authorization for an organization via
https://connect.digits.com/v1/oauth/authorize- Token Exchange via
https://connect.digits.com/v1/oauth/token- API calls with the app's organization access token, selecting an entity when needed
One authorization for the organization
A user with permission to install organization connections approves access for their firm or organization. Your app can then work across its clients or entities, within the granted scopes.
1. Configure your app and request authorization
Create an app with Installs Into → Organization. Enable the scopes you need (ledger:read for these examples), register your redirect URI, and copy your credentials from Keys. The install location is fixed when you create the app.
Redirect the user to this URL, using your app's client ID and registered redirect URI:
https://connect.digits.com/v1/oauth/authorize?response_type=code&client_id=your_client_id&redirect_uri=https%3A%2F%2Fyourapp.com%2Foauth%2Fcallback&scope=ledger%3Aread&state=your_random_stateGenerate an unpredictable, single-use state for each attempt. The app's Installs Into setting determines that the user selects an organization during authorization.
After approval, Digits redirects to your callback:
https://yourapp.com/oauth/callback?code=your_authorization_code&state=your_random_stateVerify that state matches the value stored for the user's session before handling the callback. If the user declines, handle error=access_denied instead of exchanging a code.
2. Exchange the authorization code
On your server, exchange the code once, promptly. Use the same client ID and exact redirect URI as in the authorization request.
curl --request POST 'https://connect.digits.com/v1/oauth/token' \
--header 'Content-Type: application/json' \
--data '{
"grant_type": "authorization_code",
"client_id": "your_client_id",
"client_secret": "your_client_secret",
"code": "your_authorization_code",
"redirect_uri": "https://yourapp.com/oauth/callback"
}'Example response:
{
"access_token": "your_access_token",
"refresh_token": "your_refresh_token",
"expires_in": 3599,
"token_type": "Bearer",
"scope": "ledger:read"
}The token represents the organization that approved the installation. Store both tokens securely; use the refresh token to renew access. Organization apps require OAuth approval and cannot use client_credentials.
3. Call the API for the authorized organization
Organization endpoints use the organization identified by the token:
curl 'https://connect.digits.com/v1/organization' \
--header 'Authorization: Bearer your_access_token'For a particular entity's ledger, include its Digits entity ID:
curl --get 'https://connect.digits.com/v1/ledger/categories' \
--data-urlencode 'entity.id=your_digits_entity_id' \
--header 'Authorization: Bearer your_access_token'The entity must belong to, or be accessible by, the authorized organization. See Selecting an entity below for more details.
Working with tokens
Access tokens last up to 1 hour. Use the response's expires_in value to schedule renewal. Every API call sends the token in the Authorization: Bearer <access_token> header.
Keep credentials on your server. Store client secrets and tokens securely, keep them out of browser code and logs, and use HTTPS for API calls and production callbacks.
Renewing access tokens
Service accounts: request another token with grant_type=client_credentials. No refresh token is issued.
Both app types: send the refresh token to the same token endpoint:
curl --request POST 'https://connect.digits.com/v1/oauth/token' \
--header 'Content-Type: application/json' \
--data '{
"grant_type": "refresh_token",
"client_id": "your_client_id",
"client_secret": "your_client_secret",
"refresh_token": "your_refresh_token"
}'The response includes a new access token and the same refresh token. Refresh tokens do not expire automatically, but disconnecting or uninstalling the app revokes them. If the refresh token is no longer valid, start a new authorization flow.
Selecting an entity
Service accounts and organization apps receive organization-level tokens. For endpoints that act on an entity or connection, use the endpoint's entity selector to identify the business you want to work with.
- GET requests: pass
entity.idas a query parameter, as shown in the tabs above. - JSON requests: use
"entity": {"id": "your_digits_entity_id"}when the endpoint defines anentityfield.
Find entity IDs through List Entities for an organization's own entities, or List Clients, then Get Client for a firm's clients. Get Client returns an entities array; use each entity's id. These discovery requests require an organization-level token with no additional scope.
Digits checks the selected entity against the organization represented by your token. An entity app's token is already bound to its authorized entity; adding a selector cannot expand its access.
Choosing scopes
Scopes control which operations a token can perform. The authentication mode controls whose data it can access. Configure only the scopes you need and check each endpoint's requirements.
| Scope | Allows |
|---|---|
ledger:read | Read ledger data |
source:sync | Sync source data into Digits |
documents:write | Upload and manage documents |
Service account tokens use the account's configured scopes. Apps request configured scopes in the authorization URL and receive the scopes granted for that installation. Check the token response's scope value.
Troubleshooting authentication
| Issue | What to check |
|---|---|
| Invalid client credentials | Use the client ID and secret from the same account or app environment. |
| Redirect URI mismatch | Use the exact registered URI in both authorization and token exchange. |
| Invalid authorization code | Codes are single-use and short-lived. Start authorization again if a code expired or was already exchanged. |
| Wrong grant type | Service accounts use client_credentials; both app types use authorization_code, then refresh_token. |
| Expired access token | Renew the token using the method for your authentication mode. |
| Missing scope or wrong access level | Check the endpoint's scopes and whether it requires organization, entity, or connection access. |
| Missing or inaccessible entity | With an organization-level token, supply an entity selector for entity-specific requests and verify that the organization can access it. |
Next steps
Follow Working across an organization to discover entities, read two businesses with one token, and initialize source writes for each. Vault document uploads require an entity-app token; that upload endpoint does not support organization-token entity selectors.
Updated 15 days ago