> For the complete documentation index, see [llms.txt](https://docs.docs-dispatcher.io/docs-dispatcher/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.docs-dispatcher.io/docs-dispatcher/authentication/jwt-bearer.md).

# JWT Bearer authentication

Docs-Dispatcher accepts **Bearer JWTs** in addition to [Basic Auth](/docs-dispatcher/authentication/basic-auth.md). Pick whichever fits your integration best — both reach the same endpoints with the same permissions.

| Method         | Best for                                                                                                                |
| -------------- | ----------------------------------------------------------------------------------------------------------------------- |
| **Basic Auth** | Server-to-server integrations where you store the account credentials securely once and forget about them               |
| **JWT Bearer** | Multi-step flows where you want a short-lived token, browser-mediated logins (proxied), or session-scoped impersonation |

## Two hosts at play

The JWT flow involves **two different hosts** — make sure you point each request at the right one:

| Operation                                                 | Host                                       |
| --------------------------------------------------------- | ------------------------------------------ |
| Issue / refresh a JWT (`POST /auth`, `GET /auth/refresh`) | `https://api.templater.docs-dispatcher.io` |
| Use the JWT to call the API (`POST /api/{service}` …)     | `https://api.docs-dispatcher.io`           |

Once you hold a JWT, it is accepted transparently by the Dispatcher API — same permissions as the credentials that produced it.

## Get a JWT

JWTs are issued by the Auth API:

```bash
curl -X POST https://api.templater.docs-dispatcher.io/auth \
  -H "Content-Type: application/json" \
  -d '{
    "username": "your-email@company.com",
    "password": "your-password"
  }'
```

**Successful response (`200 OK`):**

```json
{
  "data": {
    "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
    "userId": 42,
    "companyId": 7
  }
}
```

**Authentication failure (`403 Forbidden`):**

```json
{
  "error": "forbidden",
  "message": "Invalid credentials"
}
```

Capture the `token` value — that's what you'll send on every subsequent request.

```bash
export JWT="$(curl -sX POST https://api.templater.docs-dispatcher.io/auth \
  -H 'Content-Type: application/json' \
  -d '{"username":"…","password":"…"}' | jq -r .data.token)"
```

## Use the JWT

Send the token as a `Bearer` value in the `Authorization` header on every API call:

```http
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
```

```bash
curl -X POST https://api.docs-dispatcher.io/api/invoicing/validate \
  -H "Authorization: Bearer $JWT" \
  -H "Content-Type: application/json" \
  --data @invoice.json
```

## Refresh a JWT

Tokens expire (default: 120 minutes). Get a fresh one with the refresh endpoint — it accepts the current token and returns a new one with the same user/company context:

```bash
curl -X GET https://api.templater.docs-dispatcher.io/auth/refresh \
  -H "Authorization: Bearer $JWT"
```

Response shape is identical to `POST /auth`. Implement this in your client to avoid the user re-entering credentials.

{% hint style="info" %}
A simple rule of thumb: refresh whenever a response returns `401 Unauthorized`. If the refresh also fails, prompt the user to log in again.
{% endhint %}

## Code patterns

{% tabs %}
{% tab title="curl" %}

```bash
# 1. Login
JWT=$(curl -sX POST https://api.templater.docs-dispatcher.io/auth \
  -H 'Content-Type: application/json' \
  -d '{"username":"you@co.com","password":"…"}' \
  | jq -r .data.token)

# 2. Call any endpoint with the token
curl -X POST https://api.docs-dispatcher.io/api/invoicing \
  -H "Authorization: Bearer $JWT" \
  -H "Content-Type: application/json" \
  --data @invoice.json
```

{% endtab %}

{% tab title="Node.js (fetch)" %}

```javascript
async function login(username, password) {
  const r = await fetch('https://api.templater.docs-dispatcher.io/auth', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ username, password })
  });
  if (!r.ok) throw new Error(`Login failed: ${r.status}`);
  const { data } = await r.json();
  return data.token;
}

async function dispatch(token, payload) {
  const r = await fetch('https://api.docs-dispatcher.io/api/invoicing', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify(payload)
  });
  if (!r.ok) throw new Error(`Dispatch failed: ${r.status}`);
  return r.json();
}
```

{% endtab %}

{% tab title="PHP (Guzzle)" %}

```php
<?php
use GuzzleHttp\Client;

$client = new Client(['base_uri' => 'https://api.docs-dispatcher.io/']);

// 1. Login
$response = $client->post('auth', [
    'json' => ['username' => $email, 'password' => $password]
]);
$token = json_decode($response->getBody(), true)['data']['token'];

// 2. Dispatch
$response = $client->post('api/invoicing', [
    'headers' => ['Authorization' => "Bearer $token"],
    'json'    => $invoicePayload,
]);
```

{% endtab %}

{% tab title="Java (HttpClient)" %}

```java
import java.net.URI;
import java.net.http.*;

HttpClient client = HttpClient.newHttpClient();

// 1. Login
HttpRequest login = HttpRequest.newBuilder()
    .uri(URI.create("https://api.templater.docs-dispatcher.io/auth"))
    .header("Content-Type", "application/json")
    .POST(HttpRequest.BodyPublishers.ofString(
        "{\"username\":\"" + email + "\",\"password\":\"" + password + "\"}"))
    .build();
HttpResponse<String> loginResp = client.send(login, HttpResponse.BodyHandlers.ofString());
String token = new com.fasterxml.jackson.databind.ObjectMapper()
    .readTree(loginResp.body()).get("data").get("token").asText();

// 2. Dispatch
HttpRequest dispatch = HttpRequest.newBuilder()
    .uri(URI.create("https://api.docs-dispatcher.io/api/invoicing"))
    .header("Authorization", "Bearer " + token)
    .header("Content-Type", "application/json")
    .POST(HttpRequest.BodyPublishers.ofString(invoiceJson))
    .build();
```

{% endtab %}
{% endtabs %}

## Security considerations

* **HTTPS only** — the JWT is a bearer credential; never send it over plain HTTP.
* **Short lifetimes** — tokens are valid 120 minutes by default; refresh proactively or on `401`.
* **Storage** — store the token like you would a password. In a browser context, never put it in `localStorage`; proxy the call from your backend instead.
* **One per identity** — refresh tokens carry the same user/company as the original. Treat impersonation tokens as a separate identity audit-wise.

## Common errors

| Code                          | Cause                                                  | Fix                                                          |
| ----------------------------- | ------------------------------------------------------ | ------------------------------------------------------------ |
| `401 Unauthorized`            | Missing/invalid/expired token                          | Re-issue via `POST /auth` or refresh via `GET /auth/refresh` |
| `403 Forbidden` (on login)    | Wrong username/password, or user disabled              | Verify credentials; contact admin if the account is locked   |
| `403 Forbidden` (on API call) | Token valid but user lacks permission for this service | Have an admin grant the service permission                   |

## Next steps

* [Basic Auth](/docs-dispatcher/authentication/basic-auth.md) — the simpler alternative
* [Using Basic Auth in your code](/docs-dispatcher/authentication/using-basic-auth.md) — the same patterns adapted to Basic credentials
* [Quickstart](/docs-dispatcher/start-here/quickstart.md) — your first dispatched request


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.docs-dispatcher.io/docs-dispatcher/authentication/jwt-bearer.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
