Skip to main content
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 to dynamically resolve an agent per visitor.

Endpoints

MethodPathDescription
GET/v1/publishable-keyList publishable keys
POST/v1/publishable-keyCreate 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}/transferCopy 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,
  "webhook_url": null,
  "created_at": "2026-04-20T18:24:10.113Z",
  "updated_at": "2026-04-20T18:24:10.113Z"
}
FieldTypeDescription
idintegerKey id
namestringDisplay name
keystringThe raw pk_live_... value. Publishable keys are public, so the full value is retrievable at any time
key_prefixstringFirst 12 chars for display (e.g. pk_live_abc)
allowed_domainsarray of stringOrigin allowlist — exact hostnames and *.subdomain wildcards. localhost/127.0.0.1 always allowed
is_activebooleanfalse after deactivation; a deactivated key stops working
is_enabledbooleanTemporary disable toggle (separate from is_active)
modestringagent or webhook
agent_idinteger | nullRequired for mode="agent"
webhook_urlstring | nullRequired for mode="webhook" — overrides the org-level webhook
created_at, updated_attimestamp

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"]
  }'
FieldTypeRequiredDescription
namestringnoDefaults to "Web Widget"
modestringnoagent (default) or webhook
agent_idintegeryes (if mode="agent")
webhook_urlstringyes (if mode="webhook")HTTPS URL
allowed_domainsarray of stringyesAt least one entry required. See rules below
is_enabledbooleannoDefaults 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. 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"}'
FieldTypeRequiredDescription
target_org_idintegeryes
modestringyescopy or move
namestringnoOverride the copy’s name

Widget overview

How the widget uses pk_live_ keys at runtime.

Dynamic webhook config

Shape the call.incoming webhook must respond with in webhook mode.