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

# Authentication

> JWT Bearer tokens via user credentials or API key — form-encoded token endpoints and claims.

The External API uses **JWT Bearer tokens** (HS256 symmetric signing). All business routes expect:

```http theme={null}
Authorization: Bearer <access_token>
```

There is **no** API-key-in-header pattern for data routes: you exchange the API key or password for a JWT using the token endpoints, then call `/api/*` with the Bearer token.

## How to obtain credentials

| Method             | When to use                                                                                                                         |
| ------------------ | ----------------------------------------------------------------------------------------------------------------------------------- |
| **Dashboard user** | You have a Telemax username and password (same identity as the web app).                                                            |
| **API key**        | Your company issued a GUID API key in the Telemax dashboard, or a **legacy** non-GUID key resolved via the legacy tracking service. |

Credentials are **not** passed on every data request—only the JWT.

## Token endpoints

### User login

[`POST /api/Authentication/token/user`](/v1/api-reference/post-authentication-token-user)

* **Content-Type**: `application/x-www-form-urlencoded`
* **Body fields**: `UserName`, `Password` (see `LoginRequest` in code)

### API key login

[`POST /api/Authentication/token/api-key`](/v1/api-reference/post-authentication-token-api-key)

* **Content-Type**: `application/x-www-form-urlencoded`
* **Body field**: `apiKey` (string)

## Successful token response

JSON fields (see `JwtTokenResponse`):

| Field          | Type   | Description                     |
| -------------- | ------ | ------------------------------- |
| `access_token` | string | JWT for `Authorization: Bearer` |
| `token_type`   | string | Always `bearer`                 |
| `expires_in`   | number | Lifetime in **seconds**         |

## JWT claims

Claim types are the string names of `JwtClaims` (e.g. `UserId`, `CompanyId`, `TimeZone`). API key logins may also include `Vehicles`, `Actions`, and `DateFormat` for scoped keys.

## Token expiry and refresh

* Default token lifetime: **\~24 hours** (`expires_in: 86399.0` seconds).
* The actual lifetime may vary if the server's `JWT:TokenExpiryTime` setting is configured (minimum 5 minutes).
* There is **no refresh endpoint** — when a token expires, re-authenticate using [`POST /api/Authentication/token/user`](/v1/api-reference/post-authentication-token-user) or [`POST /api/Authentication/token/api-key`](/v1/api-reference/post-authentication-token-api-key).
* **Recommendation:** Re-authenticate **5 minutes before** the token expires rather than waiting for a 401 response. See [Token caching strategy](#token-caching-strategy) below.

## Token caching strategy

<Warning>
  Do not request a new token on every API call. Each token is valid for \~24 hours. Requesting tokens unnecessarily adds latency and risks hitting future rate limits.
</Warning>

Recommended approach:

1. On startup, request a token and store it in memory with its expiry time (`Date.now() + expires_in * 1000`).
2. Before each API call, check if the token expires within the next **5 minutes**.
3. If yes, re-authenticate and replace the cached token.
4. If no, use the cached token.

**Example (JavaScript):**

```javascript theme={null}
let cachedToken = null;
let tokenExpiresAt = 0;

async function getToken() {
  if (cachedToken && Date.now() < tokenExpiresAt - 5 * 60 * 1000) {
    return cachedToken;
  }
  const res = await fetch('https://api.telemax.com.au/api/Authentication/token/api-key', {
    method: 'POST',
    headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
    body: new URLSearchParams({ apiKey: process.env.TELEMAX_API_KEY }),
  });
  const { access_token, expires_in } = await res.json();
  cachedToken = access_token;
  tokenExpiresAt = Date.now() + expires_in * 1000;
  return cachedToken;
}
```

**Example (Python):**

```python theme={null}
import time
import requests

_token = None
_token_expires_at = 0

def get_token():
    global _token, _token_expires_at
    if _token and time.time() < _token_expires_at - 300:
        return _token
    r = requests.post(
        'https://api.telemax.com.au/api/Authentication/token/api-key',
        data={'apiKey': os.environ['TELEMAX_API_KEY']},
    )
    r.raise_for_status()
    data = r.json()
    _token = data['access_token']
    _token_expires_at = time.time() + data['expires_in']
    return _token
```

***

## Token scoping

Every token is bound to a **single company**. The `CompanyId` claim in the JWT determines which resources the token can access.

### User tokens

Scoped to the primary company associated with the Telemax user account. Multi-company users are pinned to their first company — if you need access to a different company, use an API key issued for that company instead.

### API key tokens

API key tokens carry additional optional claims:

| Claim        | Empty value means           | Non-empty value means                          |
| ------------ | --------------------------- | ---------------------------------------------- |
| `Vehicles`   | All vehicles in the company | Comma-separated list of allowed vehicle IDs    |
| `Actions`    | All actions                 | Comma-separated list of allowed action strings |
| `DateFormat` | —                           | User's configured date format                  |

To check which companies and vehicles your token can access, call [`POST /api/GetCompanies`](/v1/api-reference/post-get-companies) and [`POST /api/Devices`](/v1/api-reference/post-devices) after authenticating.

***

## API key rotation

To rotate an API key without downtime:

1. **Create the new key** in the Telemax dashboard (Webhooks and API Key section). Both keys are active simultaneously.
2. **Update your integration** to use the new key and confirm tokens are being obtained successfully.
3. **Delete the old key** in the dashboard. Existing tokens issued with the old key remain valid until they expire (up to 24 hours).

<Warning>
  Deleting a key immediately invalidates future token requests with that key. Tokens already issued remain valid until their `expires_in` elapses.
</Warning>

***

## Code examples

<CodeGroup>
  ```bash cURL (user) theme={null}
  curl -s -X POST "https://api.telemax.com.au/api/Authentication/token/user" \
    -H "Content-Type: application/x-www-form-urlencoded" \
    --data-urlencode "UserName=integration.reader@acmefleet.com.au" \
    --data-urlencode "Password=K7mP2vL9qW4nR8xT"
  ```

  ```bash cURL (API key) theme={null}
  curl -s -X POST "https://api.telemax.com.au/api/Authentication/token/api-key" \
    -H "Content-Type: application/x-www-form-urlencoded" \
    --data-urlencode "apiKey=a3f1c2b4-5d6e-7890-abcd-ef1234567890"
  ```

  ```javascript JavaScript theme={null}
  const tokenRes = await fetch('https://api.telemax.com.au/api/Authentication/token/api-key', {
    method: 'POST',
    headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
    body: new URLSearchParams({ apiKey: 'a3f1c2b4-5d6e-7890-abcd-ef1234567890' }),
  });
  const { access_token, expires_in } = await tokenRes.json();
  ```

  ```python Python theme={null}
  import requests
  r = requests.post(
      'https://api.telemax.com.au/api/Authentication/token/user',
      data={'UserName': 'integration.reader@acmefleet.com.au', 'Password': 'K7mP2vL9qW4nR8xT'},
  )
  r.raise_for_status()
  token = r.json()['access_token']
  ```
</CodeGroup>
