> ## Documentation Index
> Fetch the complete documentation index at: https://developer.jtl-software.com/llms.txt
> Use this file to discover all available pages before exploring further.

# API Keys & Tokens

> Manage API keys, session tokens, and JWTs for the JTL Platform

The JTL platform uses different credentials and tokens depending on the integration type and API. This page explains what each one is, when to use it, and how to manage it.

To obtain these tokens, see the [OAuth 2.0 Flow](/guides/essentials/authentication/oauth2-flow) page.

## Credentials and Tokens at a Glance

| Credential / Token      | What it is                                                  | Where you get it                            | Lifetime                   |
| ----------------------- | ----------------------------------------------------------- | ------------------------------------------- | -------------------------- |
| **Client ID**           | Your app's public identifier                                | Partner Portal (after registration)         | Permanent (can regenerate) |
| **Client Secret**       | Your app's private key for authentication                   | Partner Portal (shown once at registration) | Permanent (can regenerate) |
| **Access Token (JWT)**  | Short-lived token for authenticating API requests           | Token endpoint (client credentials grant)   | \~24 hours                 |
| **Session Token**       | Short-lived token identifying the current merchant and user | App Shell (via AppBridge)                   | Short-lived                |
| **API Key (OnPremise)** | Permanent key for local ERP API access                      | JTL-Wawi desktop registration flow          | Permanent                  |
| **SCX Auth Token**      | Short-lived token for SCX Channel API requests              | SCX auth endpoint                           | \~1 hour                   |

## Client Credentials

Client credentials identify your app on the JTL platform. Every registered app receives a pair: a **Client ID** (public) and a **Client Secret** (private). Together, they're used to request access tokens from JTL's identity provider.

### Client ID

Your app's public identifier. It's safe to include in logs and non-sensitive contexts. The Partner Portal displays it on your app's detail page, and you can view it at any time.

### Client Secret

Your app's private key, used alongside the Client ID to generate access token for your app. The Partner Portal displays it **only once**, immediately after registration.

<Warning>
  **The Client Secret is shown only once.** Copy and store it securely at the
  moment of registration. If you lose it, you'll need to regenerate it.
</Warning>

### Regenerating a Lost Secret

