Skip to main content
The JTL platform uses different authentication mechanisms across its APIs. This page explains how each one works, when to use it, and how tokens are managed.

Authentication at a Glance

APIAuth mechanismToken typeLifetime
JTL Cloud APIOAuth 2.0 Client CredentialsShort-lived JWT~1 hour (refresh before expiry)
OnPremise ERP APIProprietary registration flowStatic API keyPermanent
SCX Channel APIRefresh token exchangeShort-lived access token~1 hour (refresh before expiry)

Two Types of Tokens (Cloud)

If you’re building on the Cloud platform, you’ll use two tokens:
TokenHow you get itWhat it’s forWho creates it
Access token (JWT)Client credentials grant > JTL Identity ProviderAuthenticating your backend’s API calls to JTLYour app’s backend
Session tokenappBridge.method.call('getSessionToken')Identifying which merchant (tenant) and user is interacting with your appJTL App Shell (via AppBridge)
Access token: identifies your app (machine-to-machine). Session token: identifies the tenant and user (from the App Shell). In a typical Cloud App, both tokens are in play: the session token tells you who, and the access token lets you act on their behalf.
Check out the Cloud Apps: Authentication & Login guide to learn more about Cloud Apps authentication (JWKS, AppBridge integration).

Cloud Authentication (OAuth 2.0)

The Cloud API uses the OAuth 2.0 Client Credentials Flow. Your app’s backend authenticates with a client ID and secret, receives a short-lived JWT, and uses that JWT as a Bearer token for all API requests.

Prerequisites

Your app must be registered in the Partner Portal. Registration creates an OAuth client with a Client ID and Client Secret.
Your Client Secret is displayed only once immediately after registration. Store it securely.

How the Flow Works

  1. Your backend sends its client credentials to the JTL Identity Provider
  2. The Identity Provider returns a short-lived JWT access token
  3. Your backend includes that token (along with the tenant ID) in every API request

Token Endpoint

POST https://auth.jtl-cloud.com/oauth2/token
Request:
ComponentValue
MethodPOST
Content-Typeapplication/x-www-form-urlencoded
AuthorizationBasic <Base64(clientId:clientSecret)>
Bodygrant_type=client_credentials
Response:
{
  "access_token": "eyJhbGciOiJSUzI1NiIs...",
  "token_type": "Bearer",
  "expires_in": 3600
}
FieldDescription
access_tokenThe JWT to use in API requests
token_typeAlways Bearer
expires_inToken lifetime in seconds. Request a new token before this expires.

Reference Examples

async function getAccessToken(): Promise<string> {
  const clientId = process.env.CLIENT_ID;
  const clientSecret = process.env.CLIENT_SECRET;
 
  if (!clientId || !clientSecret) {
    throw new Error('CLIENT_ID and CLIENT_SECRET must be defined');
  }
 
  const authString = Buffer.from(`${clientId}:${clientSecret}`).toString('base64');
 
  const response = await fetch('https://auth.jtl-cloud.com/oauth2/token', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/x-www-form-urlencoded',
      Authorization: `Basic ${authString}`,
    },
    body: new URLSearchParams({
      grant_type: 'client_credentials',
    }),
  });
 
  const data = await response.json();
 
  if (response.ok) {
    return data.access_token;
  } else {
    throw new Error(`Failed to fetch token (${response.status}): ${data.error}`);
  }
}

Making Authenticated API Requests

Once you have an access token, include it in every API request along with the tenant ID.Base URL:
https://api.jtl-cloud.com/erp/v2/
Required headers:
HeaderValueDescription
AuthorizationBearer <JWT>The access token from the client credentials flow
X-Tenant-ID<tenantId>The JTL Cloud tenant ID (identifies which merchant’s data you’re accessing)
Example:
const accessToken = await getAccessToken();
 
const response = await fetch('https://api.jtl-cloud.com/erp/v2/info', {
  headers: {
    'Authorization': `Bearer ${accessToken}`,
    'X-Tenant-ID': tenantId,
  },
});
Check out the Cloud Apps: Authentication & Login guide to see how to get the tenant ID.

