On this page
Triggers and webhooks
Webhooks tell your systems what a run did; triggers start runs without an API call. Both are part of the agent's definition: you add them to a draft and they take effect when that version is published.
Webhooks
A webhook sends chosen run events to your HTTPS endpoint, signed with a secret you create. Add it with the notification.put draft edit and publish. The events you can subscribe to: run.admitted, run.ended, recording.updated, analysis.updated.
Add a webhook
# 1. Create the signing secret (admin key). Keep the value: your receiver needs it.
PUT /api/v1/secrets/crm-webhook { "kind": "webhook_signing", "value": "<a long random string>" }
# → { "id": "crm-webhook", "current_version": "<version>", … }
# 2. Add the webhook to the agent's draft, then publish.
PATCH /api/v1/agents/{agent_id}/draft
{ "draft_id": "…", "expected_revision": 7, "edits": [
{ "type": "notification.put", "notification": {
"id": "crm", "url": "https://hooks.example.com/talos", "format": "semantic.v1",
"secret_ref": { "id": "crm-webhook", "version": "<version>" },
"events": ["run.ended", "analysis.updated"] } } ] }
Delivery
The body is the event exactly as the run's event stream carries it (format semantic.v1).
What your endpoint receives
POST https://hooks.example.com/talos
content-type: application/json
x-talos-event-id: 3f6c… # the event's id; the same on every retry
idempotency-key: 3f6c… # the same value: deduplicate on it
x-talos-signature: t=1790380800,v1=5d41402abc4b2a76b9719d911017c592…
{ "event_id": "3f6c…", "type": "run.ended", "sequence": 14, "run": { "run_id": "…", … }, "data": { "outcome": { … }, … }, … }
- Answer
2xxto accept. A408,429,5xxor no answer within 10 seconds is retried, up to 11 attempts; any other4xxis final. - A retry can arrive after a success you sent but Talos did not receive: deduplicate on
idempotency-key. - Each attempt is signed again, with its own timestamp. Deliveries are governed effects: see them with
GET /api/v1/runs/{run_id}/effects. - Test runs (
"test": true) are never delivered.
Verify the signature
x-talos-signature is t=<unix seconds>,v1=<hex HMAC-SHA256> of <t>.<raw body> with your signing secret. Compute it over the raw bytes you received (before parsing the JSON), compare in constant time, and reject old timestamps; five minutes is a reasonable window.
import { createHmac, timingSafeEqual } from "node:crypto";
/** True when the body was signed with your webhook signing secret within the last five minutes. */
export function verifyTalosWebhook(rawBody: string, signatureHeader: string, secret: string): boolean {
const parts = Object.fromEntries(signatureHeader.split(",").map(part => part.split("=", 2) as [string, string]));
// t is unix seconds; v1 is exactly 64 lowercase hex characters (Buffer.from would skip trailing junk).
if (!/^\d{1,12}$/.test(parts.t ?? "") || !/^[0-9a-f]{64}$/.test(parts.v1 ?? "")) return false;
if (Math.abs(Date.now() / 1000 - Number(parts.t)) > 300) return false;
const expected = createHmac("sha256", secret).update(`${parts.t}.${rawBody}`).digest();
return timingSafeEqual(Buffer.from(parts.v1, "hex"), expected);
}Note
Publications pin a secret version. After you rotate a secret with
PUT /api/v1/secrets/{secret_id}, update the draft'ssecret_refand publish again.
Triggers
A trigger starts a background cloud run of the agent it belongs to:
- schedule: a five-field cron expression in a time zone, with a fixed
input. - event: when a run matching
source(an agent, a channel, dispositions) emitsrun.endedoranalysis.updated.input_mappingbuilds the new run's input from JSON pointers into that event.
Draft edits
# Every weekday at 9:00 in New York, start a background run with this input.
{ "type": "trigger.put", "trigger": {
"kind": "schedule", "id": "morning-sweep", "cron": "0 9 * * 1-5", "timezone": "America/New_York",
"input": { "segment": "overdue" } } }
# When a phone run of another agent ends as voicemail, start a run of this agent with fields from that event.
{ "type": "trigger.put", "trigger": {
"kind": "event", "id": "after-voicemail", "event_type": "run.ended",
"source": { "agent_id": "<caller agent id>", "channel": "phone", "dispositions": ["voicemail_left"] },
"input_mapping": { "previous_run_id": "/run/run_id" } } }
- Only the head's triggers are active. Publishing with
"live": falsedoes not register them; promoting that version does, and replaces the previous head's triggers. - Publishing a trigger checks that you may start those runs; if not,
403 policy_denied. - Test runs never fire event triggers. Remove a trigger with
trigger.removeand publish.
Secrets
Secrets hold the values agents must not carry in their definition: webhook signing keys, HTTP action credentials and SIP passwords (kinds webhook_signing, http_auth, sip_auth). Values are write-only: reads return names and versions, never values. Each PUT adds a version; drafts reference a secret as { id, version }. Writing and deleting secrets takes an admin key. See Phone for trunk credentials.
| Operation | MCP tool | Key role | Purpose |
|---|---|---|---|
GET /api/v1/secrets | secrets_list | viewer | List secret metadata (never values) |
PUT /api/v1/secrets/{secret_id} | secrets_put | admin | Create a secret or add a new version |
DELETE /api/v1/secrets/{secret_id} | secrets_delete | admin | Delete a secret and all its versions |