Skip to main content
A service account allows your backend to authenticate as your app without a merchant being signed in. It uses the OAuth 2.0 client credentials grant to obtain a access token. Use a service account when your backend needs to make API calls on its own, such as:
  • Processing webhooks
  • Running scheduled jobs
  • Calling the API on behalf of a merchant after verifying their app token
The permissions available to the service account are controlled by the scopes declared in your app manifest.

Before You Start

Your backend needs the credentials issued when you registered your app. The CLI’s npm run register writes both to your backend .env. Your app manifest declares the service account under authentication.serviceAccount. See App Manifest: Authentication.
The client secret is shown once and cannot be retrieved afterwards. Keep it on your server, never in frontend code or a public repository.

Getting a Access Token

Your backend sends its credentials to the token endpoint using HTTP Basic authentication.
The token endpoint requires HTTP Basic authentication. Sending the credentials in the request body instead of the Authorization header is rejected.

Token Response

A successful request returns:

Caching the Token

Access tokens are valid for approximately 24 hours. Requesting a new one on every API call adds latency and unnecessary load on the token endpoint. Hold the token in memory and request a new one shortly before it expires.
The 60-second buffer prevents a token expiring between the cache check and the API call.
This in-memory cache works for a single-instance server. If you run multiple instances behind a load balancer, use a shared cache such as Redis so they do not each hold their own token.

Calling the API

Access tokens are not tied to a merchant, so every call names the tenant it applies to in the X-Tenant-ID header. Take that value from a verified app token rather than from the incoming request. See App Token Authentication. If a call returns 401 Unauthorized, clear the cached token and retry once with a fresh one.

Scope Enforcement

Calls made with a access token are bound by the scopes declared in capabilities.erp.api.scopes in your manifest. A call that exceeds them returns 403 Forbidden. This holds even when a merchant is signed in. A backend that verifies an app token and then calls the API with its service account is making a machine call, so the manifest’s scopes apply rather than the merchant’s own permissions. See Scopes & Permissions.

Common Errors

The failures you are most likely to hit, and what causes them.
The CLIENT_ID or CLIENT_SECRET is incorrect. Check both values in your .env for extra whitespace, missing characters, or swapped values. If the secret has been lost, register a new app version in the Partner Portal to receive fresh credentials.
The cached token has expired. Clear the cache and request a new one, as the retry example above does. If this happens often, confirm the cache is reading exp from the token rather than assuming a fixed lifetime.
The credentials were sent in the request body. The token endpoint requires HTTP Basic authentication, so they belong in the Authorization header.
The token is valid, but the call exceeds the scopes declared in your manifest. Add the scope your app needs and submit an updated manifest through the Partner Portal.
The X-Tenant-ID header is missing or names a tenant your app is not installed on. Take the value from a verified app token’s urn:jtl:tenant_id claim.

What’s Next?

App Token Authentication

Verify the app token that tells your backend which merchant a request belongs to.

Using Platform APIs

Call the JTL Cloud and JTL-Wawi APIs with the right headers and scoping.

Scopes & Permissions

Declare what your app can access, and understand how enforcement differs by token.

Best Practices

Production patterns for token caching, error handling, and security.