The [Partner Portal](https://partner.jtl-software.com/) does not currently support in-place secret rotation. To regenerate, register a new app with the same manifest:

1. Log in to the Partner Portal
2. Click the **+ Create** button. You'll see a manifest editor with prefilled example
3. Replace the example manifest with the contents of your `manifest.json` file
4. Click the **Register App** and copy the new Client Secret
5. Update the secret in your app's environment variables and redeploy.

### Storage Best Practices

| Do                                                                               | Don't                                           |
| -------------------------------------------------------------------------------- | ----------------------------------------------- |
| Store in environment variables (`.env`, `.env.local`)                            | Hardcode in source code                         |
| Use a secrets manager in production (AWS Secrets Manager, HashiCorp Vault, etc.) | Commit `.env` files to version control          |
| Restrict access to credentials to team members who need them                     | Share credentials in chat, email, or tickets    |
| Rotate the secret if you suspect it's been compromised                           | Reuse the same credentials across multiple apps |

## Access Token (JWT)

The access token is what your backend uses to authenticate API requests to JTL. You obtain it by sending your client credentials to JTL's token endpoint via the [client credentials grant](/guides/essentials/authentication/oauth2-flow).

### Token Response

When you request an access token, JTL returns:

```json theme={null}
{
	"access_token": "eyJhbGciOiJSUzI1NiIsImt.......",
	"expires_in": 86399,
	"scope": "",
	"token_type": "bearer"
}
```

| Field          | Type   | Description                                                 |
| -------------- | ------ | ----------------------------------------------------------- |
| `access_token` | string | The JWT to include in API request headers                   |
| `expires_in`   | number | Seconds until the token expires (86399 ≈ 24 hours)          |
| `scope`        | string | The granted scopes                                          |
| `token_type`   | string | Always `bearer`: include as `Authorization: Bearer <token>` |

### Using the Access Token

Include it in the `Authorization` header of every API request:

```
Authorization: Bearer eyJhbGciOiJSUzI1NiIsImt.......
```

### Token Lifecycle

Access tokens expire in approximately **24 hours** (`expires_in: 86399`). Handle this as follows:

* **Cache the token**: Don't request a new one for every API call
* **Track expiry**: Store the `expires_in` value and request a new token before it expires (for example, when less than 5 minutes remain)
* **Handle 401 responses**: A `401 Unauthorized` response typically means the token has expired. Request a new one and retry the request once.

```mermaid theme={null}
flowchart TD
    A["Request access token (client credentials)"] --> B["Cache token + track expiry"]
    B --> C["Make API requests with Bearer token"]
    C --> D{Response?}
    D -->|"200 OK"| C
    D -->|"401 Unauthorized"| A
    B --> E{"Token near expiry? (< 5 min remaining)"}
    E -->|Yes| A
    E -->|No| C

    style A fill:#FFF2EB,stroke:#FB581F,stroke-width:2px,color:#0B1B45
    style B fill:#E8F4FF,stroke:#89D2FF,stroke-width:2px,color:#0B1B45
    style C fill:#E8F4FF,stroke:#89D2FF,stroke-width:2px,color:#0B1B45
    style D fill:#EEEEE7,stroke:#0B1B45,stroke-width:2px,color:#0B1B45
    style E fill:#EEEEE7,stroke:#0B1B45,stroke-width:2px,color:#0B1B45
```

## Session Token

Session tokens are issued by the **App Shell** and identify which merchant (tenant) and user is currently interacting with your app. They are only relevant for **Cloud Apps** that run inside the App Shell.

Unlike access tokens (requested by your app), session tokens come from the host environment. The App Shell passes them to your app through AppBridge, a lightweight SDK that handles session tokens, method calls, and events.

For implementation details on retrieving and verifying session tokens, see [Cloud Apps: Authentication & Login](/guides/cloud-apps/authentication-login).

### Decoded Structure

A session token is a JWT with three parts: header, payload, and signature.

```json theme={null}
{
	"header": {
		"alg": "EdDSA",
		"typ": "JWT"
	},
	"payload": {
		"exp": 1746616503,
		"userId": "<UUID>",
		"tenantId": "<UUID>",
		"tenantSlug": "<string>"
	},
	"signature": "fwjol6pXYkS7sXQ..."
}
```

### Header

| Field | Value   | Description                                                       |
| ----- | ------- | ----------------------------------------------------------------- |
| `alg` | `EdDSA` | Edwards-curve Digital Signature Algorithm (the signing algorithm) |
| `typ` | `JWT`   | Standard JWT type identifier                                      |

### Payload

| Field        | Type          | Description                                                                                         |
| ------------ | ------------- | --------------------------------------------------------------------------------------------------- |
| `exp`        | number        | Expiration timestamp (Unix time). The token is invalid after this time                              |
| `userId`     | string (UUID) | Unique identifier of the authenticated user                                                         |
| `tenantId`   | string (UUID) | Identifier of the merchant's tenant. Send this as the `X-Tenant-ID` header on every ERP API request |
| `tenantSlug` | string        | Human-readable tenant identifier                                                                    |

### Signature

The signature ensures the token has not been tampered with. Your backend verifies it using JTL's public keys, fetched from the JWKS endpoint.

### Verification

Session tokens must be verified **server-side** using JTL's public keys:

* Your backend requests an access token (client credentials grant)
* Using that access token, it fetches JTL's public keys from the JWKS endpoint (`https://api.jtl-cloud.com/account/.well-known/jwks.json`)
* It uses the public key to verify the session token's signature
* If the signature is valid, the payload (tenantId, userId, etc.) can be trusted

<Note>
  Never trust a session token without verifying it server-side. A token
  received from the client (frontend) could be tampered with. Always verify
  the signature against the JWKS public key before acting on the payload.
</Note>

## API Key (OnPremise)

API keys are permanent credentials used only in the OnPremise deployment model. They are generated through a two-step registration process in the JTL-Wawi desktop application.

### Key Characteristics

| Property          | Value                                                  |
| ----------------- | ------------------------------------------------------ |
| **Format**        | UUID (e.g. `00000000-0000-0000-0000-000000000000`)     |
| **Lifetime**      | Permanent (does not expire unless revoked)             |
| **Shown**         | Once, at the time of creation                          |
| **Header format** | `Authorization: Wawi <API-Key>`                        |
| **Scope**         | Local to the Wawi installation where it was registered |

<Warning>
  Like the Client Secret, the API key is displayed **only once** during
  registration. Store it securely immediately. If lost, you will need to go
  through the registration process again.
</Warning>

For the full OnPremise registration flow, see [OAuth 2.0 Flow (OnPremise
tab)](/guides/essentials/authentication/oauth2-flow).

## Token Comparison

|                         | Access Token                | Session Token                    | API Key              | SCX Auth Token              |
| ----------------------- | --------------------------- | -------------------------------- | -------------------- | --------------------------- |
| **Used by**             | Cloud apps (backend)        | Cloud apps (frontend to backend) | OnPremise apps       | Marketplace channels        |
| **Format**              | JWT (Bearer)                | JWT (EdDSA)                      | UUID                 | JWT (Bearer)                |
| **Lifetime**            | \~24 hours                  | Short-lived                      | Permanent            | \~1 hour                    |
| **How to get**          | Client credentials grant    | AppBridge (`getSessionToken`)    | Wawi registration    | SCX auth endpoint           |
| **Refresh mechanism**   | Re-request with credentials | Re-request via AppBridge         | N/A (permanent)      | Re-request with credentials |
| **Verify server-side?** | No (trusted issuer)         | Yes (JWKS)                       | N/A (you created it) | No (trusted issuer)         |

## Inspecting Tokens for Debugging

During development, you may need to read a token's contents to confirm what's inside. For the verification flow that backends should use in production, see [Cloud Apps: Authentication & Login](/guides/cloud-apps/authentication-login).

<Tip>
  For quick inspection during development, paste your token into
  [jwt.io](https://jwt.io/). It decodes the header and payload instantly. **Do
  not paste production tokens or any sensitive credentials into third-party
  tools.**
</Tip>

## Next Steps

<CardGroup cols={2}>
  <Card title="OAuth 2.0 Flow" icon="lock" href="/guides/essentials/authentication/oauth2-flow">
    How to obtain access tokens, API keys, and SCX auth tokens.
  </Card>

  <Card title="Cloud Apps: Authentication" icon="grid-2x2" href="/guides/cloud-apps/authentication-login">
    Implementation guide for session tokens, AppBridge, and the setup
    handshake in Cloud Apps.
  </Card>

  <Card title="Error Handling" icon="triangle-alert" href="/guides/essentials/common-patterns/error-handling">
    How to handle auth errors, expired tokens, and 401 responses.
  </Card>
</CardGroup>
