Contactivity
Getting started.md

Events, webhooks and debugging

Contactivity tells your server what happens on calls through webhooks, keeps a 7-day event log, and records every request it sends you and every error it hits. Paths are relative to https://api.contactivity.io/v1/projects/{project_id}; see the overview.

Method Path Scope Purpose
GET /events events:read Event log (paged)
GET /webhook-endpoint events:read The project's default webhook endpoint
PUT /webhook-endpoint events:write Set or turn off the endpoint
GET /webhook-public-key any key The Ed25519 key that signs webhooks (trunks-routes.md)
POST /webhook-public-key/rotate events:write New signing key; the old one keeps signing for 24 h
GET /webhook-deliveries events:read Every request sent to your URLs (paged)
GET /errors events:read Error debugger (paged)
GET /alert-rules events:read List alert rules (paged)
POST /alert-rules events:write Create an alert rule
PATCH /alert-rules/{rule_id} events:write Update an alert rule
DELETE /alert-rules/{rule_id} events:write Archive an alert rule

Native events

Voice apps with param_style: "contactivity" (the default for apps created through this API, see voice-apps.md) send call-control events in the native envelope, and recording and transcription events use it too:

json
{
  "id": "evt_5f0c…",
  "type": "call.answered",
  "created_at": "2026-10-22T14:03:11.123Z",
  "project_id": "6a1f…",
  "data": {"call_id": "CA…", "parent_call_id": null, "app_id": "app_us1_…", "client_state": "b3JkZXItNDI=",
           "from": "+12125550123", "to": "+14155550100", "direction": "outbound"}
}

data always has call_id, parent_call_id, app_id and client_state; from, to, direction and custom_parameters when known; plus fields of the event (digits, recording ids, ...).

Event types:

Group Types
Call lifecycle call.initiated, call.answered, call.bridged, call.completed, call.error
Input call.dtmf.received, call.gather.ended, call.ai_gather.ended
Media call.playback.started, call.playback.ended, call.speak.started, call.speak.ended
Queues and transfer call.enqueued, call.dequeued, call.refer.started, call.refer.completed, call.refer.failed
Machine detection call.machine_detection.ended, call.machine_greeting.ended
Streams and live transcription call.stream.started, call.stream.stopped, call.stream.failed, call.transcription.received
AI agents call.ai_agent.started, call.ai_agent.ended
Recordings and transcriptions call.recording.completed, call.transcription.completed, call.transcription.failed
Conferences conference.created, conference.ended, conference.participant.joined, conference.participant.left, conference.playback.started, conference.playback.ended, conference.speak.started, conference.speak.ended, conference.recording.completed

New types may be added; ignore types you do not handle. Apps with param_style: "twilio" keep the Twilio/Telnyx-compatible formats (compatibility.md).

Where events go

To the voice app's webhook_url (or the call's webhook_url). Events without a URL of their own go to the project's webhook endpoint, when one is set:

sh
curl -X PUT https://api.contactivity.io/v1/projects/$PROJECT_ID/webhook-endpoint \
  -H "Authorization: Bearer $CONTACTIVITY_API_KEY" -H "Content-Type: application/json" \
  -d '{"url": "https://example.com/contactivity-events", "event_types": ["call.completed", "call.recording.completed"]}'

url must be a public https:// URL; null or "" turns the endpoint off. event_types empty or missing means all types. Both GET and PUT return {url, event_types, updated_at}. Errors: 400 invalid_parameter (param url, event_types), 400 unknown_parameter.

Verifying signatures

Every webhook, CoXML fetch and AI tool call is signed with Standard Webhooks headers: webhook-id (the event id for native events), webhook-timestamp (Unix seconds) and webhook-signature (v1a,<base64 Ed25519 signature> over {webhook-id}.{webhook-timestamp}.{raw body}). Get the public key from GET /webhook-public-key, reject timestamps more than 5 minutes old, and verify over the raw body. For 24 h after a key rotation the header carries two space-separated entries (new key first); a signature is valid when any entry verifies, which the SDK helpers do:

