Before You Start
Token verification requires two values. The CLI’snpm 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.
Verify the Token
Your frontend sends the token asAuthorization: Bearer <token>. Your backend verifies it before acting on the request.
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.
Use the Token to Identify the Tenant
After verifying the token, use theurn: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:- Receive the app token from the frontend using the AppBridge.
- Verify the token.
- Read
urn:jtl:tenant_idfrom the verified claims. - Obtain a service account token.
- Call the JTL-Wawi via GraphQL using the service account token and the tenant ID.
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.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.Verification fails on the audience check
Verification fails on the audience check
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.Verification fails on signature or issuer
Verification fails on signature or issuer
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.Verification fails on a token that worked earlier
Verification fails on a token that worked earlier
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.API calls return 403 after successful verification
API calls return 403 after successful verification
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.