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

# API keys

> Create, list, and revoke API keys for Lettuce Stream.

Use these endpoints to manage API keys for a user account. These endpoints require a valid Firebase ID token (not an API key) because they create or revoke keys.

## Authentication

Provide an ID token using one of these methods:

* `Authorization: Bearer <ID_TOKEN>`
* `x-id-token: <ID_TOKEN>`
* JSON body field `idToken`

## Base URL

```
https://www.lettucestream.com
```

## Endpoints

### List API keys

`GET /api/api-keys`

**Response**

```json theme={null}
{
  "keys": [
    {
      "id": "string",
      "label": "string",
      "prefix": "ls_live_AbCd...",
      "createdAt": "2026-02-06T18:02:12.123Z",
      "lastUsedAt": "2026-02-06T18:10:55.000Z",
      "revokedAt": null
    }
  ]
}
```

**Example requests**

<Tabs>
  <Tab title="cURL">
    ```bash theme={null}
    curl "https://www.lettucestream.com/api/api-keys" \
      -H "Authorization: Bearer <ID_TOKEN>"
    ```
  </Tab>

  <Tab title="Node.js">
    ```js theme={null}
    import fetch from "node-fetch";

    const response = await fetch("https://www.lettucestream.com/api/api-keys", {
      headers: {
        Authorization: "Bearer <ID_TOKEN>",
      },
    });

    const data = await response.json();
    console.log(data.keys);
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    import requests

    response = requests.get(
        "https://www.lettucestream.com/api/api-keys",
        headers={"Authorization": "Bearer <ID_TOKEN>"},
        timeout=10,
    )

    print(response.json()["keys"])
    ```
  </Tab>
</Tabs>

### Create API key

`POST /api/api-keys`

**Body**

```json theme={null}
{
  "label": "Production key"
}
```

**Response**

```json theme={null}
{
  "key": "ls_live_...",
  "keyId": "string",
  "keys": [
    {
      "id": "string",
      "label": "string",
      "prefix": "ls_live_AbCd...",
      "createdAt": "2026-02-06T18:02:12.123Z",
      "lastUsedAt": null,
      "revokedAt": null
    }
  ]
}
```

**Example requests**

<Tabs>
  <Tab title="cURL">
    ```bash theme={null}
    curl -X POST "https://www.lettucestream.com/api/api-keys" \
      -H "Authorization: Bearer <ID_TOKEN>" \
      -H "Content-Type: application/json" \
      -d '{"label":"Production key"}'
    ```
  </Tab>

  <Tab title="Node.js">
    ```js theme={null}
    import fetch from "node-fetch";

    const response = await fetch("https://www.lettucestream.com/api/api-keys", {
      method: "POST",
      headers: {
        Authorization: "Bearer <ID_TOKEN>",
        "Content-Type": "application/json",
      },
      body: JSON.stringify({ label: "Production key" }),
    });

    const data = await response.json();
    console.log(data.key);
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    import requests

    response = requests.post(
        "https://www.lettucestream.com/api/api-keys",
        headers={
            "Authorization": "Bearer <ID_TOKEN>",
            "Content-Type": "application/json",
        },
        json={"label": "Production key"},
        timeout=10,
    )

    print(response.json()["key"])
    ```
  </Tab>
</Tabs>

**Notes**

* The full `key` value is returned only once. Store it securely.
* The UI and API store only a SHA-256 hash of the key.

### Revoke API key

`DELETE /api/api-keys`

**Body**

```json theme={null}
{
  "keyId": "string"
}
```

**Response**

```json theme={null}
{
  "keys": [
    {
      "id": "string",
      "label": "string",
      "prefix": "ls_live_AbCd...",
      "createdAt": "2026-02-06T18:02:12.123Z",
      "lastUsedAt": "2026-02-06T18:10:55.000Z",
      "revokedAt": "2026-02-06T18:12:01.000Z"
    }
  ]
}
```

**Example requests**

