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

# Authentication

> How API keys work, how to send them, and how to keep them safe.

Every call to [`POST /generate`](/api-reference/generate) is authenticated with an **API key**, sent as a bearer token.

```bash theme={"dark"}
Authorization: Bearer tipar_live_<32 characters>
```

A key looks like `tipar_live_aB3dE...` — the `tipar_live_` prefix followed by 32 random characters. There is no separate API secret or signature; the key alone authenticates the request.

## Creating a key

API keys are managed from the [dashboard](https://app.tipar.dev), not from the API:

<Steps>
  <Step title="Sign in">
    [app.tipar.dev](https://app.tipar.dev) signs you in with a passwordless magic link sent to your email.
  </Step>

  <Step title="Create a key">
    Under **API keys**, give the key a name (something that says where it's used — `prod-billing-worker`, `staging`) and create it.
  </Step>

  <Step title="Copy it once">
    The full key is shown **only at creation**. Tipar stores a SHA-256 hash, never the key itself — so it can't show it to you again and can't recover it. Copy it now into your secrets store.
  </Step>
</Steps>

<Info>
  The name and a four-character suffix (e.g. `…aB3d`) are kept so you can recognise a key in the dashboard. The suffix is not enough to use the key — only the value you copied at creation is.
</Info>

## Using a key

Send it as a bearer token on every request:

<CodeGroup>
  ```bash curl theme={"dark"}
  curl https://api.tipar.dev/generate \
    -H "Authorization: Bearer $TIPAR_API_KEY" \
    -H "Content-Type: application/json" \
    -d @request.json --output out.pdf
  ```

  ```javascript Node.js theme={"dark"}
  const res = await fetch("https://api.tipar.dev/generate", {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.TIPAR_API_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ template, data }),
  });
  ```

  ```python Python theme={"dark"}
  import os, requests

  res = requests.post(
      "https://api.tipar.dev/generate",
      headers={"Authorization": f"Bearer {os.environ['TIPAR_API_KEY']}"},
      json={"template": template, "data": data},
  )
  ```
</CodeGroup>

## When authentication fails

A missing, malformed, or revoked key returns **`401 Unauthorized`** with a `WWW-Authenticate: Bearer` header. The body is a [Problem Details](/api-reference/errors) document. The request never reaches the renderer, so no quota or rate limit is consumed.

| Situation                            | Result                                                                            |
| ------------------------------------ | --------------------------------------------------------------------------------- |
| No `Authorization` header            | `401 Unauthorized`                                                                |
| Header isn't `Bearer <key>`          | `401 Unauthorized`                                                                |
| Key doesn't exist or was revoked     | `401 Unauthorized`                                                                |
| Valid key, but monthly quota reached | `402 Payment Required` — see [quotas](/api-reference/overview#quotas-and-billing) |

<Tip>
  Tipar checks the key's prefix before any database lookup, so malformed tokens are rejected cheaply. That's an implementation detail, not something you need to handle — just send a valid `tipar_live_` key.
</Tip>

## Revoking and rotating

Revoke a key from the dashboard at any time. Revocation takes effect within seconds across the service; the next request with that key gets `401`.

To rotate without downtime:

<Steps>
  <Step title="Create the replacement">
    Mint a new key and deploy it to your environment alongside the old one.
  </Step>

  <Step title="Cut over">
    Switch traffic to the new key and confirm requests still return `200`.
  </Step>

  <Step title="Revoke the old key">
    Once nothing uses it, revoke the old key from the dashboard.
  </Step>
</Steps>

## Good practice

<CardGroup cols={2}>
  <Card title="Keep keys server-side" icon="server">
    Use a key only from a backend you control. Never embed it in a browser, a mobile app, or anything a user can inspect.
  </Card>

  <Card title="Store in env / secrets" icon="lock">
    Inject the key through environment variables or a secrets manager. Keep it out of source control and CI logs.
  </Card>

  <Card title="One key per use" icon="diagram-project">
    Separate keys for prod, staging, and each service. Revoking one then never affects the others.
  </Card>

  <Card title="Rotate on exposure" icon="rotate">
    If a key might have leaked, revoke it immediately and mint a replacement. There's no limit on how many keys you can hold.
  </Card>
</CardGroup>
