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

# API Keys

> Create scoped API keys, list active keys, and revoke keys you no longer need.

API keys authenticate every REST call (`Authorization: Bearer gork_live_...`). Raw key material is never stored — only a SHA-256 hash — so the secret returned at creation is shown **exactly once** and can never be retrieved again.

## Create a key

`POST /v1/keys` mints a new key. Requires the `keys:write` scope.

<ParamField body="name" type="string" required>
  Human-readable label, 1–64 characters (e.g. `"billing-agent prod"`).
</ParamField>

<ParamField body="scopes" type="string[]">
  Scopes to grant. Defaults to `["*"]` (full access). Supports exact scopes (`messages:send`) and resource wildcards (`messages:*`).
</ParamField>

<ParamField body="inboxId" type="string">
  Bind the key to a single inbox (`inb_...`), which must belong to your organization and be active. A bound key is confined to mail-only scopes — `inboxes:read`, `messages:read`, `messages:send`, `threads:read` — and every call is restricted to that inbox. When `scopes` is omitted the bound key receives all four; when provided, the list is intersected with the allowlist and rejected with `400 invalid_scopes` if nothing remains.
</ParamField>

<RequestExample>
  ```bash cURL theme={null}
  curl -X POST https://api.gork.email/v1/keys \
    -H "Authorization: Bearer gork_live_YOUR_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{"name": "billing-agent prod", "scopes": ["messages:read", "messages:send"]}'
  ```

  ```typescript TypeScript theme={null}
  import { Gork } from "@gork/sdk"

  const gork = new Gork({ apiKey: process.env.GORK_API_KEY })

  const { apiKey } = await gork.keys.create({
    name: "billing-agent prod",
    scopes: ["messages:read", "messages:send"],
  })
  // Save apiKey now — it will never be shown again.
  ```

  ```python Python theme={null}
  import requests, os

  res = requests.post(
      "https://api.gork.email/v1/keys",
      headers={"Authorization": f"Bearer {os.getenv('GORK_API_KEY')}"},
      json={"name": "billing-agent prod", "scopes": ["messages:read", "messages:send"]},
  )
  ```
</RequestExample>

<ResponseExample>
  ```json 201 Created theme={null}
  {
    "data": {
      "id": "key_1a2b3c4d5e6f",
      "name": "billing-agent prod",
      "keyPrefix": "gork_live_abcd12",
      "scopes": ["messages:read", "messages:send"],
      "inboxId": null,
      "createdAt": "2026-09-11T08:00:00.000Z",
      "apiKey": "gork_live_9f2c4a1e8b7d6f5a3c2e1d0b9a8f7e6d5c4b3a2918",
      "warning": "Please save this secret key safely. You will not be able to view it again."
    }
  }
  ```
</ResponseExample>

| Code              | HTTP | Meaning                                                                   |
| ----------------- | ---- | ------------------------------------------------------------------------- |
| `inbox_not_found` | 404  | The `inboxId` binding target is not an active inbox in your organization. |
| `invalid_scopes`  | 400  | An inbox-bound key requested only non-mail scopes.                        |

## List keys

`GET /v1/keys` returns every key in your organization, newest first. Requires the `keys:read` scope. Only the key **prefix** is returned — never the hash, never the raw secret.

<ResponseField name="data" type="object[]">
  <Expandable title="properties">
    <ResponseField name="id" type="string">
      Unique key ID (`key_...`).
    </ResponseField>

    <ResponseField name="name" type="string">
      The label given at creation.
    </ResponseField>

    <ResponseField name="keyPrefix" type="string">
      First characters of the key (e.g. `gork_live_abcd12`) for identification.
    </ResponseField>

    <ResponseField name="scopes" type="string[]">
      Granted scopes.
    </ResponseField>

    <ResponseField name="inboxId" type="string">
      Bound inbox, or `null` for organization-wide keys.
    </ResponseField>

    <ResponseField name="isActive" type="boolean">
      `false` once revoked.
    </ResponseField>

    <ResponseField name="lastUsedAt" type="string">
      Last authenticated call, or `null` when never used.
    </ResponseField>

    <ResponseField name="createdAt" type="string">
      ISO 8601 creation timestamp.
    </ResponseField>
  </Expandable>
</ResponseField>

```bash cURL theme={null}
curl https://api.gork.email/v1/keys \
  -H "Authorization: Bearer gork_live_YOUR_API_KEY"
```

## Revoke a key

`DELETE /v1/keys/{id}` revokes a key. Requires the `keys:write` scope. Revocation is a soft revoke — the row is marked inactive (`isActive: false`) rather than deleted, so past audit references stay intact. Revoked keys immediately receive `401 invalid_api_key`.

<RequestExample>
  ```bash cURL theme={null}
  curl -X DELETE https://api.gork.email/v1/keys/key_1a2b3c4d5e6f \
    -H "Authorization: Bearer gork_live_YOUR_API_KEY"
  ```

  ```typescript TypeScript theme={null}
  await gork.keys.delete("key_1a2b3c4d5e6f")
  ```

  ```python Python theme={null}
  res = requests.delete(
      "https://api.gork.email/v1/keys/key_1a2b3c4d5e6f",
      headers={"Authorization": f"Bearer {os.getenv('GORK_API_KEY')}"},
  )
  ```
</RequestExample>

<ResponseExample>
  ```json 200 OK theme={null}
  {
    "data": {
      "id": "key_1a2b3c4d5e6f",
      "status": "revoked"
    }
  }
  ```
</ResponseExample>

| Code            | HTTP | Meaning                                          |
| --------------- | ---- | ------------------------------------------------ |
| `key_not_found` | 404  | No key with this ID exists in your organization. |
