---
title: API Keys Reference
path: reference/api-keys
status: published
---

# API Keys Reference

Endpoints for managing tenant API keys. For the conceptual overview, see [Authentication](../concepts/authentication).

**Base path:** `/v3/api_keys/`
**Required permission:** `admin.api_keys` (for all endpoints except `/v3/scopes`).

## GET /v3/api_keys

List API keys for the current tenant.

**Query parameters:**

| Parameter | Type | Default | Notes |
|-----------|------|---------|-------|
| `include_revoked` | boolean | `false` | If true, include revoked keys |

**Response (200):**

```json
{
  "api_keys": [
    {
      "id": "key_01HXYZ",
      "name": "production-sender",
      "prefix": "sg_live_a1b2c3d4",
      "environment": "live",
      "scopes": ["mail.send", "mail.schedule"],
      "created_at": "2026-04-23T10:00:00Z",
      "last_used_at": "2026-04-23T10:15:30Z",
      "expires_at": null,
      "revoked_at": null
    }
  ]
}
```

The `prefix` is the first 12 characters of the key — enough to identify it, not enough to authenticate with. The full secret is only available at creation.

## POST /v3/api_keys

Create a new API key.

**Request body:**

| Field | Type | Required | Notes |
|-------|------|---------|-------|
| `name` | string | Yes | 1–255 chars |
| `environment` | string | No | `live` (default) or `test` |
| `scopes` | array of string | No | Permission scopes; defaults to empty (no access) |
| `expires_at` | datetime (ISO-8601) | No | Optional expiry |

**Response (201):**

```json
{
  "id": "key_01HXYZ",
  "name": "production-sender",
  "api_key": "sg_live_a1b2c3d4e5f6g7h8i9j0...",
  "prefix": "sg_live_a1b2c3d4",
  "environment": "live",
  "scopes": ["mail.send"],
  "created_at": "2026-04-23T10:00:00Z",
  "expires_at": null
}
```

**The `api_key` field is returned exactly once.** Store it immediately.

**Errors:**

| Status | Cause |
|--------|-------|
| 400 | `scopes` contains an unknown permission name |
| 403 | `scopes` requests a permission the caller doesn't have |

## GET /v3/api_keys/{key_id}

Get a single API key's metadata (never the secret).

**Response (200):**

```json
{
  "id": "key_01HXYZ",
  "name": "production-sender",
  "prefix": "sg_live_a1b2c3d4",
  "environment": "live",
  "scopes": ["mail.send"],
  "created_at": "2026-04-23T10:00:00Z",
  "last_used_at": "2026-04-23T10:15:30Z",
  "expires_at": null,
  "revoked_at": null
}
```

## PATCH /v3/api_keys/{key_id}

Update name or scopes on an existing key.

**Request body:**

| Field | Type | Notes |
|-------|------|-------|
| `name` | string | New name |
| `scopes` | array | New scope set (replaces existing) |

**Response (200):** updated key metadata.

## DELETE /v3/api_keys/{key_id}

Revoke a key. Takes effect immediately.

**Response (204):** no body.

Revoked keys remain in the database (as an audit record) but cannot authenticate.

## POST /v3/api_keys/{key_id}/regenerate

Rotate a key — issue a new secret, invalidate the old one.

**Response (200):**

```json
{
  "id": "key_01HXYZ",
  "name": "production-sender",
  "api_key": "sg_live_newSecret...",
  "prefix": "sg_live_newSecre",
  "environment": "live",
  "scopes": ["mail.send"],
  "created_at": "2026-04-23T10:00:00Z",
  "rotated_at": "2026-04-24T10:00:00Z"
}
```

The `api_key` field is the new secret, returned once. The old secret stops working immediately.

## GET /v3/scopes

List available permission scopes. Does not require `admin.api_keys` — any authenticated caller can query.

**Query parameters:**

| Parameter | Notes |
|-----------|-------|
| `category` | Filter by category (`mail`, `templates`, `admin`, etc.) |

**Response (200):**

```json
{
  "permissions": [
    {"name": "mail.send", "category": "mail", "description": "Send emails"},
    {"name": "mail.schedule", "category": "mail", "description": "Schedule emails for later delivery"},
    {"name": "mail.cancel", "category": "mail", "description": "Cancel scheduled emails"},
    {"name": "templates.read", "category": "templates", "description": "View templates"},
    {"name": "templates.write", "category": "templates", "description": "Create and update templates"},
    {"name": "templates.delete", "category": "templates", "description": "Delete templates"},
    {"name": "suppressions.read", "category": "suppressions", "description": "View suppression lists"},
    {"name": "suppressions.write", "category": "suppressions", "description": "Manage suppression lists"},
    {"name": "stats.read", "category": "stats", "description": "View email statistics"},
    {"name": "stats.export", "category": "stats", "description": "Export statistics data"},
    {"name": "webhooks.read", "category": "webhooks", "description": "View webhook configurations"},
    {"name": "webhooks.write", "category": "webhooks", "description": "Manage webhook configurations"},
    {"name": "domains.read", "category": "domains", "description": "View sender domains"},
    {"name": "domains.write", "category": "domains", "description": "Manage sender domains"},
    {"name": "admin.api_keys", "category": "admin", "description": "Manage API keys"},
    {"name": "admin.users", "category": "admin", "description": "Manage user roles"},
    {"name": "admin.settings", "category": "admin", "description": "Manage tenant settings"}
  ]
}
```

## Key format

| Prefix | Environment | Behavior |
|--------|-------------|----------|
| `sg_live_` | Live | Delivers real mail |
| `sg_test_` | Test | Forces sandbox mode on every send |

Both formats are followed by a 64-character hex string. Total key length: 72 characters.

## Related

- [Authentication (guide)](../concepts/authentication)
- [Roles and Permissions](../concepts/roles-and-permissions)
- [Sandbox vs Live](../concepts/sandbox-vs-live)
