Publishable Keys
Create, list, and revoke the pk_live_ publishable keys that let the embeddable web widget start browser voice sessions with your agents from your own site.
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 yourcall.incomingwebhook 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
{
"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
curl https://api.thunderphone.com/v1/publishable-key \
-H "Authorization: Bearer sk_live_YOUR_API_KEY"Returns an array of Publishable key objects.
Create a publishable key
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"]
}'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"]
}'| 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(matchesapi.example.combut not bareexample.com) localhostand127.0.0.1are always allowed during local dev, regardless of the list- Bare wildcards (
*or*.*) are rejected with400 - 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.
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
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"}'| Field | Type | Required | Description |
|---|---|---|---|
target_org_id | integer | yes | |
mode | string | yes | copy or move |
name | string | no | Override the copy's name |