Skip to main content
An app token identifies the merchant using your app and the tenant they belong to. It is a JWT issued by the platform identity provider, signed with RS256, and bound to your app. This means a token issued for another app cannot be used with yours. The way your app obtains the token depends on where it runs:

Before You Start

Token verification requires two values. The CLI’s npm run register command writes these values to your backend .env file.

Understand the Token Claims

Once decoded, an app token contains claims that identify the token, the merchant, and the tenant:
The app token carries no profile claims. To read a merchant’s name or email, call the identity provider’s userinfo endpoint.

What Verification Checks

Verification is three separate checks. A token is valid only when all three pass. The public keys are published at ${JTL_ISSUER}/oauth/v2/keys. The token header carries a kid, and the key with the matching kid is the one that verifies it.
Reject the request when any check fails. A token that carries a valid signature but a different audience was minted for another app, and treating it as valid would let that app act through yours.

Verify the Token

Your frontend sends the token as Authorization: Bearer <token>. Your backend verifies it before acting on the request.
The TypeScript path uses verifyAppToken, which performs all checks and returns a per-check breakdown in result.checks rather than throwing, so you can report which check failed. The C# and PHP paths implement the same three checks directly.
Cache the key set rather than fetching it on every request. Public keys change infrequently, and the samples above hold the set for the process lifetime.

Use the Token to Identify the Tenant

After verifying the token, use the urn:jtl:tenant_id claim to identify the tenant associated with the request.

Fullstack Apps (Embedded Views)

If your app has a backend, the flow is:
  1. Receive the app token from the frontend using the AppBridge.
  2. Verify the token.
  3. Read urn:jtl:tenant_id from the verified claims.
  4. Obtain a service account token.
  5. Call the JTL-Wawi via GraphQL using the service account token and the tenant ID.
The app token identifies the tenant. The service account token determines what the backend can do. The scopes declared in your manifest control the permissions available to the service account. See Service Account Authentication for obtaining and caching that token, and Scopes & Permissions for how the two tokens differ.

Standalone Frontend Apps: Browser Sign-in

If your app has no backend, it can call the API directly from the browser using the app token. In this case, the signed-in merchant’s own permissions apply. See Standalone Authentication for the implementation.

Tenant Mapping

Your backend should store a record that links the JTL tenant to your app’s internal state. Without this mapping, your backend cannot associate an incoming request with the merchant’s existing account or data. Use persistent storage rather than in-memory storage. In-memory data is lost when the server restarts and cannot be shared between multiple server instances.

What to Store

At minimum, persist the following the first time a merchant reaches your backend. If your app has its own user or account model, link the tenant ID to your internal record. A minimal PostgreSQL schema:

Writing the Record

Use an upsert rather than an insert. A merchant may connect your app more than once, and an existing tenant should update its record instead of causing a duplicate-key error.

Reading the Record

On each incoming request, verify the token, take the tenant ID from its claims, and look up your record:

What Not to Do

A few patterns cause most tenant-mapping bugs in production.

Token Lifetime

App tokens are valid for approximately one hour. Request a fresh token rather than holding one across a long-running session.
Calling getAppToken before each backend request keeps the token current without requiring your app to handle token expiry itself.

Common Errors

The failures you are most likely to hit, and what causes them.
The aud claim does not contain your app ID. JTL_APP_ID is the GUID shown in the Partner Portal under your app. Compare the value in your .env against the one in the portal.
The token was issued by a different environment than the one your backend is verifying against. Confirm JTL_ISSUER matches the environment your app is registered in.
The token has expired. App tokens are valid for approximately one hour, so a token captured during debugging stops verifying. Request a fresh one with getAppToken.
The token verified, so the merchant and tenant are known, but the call exceeded what the calling credential is permitted to do. A backend calling with a service account is bound by the scopes declared in your manifest. See Scopes & Permissions.

What’s Next?

Service Account Authentication

Obtain and cache the access token your backend uses to call the JTL Cloud API.

Using Platform APIs

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

Standalone Authentication

Sign merchants in from an app running on your own domain.

Scopes & Permissions

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