Recipes
Receive webhooks
Written beside the API’s code, and read from it every hour.
Have events pushed to your server instead of polling for them.
1Create an endpoint with the events you want. The answer holds secret_key. It is shown this once, so store it with your other secrets.
curl -X POST https://api.vocatech.com/v1/webhooks \
-H "Authorization: Bearer $VOCATECH_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "CRM sync",
"url": "https://YOUR_DOMAIN/webhooks/vocatech",
"event_filters": ["call.ended", "call.transcription", "message.received"]
}'import { vt } from './vocatech.mjs';
const hook = await vt('POST', '/v1/webhooks', {
name: 'CRM sync',
url: 'https://YOUR_DOMAIN/webhooks/vocatech',
event_filters: ['call.ended', 'call.transcription', 'message.received'],
});
// hook.secret_key is shown this once. Put it in your secret store now.
console.log('webhook', hook.id, 'created');from vocatech import vt
hook = vt("POST", "/v1/webhooks", {
"name": "CRM sync",
"url": "https://YOUR_DOMAIN/webhooks/vocatech",
"event_filters": ["call.ended", "call.transcription", "message.received"],
})
# hook["secret_key"] is shown this once. Put it in your secret store now.
print("webhook", hook["id"], "created")<?php
require __DIR__ . '/vocatech.php';
$hook = vt('POST', '/v1/webhooks', [
'name' => 'CRM sync',
'url' => 'https://YOUR_DOMAIN/webhooks/vocatech',
'event_filters' => ['call.ended', 'call.transcription', 'message.received'],
]);
// $hook['secret_key'] is shown this once. Put it in your secret store now.
echo 'webhook ', $hook['id'], " created\n";{
"id": 42,
"company_id": 1234567890,
"name": "CRM sync",
"description": null,
"url": "https://YOUR_DOMAIN/webhooks/vocatech",
"event_filters": ["call.ended", "call.transcription", "message.received"],
"extension_filters": [],
"number_filters": [],
"enabled": true,
"created_at": "2026-09-24 13:40:00",
"updated_at": "2026-09-24 13:40:00",
"secret_key": "4f1c2e6a9b0d3f7e8a5c1b2d6e9f0a3c4b7d8e1f2a5c6b9d0e3f4a7b8c1d2e5f"
}2Verify, answer, then work. Check the X-Vocatech-Signature header, answer 2xx within 5 seconds, and do the work after you answer. The receivers in Capture the raw body do all three.
3Dedupe. A retry repeats the same event id, so skip an id you already have. For message events, data.message_id names the message itself.
4Test it. Send a signed test event to the endpoint. The answer says what your server replied.
curl -X POST https://api.vocatech.com/v1/webhooks/42/test \
-H "Authorization: Bearer $VOCATECH_API_KEY"import { vt } from './vocatech.mjs';
const result = await vt('POST', '/v1/webhooks/42/test');
console.log(result.success, result.status_code, result.error);from vocatech import vt
result = vt("POST", "/v1/webhooks/42/test")
print(result["success"], result["status_code"], result["error"])<?php
require __DIR__ . '/vocatech.php';
$result = vt('POST', '/v1/webhooks/42/test');
echo $result['success'] ? 'delivered' : 'failed: ' . $result['error'], PHP_EOL;{
"success": true,
"status_code": 200,
"error": null
}You can also manage webhooks in the portal, in company settings under Webhooks. Every event and payload is in Webhooks in depth.