ts
import { verifyStandardWebhook } from "@contactivity/sdk";
if (!verifyStandardWebhook(publicKey, req.headers, rawBody)) return res.sendStatus(401);
python
from contactivity_sdk import verify_standard_webhook
if not verify_standard_webhook(public_key, request.headers, raw_body):
    abort(401)

Answer with a 2xx within a few seconds; failed deliveries are retried and show up in /webhook-deliveries.

Event log

GET /events lists the stored events, newest first. The stored copy keeps metadata only (ids, states, causes, times, durations, numbers and media facts); everything else, including transcripts, speech and gather results, digits, AI message history, summaries, client_state and custom parameters, is dropped and the event is marked content_redacted: true. Webhook deliveries carry the full data. Stored events are never deleted: archiving a call, recording or transcription keeps them. Filters: type (one type, or a prefix such as call.*), call_id, created_after, created_before (RFC 3339 or YYYY-MM-DD), limit, starting_after. Each item is the envelope above. GET /event-types lists every event type (any API key of the project).

sh
curl "https://api.contactivity.io/v1/projects/$PROJECT_ID/events?type=call.*&call_id=$CALL_ID" \
  -H "Authorization: Bearer $CONTACTIVITY_API_KEY"

Webhook deliveries

GET /webhook-deliveries lists every request Contactivity sent to your URLs, newest first. Filters: status (succeeded = 2xx, failed = other status, connection error or timeout), kind (webhook, coxml, alert), url (exact), call_id, limit, starting_after.

Field Type Description
id string
kind string webhook, coxml or alert
event_id string
call_id string or null
attempt integer
method, url string
status_code integer or null Null on a connection error or timeout
error string or null
duration_ms integer or null
replay_of string or null The delivery this one replayed
created_at timestamp

Error debugger

GET /errors lists errors raised while running calls and apps (bad CoXML, failed webhooks, AI agent errors), newest first. Filters: category (coxml, webhook, call, ai_agent, app), call_id, limit, starting_after. Each item: id, created_at, category, code (integer), message, url, call_id.

Alert rules

An alert rule emails people and/or calls a webhook when errors (or an AI agent metric) cross a threshold within a time window, then waits out a cooldown. A project has at most 20 rules.

Method Path Scope Purpose
GET /alert-rules events:read List rules (paged, newest first)
POST /alert-rules events:write Create a rule
PATCH /alert-rules/{rule_id} events:write Update a rule (fields left out keep their values)
DELETE /alert-rules/{rule_id} events:write Archive a rule (it stops firing)
Field Type Rules
name string Required. 1-100 characters, trimmed
category string Required: any, coxml, webhook, call, ai_agent or app (the /errors categories)
codes integer[] Up to 50 error codes (1-99999); empty counts every code of the category
metric string errors (default), ai_success_rate, ai_latency_p95 or ai_negative_pct
threshold integer Required. errors: 1-100000 errors in the window. ai_latency_p95: 50-60000 ms. ai_success_rate, ai_negative_pct: a percentage, 1-100
window_minutes integer 1-1440, default 5
cooldown_minutes integer 1-10080, default 60
emails string[] Up to 5 addresses
webhook_url string or null A public https:// URL; "" or null clears it
enabled boolean Default true
voice_app_id string or null AI metrics only: limit the rule to one AI agent. Ignored for errors

At least one of emails or webhook_url is required. The rule object has these fields plus id (alr_…), last_fired_at and created_at.

Errors: 400 invalid_parameter (param name, category, codes, threshold, metric, window_minutes, cooldown_minutes, emails, webhook_url, voice_app_id), 400 invalid_request (no emails and no webhook_url, or an AI metric threshold out of range), 400 unknown_parameter, 404 not_found, 409 conflict (already 20 rules).

sh
curl https://api.contactivity.io/v1/projects/$PROJECT_ID/alert-rules \
  -H "Authorization: Bearer $CONTACTIVITY_API_KEY" \
  -H "Content-Type: application/json" -H "Idempotency-Key: $(uuidgen)" \
  -d '{"name": "Webhook failures", "category": "webhook", "threshold": 10, "window_minutes": 5,
       "emails": ["oncall@example.com"]}'