Skip to main content

Overview

Echo’s webhook system lets you register multiple HTTPS endpoints per project. Each endpoint subscribes to specific events, carries its own signing secret, and benefits from automatic retry with a full delivery audit log.

Events

Subscribe any endpoint to one or more of these events:
EventWhen it firesWhat’s in data
call.startedA call beginsstatus: "in_progress"
call.endedA call terminates (any reason)status, ended_reason
call.summaryPost-call analysis is readytranscript, analysis, duration
An endpoint with no matching subscription for a fired event is silently skipped.

Creating a Webhook Endpoint

curl -X POST https://api.meetecho.ai/api/v1/projects/{projectId}/webhooks \
  -H "x-api-key: echo_..." \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://your-server.com/webhooks/echo",
    "events": ["call.started", "call.ended", "call.summary"]
  }'
Response
{
  "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "project_id": "e56b4c35-882d-4604-9944-9763d314ea59",
  "url": "https://your-server.com/webhooks/echo",
  "signing_secret": "whsec_a1b2c3d4e5f6789012345678901234567890123456789012345678901234abcd",
  "status": "active",
  "events": ["call.started", "call.ended", "call.summary"],
  "consecutive_failures": 0,
  "created_at": "2026-04-12T15:51:59.499Z",
  "updated_at": "2026-04-12T15:51:59.499Z"
}
Save the signing_secret — use it to verify the X-Echo-Signature header on every incoming request. It is always readable by fetching the endpoint, but treat it like a password.
During development, use a tunnel tool like ngrok or Cloudflare Tunnel to expose your local server.

Event Payloads

Every delivery sends a JSON body with this envelope:
FieldTypeDescription
event_idUUIDUnique per event occurrence. Use this to deduplicate across retries.
event_namestringOne of call.started, call.ended, call.summary
customer_idUUIDThe customer involved in the call
notification_idUUIDThe notification (scheduled call) that triggered the event
dataobjectEvent-specific payload — shape varies by event type
Fired when a call begins.
{
  "data": {
    "status": "in_progress"
  },
  "event_id": "0785025f-bd96-46df-ad11-7021b8a7a4eb",
  "event_name": "call.started",
  "customer_id": "8cc6199f-9466-46f2-9db7-936f3c2c1c0b",
  "notification_id": "b612d95c-0941-4e4a-8a1e-b8da7baf82a1"
}

data fields

FieldTypeDescription
statusstringAlways "in_progress"

Request Headers

Every delivery includes these headers:
HeaderDescription
X-Echo-Signaturesha256=<hmac-sha256-hex> — verify before processing
X-Echo-EventThe event name, e.g. call.summary
X-Echo-DeliveryThe event_id for this delivery
X-Echo-TimestampUnix timestamp (seconds) when the delivery was sent
Use X-Echo-Timestamp to reject replayed requests older than a few minutes.

Verifying Signatures

Echo signs the raw request body with HMAC-SHA256 using your endpoint’s signing_secret. Always verify the signature before processing a webhook. This prevents spoofed requests from triggering actions in your system.
const crypto = require('crypto');

app.post('/webhooks/echo', express.raw({ type: 'application/json' }), (req, res) => {
const sig = crypto
.createHmac('sha256', process.env.ECHO_SIGNING_SECRET)
.update(req.body) // raw Buffer — not parsed JSON
.digest('hex');

if (`sha256=${sig}` !== req.headers['x-echo-signature']) {
return res.status(401).send('Invalid signature');
}

const payload = JSON.parse(req.body);
const { event_name, data } = payload;

if (event_name === 'call.summary') {
console.log(data.analysis);
}

res.sendStatus(200);
});

Use the raw request body for signature verification — before any JSON parsing. Re-serializing parsed JSON can alter whitespace and break the HMAC match.

Retry & Failure Alerting

Echo automatically retries failed deliveries up to 4 attempts total with exponential backoff:
AttemptTiming
1Immediate
21 minute after attempt 1 fails
35 minutes after attempt 2 fails
430 minutes after attempt 3 fails
A delivery is considered failed if your server returns a non-2xx response, times out (5 s per attempt), or is unreachable. After all 4 attempts fail, the delivery is marked failed and no further automatic retries are scheduled. After 4 consecutive delivery failures on the same endpoint, Echo sends one email alert to all business members.
Use event_id as your idempotency key — it is identical across all retry attempts for the same event occurrence.

Delivery Logs

Every HTTP attempt is logged. Use the delivery log API to diagnose failures and trigger manual retries.
# All deliveries across the project (paginated)
GET /v1/projects/{projectId}/webhooks/deliveries

# Deliveries for a specific endpoint
GET /v1/projects/{projectId}/webhooks/{webhookId}/deliveries

# Single delivery with all per-attempt details
GET /v1/projects/{projectId}/webhooks/{webhookId}/deliveries/{deliveryId}

# Manually retry a failed delivery
POST /v1/projects/{projectId}/webhooks/{webhookId}/deliveries/{deliveryId}/retry
Filter delivery lists by status (pending, retrying, success, failed) and event_name. Paginate with limit + cursor.

Managing Endpoints

# Update URL, events, or enable/disable
PATCH /v1/projects/{projectId}/webhooks/{webhookId}
-d '{ "status": "disabled" }'

# Rotate the signing secret
POST /v1/projects/{projectId}/webhooks/{webhookId}/secret/rotate

# Delete an endpoint (cascades all delivery history)
DELETE /v1/projects/{projectId}/webhooks/{webhookId}

Safe secret rotation

1

Rotate the secret

Call POST /v1/projects/{projectId}/webhooks/{webhookId}/secret/rotate and copy the returned signing_secret.
2

Deploy the new secret

Update ECHO_SIGNING_SECRET in your environment and redeploy before proceeding.
3

Verify delivery

Trigger a test notification and confirm your server accepts the new signature.
The new secret takes effect immediately. Update your server before rotating — there is no grace period.