Skip to content
TriedDocs
Account

Create public key

Create a browser public key limited to your domains, with agent limits, reCAPTCHA and fraud checks.

POSThttps://api.tried.com.au/public-keys

Needs keys Creates a public key for browser use, such as the website chat widget or a web call button. reCAPTCHA checks need a Google reCAPTCHA v3 secret (RECAPTCHA_SECRET) on the server; without it they run in mock mode.

How public keys work#

A browser sends Authorization: Bearer public_key_.... Tried accepts it only when:

  • The request's Origin host matches an entry in allowed_domains. Without an allowed Origin the request gets 401.
  • The route is one of POST /v3/create-web-call, POST /create-chat, POST /create-chat-completion or POST /end-chat/{id}. Any other route gets 403 with A public key can only start web calls and chats.

Then, on every request:

  • Agent limits. Web calls always use the key's agent_id; asking for another agent returns 403. Chats use chat_agents (weighted A/B pick) or chat_agent_id; a chat agent outside that list returns 403. Chat completions and end-chat only work on chats started with the same key.
  • Blocked countries. The client country comes from the cf-ipcountry, x-vercel-ip-country or x-country-code header set by your CDN or load balancer. A listed country gets 403.
  • Rate limits. 30 requests per minute per client IP on each key (set per deployment with PUBLIC_KEY_IP_RATE_PER_MINUTE), plus the key's own rate_limit_per_minute if set. Over either limit returns 429.
  • reCAPTCHA v3. With recaptcha_enabled, the browser sends a token in the body as recaptcha_token or in the x-recaptcha-token header. A missing token, a failed check or a score below recaptcha_threshold returns 403. In mock mode a token of mock:<score> scores that value, any other token scores 0.9, and no token scores 0.

Calls and chats started with a public key carry public_key_id and origin in their metadata.

Request#

Field Type Required Description
name string No Label, 1 to 80 characters. Default Website widget.
allowed_domains string[] Yes 1 to 20 hosts the key works from: example.com, *.example.com (also matches example.com itself) or localhost. Add :port to match one port only.
agent_id string or null One of three Voice agent for browser web calls.
chat_agent_id string or null One of three Chat agent for chats started with this key.
chat_agents object[] One of three Up to 10 { "chat_agent_id": string, "weight": number } entries for an A/B split. Weights are 0 or more and need not add up to 1; an entry with weight 0 is never picked. Takes priority over chat_agent_id.
recaptcha_enabled boolean No Require a reCAPTCHA v3 token. Default false.
recaptcha_threshold number No Lowest accepted score, 0 to 1. Default 0.5.
rate_limit_per_minute integer or null No Requests per minute across all browsers using this key, 1 to 10000. null means no key-wide limit (the per-IP limit still applies).
blocked_countries string[] No Up to 250 two-letter country codes to reject. Stored in upper case.

You must set at least one of agent_id, chat_agent_id or chat_agents. Every agent you name must exist in the workspace.

Example request#

curl -X POST https://api.tried.com.au/public-keys \
  -H "Authorization: Bearer $TRIED_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Website widget",
    "allowed_domains": ["example.com.au", "*.example.com.au"],
    "chat_agent_id": "chat_agent_1e5b9d3f7a2c4e6b8d0f2a4c",
    "agent_id": "agent_2f8d4b6a0c1e3f5a7b9d1c3e",
    "recaptcha_enabled": true,
    "rate_limit_per_minute": 60
  }'
const res = await fetch("https://api.tried.com.au/public-keys", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.TRIED_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    name: "Website widget",
    allowed_domains: ["example.com.au", "*.example.com.au"],
    chat_agent_id: "chat_agent_1e5b9d3f7a2c4e6b8d0f2a4c",
    agent_id: "agent_2f8d4b6a0c1e3f5a7b9d1c3e",
    recaptcha_enabled: true,
    rate_limit_per_minute: 60,
  }),
});
const key = await res.json();
import os, requests

res = requests.post(
    "https://api.tried.com.au/public-keys",
    headers={"Authorization": f"Bearer {os.environ['TRIED_API_KEY']}"},
    json={
        "name": "Website widget",
        "allowed_domains": ["example.com.au", "*.example.com.au"],
        "chat_agent_id": "chat_agent_1e5b9d3f7a2c4e6b8d0f2a4c",
        "agent_id": "agent_2f8d4b6a0c1e3f5a7b9d1c3e",
        "recaptcha_enabled": True,
        "rate_limit_per_minute": 60,
    },
)
key = res.json()

Response#

201 Created

{
  "public_key_id": "pk_4a7e1c9b2d0f5e83a6c1b7d2",
  "name": "Website widget",
  "public_key": "public_key_9c2e4f7a1b3d5e6f8a0c2e4b6d8f1a3c",
  "chat_agent_id": "chat_agent_1e5b9d3f7a2c4e6b8d0f2a4c",
  "allowed_domains": ["example.com.au", "*.example.com.au"],
  "created_timestamp": 1760313600000,
  "chat_agents": [],
  "agent_id": "agent_2f8d4b6a0c1e3f5a7b9d1c3e",
  "recaptcha_enabled": true,
  "recaptcha_threshold": 0.5,
  "rate_limit_per_minute": 60,
  "blocked_countries": []
}
Field Type Description
public_key_id string ID used to update or delete the key.
public_key string The value browsers send as Bearer. public_key_ followed by 32 hex characters. It is not secret: it only works from allowed_domains and only on the routes listed above.
chat_agents object[] The A/B split. [] when not set.

The create is recorded in the audit log as public_key.create.

Errors#

Status When
400 Set chat_agent_id, chat_agents or agent_id, or a field fails validation (for example Use domains like example.com, *.example.com or localhost, or an empty allowed_domains).
401 Missing or invalid API key.
403 The key's IP allowlist or scopes reject the request. This route needs the admin scope. Dashboard members get The member role is read-only.
404 A named agent does not exist, for example Chat agent chat_agent_1e5b9d3f7a2c4e6b8d0f2a4c not found or Agent agent_2f8d4b6a0c1e3f5a7b9d1c3e not found.
429 Rate limit exceeded. See Rate limits.

See Errors for the body format.