Skip to content

Authentication

All Ditio API endpoints require authentication. The API uses OAuth 2.0 client credentials: you exchange a client_id and client_secret for a short-lived bearer token, and send that token with every request.

Your integration client id + secret Identity server issues bearer token Ditio API core / reporting 1. request token 2. access token 3. call with Authorization: Bearer reuse until it expires, then repeat

Ditio has two environments. Credentials are per-environment — a client created in production does not work in test, and vice versa.

Test Build and verify here first Core API core-api.ditio.dev/core Reporting core-api.ditio.dev/reporting Identity identity.ditio.dev Production Live customer data Core API core-api.ditio.app/core Reporting core-api.ditio.app/reporting Identity identity.ditio.app

All code samples in this documentation use environment variables so you can switch environments in one place — and they default to test:

Terminal window
# Test (default for all examples)
export DITIO_API_BASE="https://core-api.ditio.dev/core"
export DITIO_REPORTING_BASE="https://core-api.ditio.dev/reporting"
export DITIO_IDENTITY_BASE="https://identity.ditio.dev"
# Production — only after verifying in test
# export DITIO_API_BASE="https://core-api.ditio.app/core"
# export DITIO_REPORTING_BASE="https://core-api.ditio.app/reporting"
# export DITIO_IDENTITY_BASE="https://identity.ditio.app"

Any Ditio administrator can create API credentials self-service:

  1. Log in to Ditio Web.

  2. Go to Company Setup (Oppsett) → Integration.

  3. Click Create new API client and give it a descriptive name (e.g. “ERP Integration”, “Power BI”).

  4. The system generates a client_id and client_secret.

Create separate clients for separate use cases so they can be disabled or deleted independently. Disabling a client immediately revokes its access.

Exchange your credentials for a bearer token at the identity server:

Terminal window
curl -X POST "$DITIO_IDENTITY_BASE/connect/token" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=client_credentials" \
-d "client_id=YOUR_CLIENT_ID" \
-d "client_secret=YOUR_CLIENT_SECRET" \
-d "scope=ditioapiv3"

Response:

{
"access_token": "eyJhbGciOiJSUzI1NiIs...",
"expires_in": 1800,
"token_type": "Bearer",
"scope": "ditioapiv3"
}

The scope you request decides which API the token works against, so it has to match the endpoint you are calling — see Which scope for which host below. ditioapiv3 covers the Core and Integration API; the Data Extraction API needs reportingapiv1 instead.

ScopeGrants access to
ditioapiv3Core + Integration API — use this for most integrations
masstransportapiv1Mass Transport API (machine integration)
reportingapiv1Reporting / Data Extraction API

If your integration calls more than one of these APIs, you can request several scopes in one token by separating them with a space:

Terminal window
-d "scope=ditioapiv3 reportingapiv1"

Each base URL requires its own scope. A token is accepted only if it carries the scope for the API you are calling — a token requested with ditioapiv3 alone is not accepted by $DITIO_REPORTING_BASE, even though both live on the same hostname. A token requested with several scopes carries all of them and is accepted by every host whose scope it holds.

Base URLScope requiredDocumented in
$DITIO_API_BASEcore-api.ditio.app/coreditioapiv3Integration API
$DITIO_REPORTING_BASEcore-api.ditio.app/reportingreportingapiv1Data Extraction API
$DITIO_MASSTRANSPORT_BASE — provided by your Ditio contactmasstransportapiv1Machine Integration

Include the access token in the Authorization header of every API request:

Terminal window
curl -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
"$DITIO_API_BASE/api/v4/integration/projects"

Scope is never read from a request header — it comes from the token.

Tokens are short-lived — about 30 minutes by default (configurable per client, up to 24 hours). Read expires_in from the token response, cache the token, and refresh it shortly before it lapses. Your integration should also handle 401 Unauthorized responses by requesting a new token — but only retry once. If the retry also returns 401, the token is being rejected for something other than expiry and retrying will loop forever; read the WWW-Authenticate header to see which cause it is, per Error Handling. Never request a fresh token per API call.

// Cache the token; only fetch a new one shortly before it expires.
private static string _token;
private static DateTime _tokenExpiresUtc = DateTime.MinValue;
async Task<string> GetTokenAsync(HttpClient http)
{
// Refresh ~1 minute early so a token never expires mid-request.
if (_token is not null && DateTime.UtcNow < _tokenExpiresUtc.AddMinutes(-1))
return _token;
var identityBase = Environment.GetEnvironmentVariable("DITIO_IDENTITY_BASE")
?? "https://identity.ditio.dev";
var response = await http.PostAsync(
$"{identityBase}/connect/token",
new FormUrlEncodedContent(new Dictionary<string, string>
{
["grant_type"] = "client_credentials",
["client_id"] = clientId,
["client_secret"] = clientSecret,
// Must match the API being called — see "Which scope for which host".
["scope"] = "ditioapiv3",
}));
response.EnsureSuccessStatusCode();
var token = await response.Content.ReadFromJsonAsync<TokenResponse>();
_token = token!.access_token;
_tokenExpiresUtc = DateTime.UtcNow.AddSeconds(token.expires_in);
return _token;
}
private record TokenResponse(string access_token, int expires_in, string token_type);

If you can’t run the token flow (e.g. a BI tool that only accepts a static token), an administrator can generate a longer-lived token directly in the Ditio Web UI on the integration page (these are audited). Each API client can be disabled or deleted at any time from the same page, immediately revoking access.

  • Never expose your Client Secret in client-side code or version control
  • Store credentials in environment variables or a secrets manager
  • Rotate your Client Secret periodically
  • Use the minimum required scopes for your integration
  • Implement token caching to avoid unnecessary token requests