<Tabs>
  <Tab title="cURL">
    ```bash theme={null}
    curl -X DELETE "https://www.lettucestream.com/api/api-keys" \
      -H "Authorization: Bearer <ID_TOKEN>" \
      -H "Content-Type: application/json" \
      -d '{"keyId":"key_123"}'
    ```
  </Tab>

  <Tab title="Node.js">
    ```js theme={null}
    import fetch from "node-fetch";

    const response = await fetch("https://www.lettucestream.com/api/api-keys", {
      method: "DELETE",
      headers: {
        Authorization: "Bearer <ID_TOKEN>",
        "Content-Type": "application/json",
      },
      body: JSON.stringify({ keyId: "key_123" }),
    });

    const data = await response.json();
    console.log(data.keys);
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    import requests

    response = requests.delete(
        "https://www.lettucestream.com/api/api-keys",
        headers={
            "Authorization": "Bearer <ID_TOKEN>",
            "Content-Type": "application/json",
        },
        json={"keyId": "key_123"},
        timeout=10,
    )

    print(response.json()["keys"])
    ```
  </Tab>
</Tabs>

## Example UI

Use a lightweight UI library like [shadcn/ui](https://ui.shadcn.com/) with a free placeholder API to prototype your API-key dashboard layout before wiring it to the Lettuce Stream endpoints.

```tsx theme={null}
import { useEffect, useState } from "react";
import { Button } from "@/components/ui/button";
import {
  Table,
  TableBody,
  TableCell,
  TableHead,
  TableHeader,
  TableRow,
} from "@/components/ui/table";

const placeholderUrl = "https://jsonplaceholder.typicode.com/users";

export default function ApiKeysTable() {
  const [rows, setRows] = useState([]);

  useEffect(() => {
    const loadRows = async () => {
      const response = await fetch(placeholderUrl);
      const users = await response.json();

      setRows(
        users.slice(0, 5).map((user) => ({
          id: `key_${user.id}`,
          label: `${user.company.name} key`,
          prefix: "ls_live_AbCd...",
          createdAt: new Date().toISOString(),
          lastUsedAt: "-",
        }))
      );
    };

    loadRows();
  }, []);

  return (
    <div className="space-y-4">
      <div className="flex items-center justify-between">
        <div>
          <p className="text-sm text-muted-foreground">
            Replace the placeholder API with your Lettuce Stream requests once
            your auth flow is ready.
          </p>
        </div>
        <Button>Create key</Button>
      </div>

      <Table>
        <TableHeader>
          <TableRow>
            <TableHead>Label</TableHead>
            <TableHead>Prefix</TableHead>
            <TableHead>Created</TableHead>
            <TableHead>Last used</TableHead>
            <TableHead className="text-right">Actions</TableHead>
          </TableRow>
        </TableHeader>
        <TableBody>
          {rows.map((row) => (
            <TableRow key={row.id}>
              <TableCell className="font-medium">{row.label}</TableCell>
              <TableCell>{row.prefix}</TableCell>
              <TableCell>{row.createdAt}</TableCell>
              <TableCell>{row.lastUsedAt}</TableCell>
              <TableCell className="text-right">
                <Button variant="ghost">Revoke</Button>
              </TableCell>
            </TableRow>
          ))}
        </TableBody>
      </Table>
    </div>
  );
}
```

## Limits

* Maximum active keys per user: `API_KEYS_MAX_ACTIVE` (default 10).
* Key prefix format: `API_KEY_PREFIX` (default `ls_live_`).
* Prefix preview length: `API_KEY_PREFIX_LENGTH` (default 12).

## Errors

* `400` — Missing label or key id, or maximum active keys reached.
* `401` — Missing or invalid authentication token.
* `403` — Attempted to revoke a key not owned by the user.
* `404` — API key not found.
* `405` — Method not allowed (allowed: GET, POST, DELETE).
* `500` — Server error.

## Example (curl)

```bash theme={null}
curl -X POST "https://www.lettucestream.com/api/api-keys" \
  -H "Authorization: Bearer <ID_TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{"label":"Prod key"}'
```

## Using API keys on other endpoints

Other protected endpoints accept API keys via:

* `x-api-key: <API_KEY>`
* `Authorization: Bearer <API_KEY>`
