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

# Publishable Keys

> Manage `pk_live_` keys used by the embeddable web widget.

Publishable keys (`pk_live_...`) are **public** credentials that the
embeddable web widget uses from the browser. Unlike secret API keys,
they are scoped to a specific agent (or a webhook that will dynamically
resolve one) and gated to a list of allowed origins — safe to ship in
your front-end code.

Each key is associated with exactly one of:

* `mode = "agent"`: fixed binding to an agent id. Simplest setup.
* `mode = "webhook"`: the widget calls your
  [`call.incoming` webhook](/webhooks/call-incoming) to dynamically
  resolve an agent per visitor.

## Endpoints

| Method   | Path                                    | Description                          |
| -------- | --------------------------------------- | ------------------------------------ |
| `GET`    | `/v1/publishable-key`                   | List publishable keys                |
| `POST`   | `/v1/publishable-key`                   | Create a publishable key             |
| `GET`    | `/v1/publishable-key/{key_id}`          | Retrieve a key                       |
| `PATCH`  | `/v1/publishable-key/{key_id}`          | Update name / allowed domains / mode |
| `DELETE` | `/v1/publishable-key/{key_id}`          | Deactivate a key                     |
| `POST`   | `/v1/publishable-key/{key_id}/transfer` | Copy or move to another org          |

## Publishable key object

```json theme={null}
{
  "id": 9,
  "name": "Marketing site (prod)",
  "key": "pk_live_abc123...",
  "key_prefix": "pk_live_abc",
  "allowed_domains": ["example.com", "*.example.com"],
  "is_active": true,
  "is_enabled": true,
  "mode": "agent",
  "agent_id": 12,
  "agent_name": "Customer Support Agent",
  "webhook_url": null,
  "created_at": "2026-04-20T18:24:10.113Z",
  "updated_at": "2026-04-20T18:24:10.113Z"
}
```

| Field                      | Type            | Description                                                                                            |
| -------------------------- | --------------- | ------------------------------------------------------------------------------------------------------ |
| `id`                       | integer         | Key id                                                                                                 |
| `name`                     | string          | Display name                                                                                           |
| `key`                      | string          | The raw `pk_live_...` value. Publishable keys are public, so the full value is retrievable at any time |
| `key_prefix`               | string          | First 12 chars for display (e.g. `pk_live_abc`)                                                        |
| `allowed_domains`          | array of string | Origin allowlist — exact hostnames and `*.subdomain` wildcards. `localhost`/`127.0.0.1` always allowed |
| `is_active`                | boolean         | `false` after deactivation; a deactivated key stops working                                            |
| `is_enabled`               | boolean         | Temporary disable toggle (separate from `is_active`)                                                   |
| `mode`                     | string          | `agent` or `webhook`                                                                                   |
| `agent_id`                 | integer \| null | Required for `mode="agent"`                                                                            |
| `agent_name`               | string \| null  | Read-only convenience — the linked agent's name                                                        |
| `webhook_url`              | string \| null  | Required for `mode="webhook"` — overrides the org-level webhook                                        |
| `created_at`, `updated_at` | timestamp       |                                                                                                        |

***

## List publishable keys

<CodeGroup>
  ```bash cURL theme={null}
  curl https://api.thunderphone.com/v1/publishable-key \
    -H "Authorization: Bearer sk_live_YOUR_API_KEY"
  ```
</CodeGroup>

Returns an array of [Publishable key objects](#publishable-key-object).

***

## Create a publishable key

<CodeGroup>
  ```bash Agent-bound key theme={null}
  curl -X POST https://api.thunderphone.com/v1/publishable-key \
    -H "Authorization: Bearer sk_live_YOUR_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "name": "Marketing site (prod)",
      "mode": "agent",
      "agent_id": 12,
      "allowed_domains": ["example.com", "*.example.com"]
    }'
  ```

  ```bash Webhook-mode key theme={null}
  curl -X POST https://api.thunderphone.com/v1/publishable-key \
    -H "Authorization: Bearer sk_live_YOUR_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "name": "Dynamic widget",
      "mode": "webhook",
      "webhook_url": "https://example.com/thunderphone-webhook",
      "allowed_domains": ["example.com"]
    }'
  ```
</CodeGroup>

| Field             | Type            | Required                  | Description                                  |
| ----------------- | --------------- | ------------------------- | -------------------------------------------- |
| `name`            | string          | no                        | Defaults to `"Web Widget"`                   |
| `mode`            | string          | no                        | `agent` (default) or `webhook`               |
| `agent_id`        | integer         | yes (if `mode="agent"`)   |                                              |
| `webhook_url`     | string          | yes (if `mode="webhook"`) | HTTPS URL                                    |
| `allowed_domains` | array of string | **yes**                   | At least one entry required. See rules below |
| `is_enabled`      | boolean         | no                        | Defaults to `true`                           |

### Allowed-domain rules

* An exact match: `example.com`
* A wildcard match: `*.example.com` (matches `api.example.com` but not
  bare `example.com`)
* `localhost` and `127.0.0.1` are always allowed during local dev,
  regardless of the list
* Bare wildcards (`*` or `*.*`) are rejected with `400`
* **At least one entry is required** — passing an empty array returns
  `400 allowed_domains: "At least one allowed domain is required."`

Returns `201 Created` with the new [Publishable key object](#publishable-key-object).
The `key` field is included on creation **and** is retrievable later
via `GET` — publishable keys are public by design.

***

## Update / delete

`PATCH` accepts any subset of `name`, `allowed_domains`, `mode`,
`agent_id`, `webhook_url`, `is_enabled`. Switching between modes
requires providing the new required field (`agent_id` or
`webhook_url`).

`DELETE` sets `is_active=false` — the key stops working but remains
visible in list responses so you can audit prior deployments. There is
no hard delete.

***

## Transfer a publishable key

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST https://api.thunderphone.com/v1/publishable-key/9/transfer \
    -H "Authorization: Bearer sk_live_YOUR_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{"target_org_id": 77, "mode": "copy"}'
  ```
</CodeGroup>

| Field           | Type    | Required | Description              |
| --------------- | ------- | -------- | ------------------------ |
| `target_org_id` | integer | yes      |                          |
| `mode`          | string  | yes      | `copy` or `move`         |
| `name`          | string  | no       | Override the copy's name |

***

## Related

<CardGroup cols={2}>
  <Card title="Widget overview" icon="window-maximize" href="/widget/overview">
    How the widget uses `pk_live_` keys at runtime.
  </Card>

  <Card title="Dynamic webhook config" icon="bolt" href="/webhooks/call-incoming">
    Shape the `call.incoming` webhook must respond with in webhook mode.
  </Card>
</CardGroup>