Token Lifecycle

Access tokens are short-lived and expire after a set period (typically 1 hour). Your app needs to handle this:
  • Cache the token and reuse it until it’s close to expiry
  • Refresh proactively (e.g. when < 5 minutes remain)
  • Handle 401 Unauthorized by refreshing once and retrying

Security Schemes

Cloud endpoints are secured using one of two schemes:
SchemeWhen it applies
Wawi AND oauth2-cloud-id-accessCodeUser-bound access: the request is tied to a specific user’s session
Wawi AND oauth2-applicationMachine-to-machine access: your backend calls the API directly using client credentials

JTL Cloud vs. OnPremise Comparison

FeatureCloudOnPremise
Auth mechanismOAuth 2.0 (Client Credentials)Proprietary API key flow
Token typeShort-lived JWT Bearer tokenPermanent static API key
RegistrationPartner PortalJTL-Wawi desktop Admin >> App Registration
CredentialsClientId + ClientSecretRegistration ID >> API key
Tenant identificationX-Tenant-ID header requiredNot required (local connection)
Base URLhttps://api.jtl-cloud.com/erp/v2/http://127.0.0.1:<port>/api/eazybusiness/
Authorization formatBearer <JWT>Wawi <PERMANENT_API_KEY>
Token refreshRe-request with client credentials before expiryNot needed (key is permanent)

SCX Channel API Authentication

The SCX Channel API uses the same client credentials mechanism as the Cloud-ERP API. You request an access token, use it for API calls, and request a new one before it expires. This section applies only to Marketplace Channel integrations. Base URL:
https://scx.api.jtl-software.com/v1/

How the Flow Works

Requesting an Access Token

const formData = new FormData();
formData.append('refreshToken', 'YOUR-REFRESH-TOKEN');

const response = await fetch('https://scx.api.jtl-software.com/v1/auth', {
	method: 'POST',
	body: formData,
});
Response:
{
  "scope": "CHANNEL",
  "authToken": "eyJ0eXAi....",
  "tokenExpireAt": "2024-05-14T10:07:42+02:00",
  "expiresIn": 3600
}
FieldDescription
scopeThe scope of the token (e.g. CHANNEL)
authTokenThe access token for authenticating requests
tokenExpireAtExpiration timestamp in ISO 8601 format
expiresInTime in seconds until the token expires

Using the Access Token

Include the authToken as a Bearer token in all subsequent requests:
const response = await fetch("https://scx.api.jtl-software.com/v1/seller/channel/MYCHANNEL", {
  method: "POST",
  headers: {
    "Authorization": "Bearer eyJ0eXAi....",
  },
});

Token Lifecycle

SCX access tokens have a TTL (Time To Live) of 1 hour. Your app should:
  • Cache the token and reuse it until it’s close to expiry
  • Monitor the expiresIn value and refresh proactively (e.g. when less than 5 minutes)
  • Refresh tokens before they expire to avoid failures during requests.

Best Practices

These practices apply regardless of which auth mechanism you’re using: Credential storage
  • Never hardcode credentials in source code. Use environment variables or a secrets manager.
  • Never commit .env files to version control.
  • Rotate credentials if you suspect they’ve been compromised.
Token management
  • Cache tokens and reuse them. Don’t request a new token for every API call.
  • Refresh proactively before expiry, not after receiving a 401 error.
  • On a 401 response, refresh the token and retry the request once.
Security
  • Always use HTTPS for Cloud and SCX API calls.
  • Validate session tokens server-side. Never trust tokens received from the client without verification.
  • Request the minimum required scopes. Don’t request broader access than your app needs.

Next Steps

API Keys & Tokens

Deeper dive into JWT structure, session tokens, and token management patterns.

Scopes & Permissions

Understand which scopes are available and how to request the right level of access.

Cloud Apps: Authentication

Implementation guide for authentication in Cloud Apps: AppBridge, session tokens, and the setup handshake.

Error Handling

How to handle auth errors, expired tokens, and common failure modes.