Webhooks: Call Event Notifications

Updated 27 Sep 2026 ยท 8 min read

A webhook tells your own software about calls as they happen. When a call arrives on one of your numbers, or ends, Siptalk sends an HTTP POST to a URL you choose, carrying the details of the call. Typical uses are popping the caller's record on screen in your CRM, logging calls in a helpdesk, or triggering an SMS follow-up after a missed call.

What webhooks do

There are two event types. A webhook is one or the other; the type is chosen when it's created and can't be changed afterwards.

  • Call Received โ€” fires the moment a call reaches the number, before anything answers. Use it for screen pops and anything that needs to know who is calling now.
  • Call Completed โ€” fires when the call ends, with the outcome: answered, missed or voicemail, which extension answered, the start, answer and hang-up times, and the duration. Use it for call logging and missed-call follow-ups.

Webhooks are created once for your organisation under Settings โ†’ Webhooks, then assigned to as many numbers as you like. Each number can have one Call Received hook and one Call Completed hook.

โ„น Your endpoint must be reachable from the internet and should use HTTPS. Siptalk doesn't retry a failed delivery, so make your endpoint respond quickly (return a 200 and do any slow work afterwards).

Create a webhook

Go to Settings โ†’ Webhooks. In the Add Webhook card on the right, choose the type โ€” Call Received or Call Completed โ€” and click [Create Webhook].

Settings โ†’ Webhooks โ†’ Add Webhook
Add Webhook card with the type selector and Create Webhook button

The new webhook appears on the left. Click the pencil next to its name to give it a name you'll recognise on the number page โ€” "CRM screen pop", say.

Configure it

A Call Received webhook's settings: endpoint, format, auth key and HTTP auth fields
  • Endpoint โ€” the full URL Siptalk should POST to. HTTPS is strongly recommended.
  • Format โ€” how the payload is sent: Form URL-encoded (like an HTML form submission; in PHP it arrives in $_POST) or JSON (a JSON object in the request body). Pick whichever your application handles more easily.
  • Auth Key โ€” optional. Any string you choose; it's sent with every event as the key field, so your endpoint can check the request really came from Siptalk. Use a long random value.
  • HTTP Auth User / Pass โ€” optional HTTP Basic authentication, if your endpoint sits behind it.

Click [Update]. Changes apply to every number the webhook is assigned to.

The Webhook Types card describing Call Received, Call Completed and the payload notes

Assign it to a number

Go to Services โ†’ Phone Numbers, click [Manage] on the number, and find the Webhooks card in the right-hand column. Pick your webhook in the Call Received or Call Completed list; only webhooks of the matching type are offered. The change saves as soon as you pick it.

Services โ†’ Phone Numbers โ†’ Manage โ†’ Webhooks
The Webhooks card on a number's page with a Call Received and a Call Completed webhook selected

Set either list back to None to stop events for that number.

Send a test

On Settings โ†’ Webhooks, click [Test Endpoint] on the webhook. Siptalk sends a sample event with the same fields as a real one โ€” the eventid starts with test- so your code can tell โ€” and shows whatever your endpoint sent back.

Test Endpoint dialog showing the endpoint address and a response box

An empty response with no error means your endpoint accepted the request. If you see a connection error or an HTTP error code, check the endpoint URL and any firewall in front of it.

Payload reference

Every event is an HTTP POST with an X-Siptalk-Event header naming the event type, and the fields below in the webhook's chosen format. Values are strings unless noted.

Fields sent on every event

FieldMeaning
eventcall-received or call-completed
keyYour Auth Key, or an empty string if none is set
eventidUnique id for this event; safe to use for de-duplication
numberThe Siptalk number that was called, digits only (e.g. 0355501987)
calleridThe caller's number, digits only, or the literal string private when withheld

Additional fields on Call Completed

FieldMeaning
statusanswered, missed or voicemail
answered_byThe extension that answered, if any
start, answered, hangupDate-times as YYYY-MM-DD HH:MM:SS
durationCall length in seconds (a number in JSON)
vPayload version, currently 2 (a number in JSON). Expect new fields to be added over time; ignore ones you don't know.

Examples

A Call Received event, form URL-encoded:

POST /siptalk/call-received HTTP/1.1
Host: crm.acmeplumbing.example
Content-Type: application/x-www-form-urlencoded
X-Siptalk-Event: call-received

event=call-received&key=a9f3e2c7d1&eventid=6f1c2a9e3b7d&number=0355501987&callerid=0491570156

A Call Completed event, JSON:

POST /siptalk/call-completed HTTP/1.1
Host: crm.acmeplumbing.example
Content-Type: application/json
X-Siptalk-Event: call-completed

{
  "event": "call-completed",
  "key": "a9f3e2c7d1",
  "eventid": "6f1c2a9e3b7d",
  "number": "0355501987",
  "callerid": "0491570156",
  "status": "answered",
  "answered_by": "104827001",
  "start": "2026-09-27 10:14:02",
  "answered": "2026-09-27 10:14:09",
  "hangup": "2026-09-27 10:17:41",
  "duration": 212,
  "v": 2
}

Migrating from the legacy platform

If your endpoint was built for the legacy Siptalk platform, it will be reading fields that the new platform names differently. Recreate the webhook here as a Call Received hook (that's the event the legacy platform sent), point it at the same endpoint, and update your code as follows:

Legacy field New field Notes
service_number number The Siptalk number that was called. Same value, same format.
caller_id callerid The caller's number. The new platform sends the literal string private for a withheld number; check for it before parsing.
hook_time not sent The Call Received event carries no timestamp. Record the time your endpoint received the request; a call is delivered to your endpoint within moments of arriving. If you need the call's start and end times, use a Call Completed webhook, which sends start, answered and hangup as date-time strings rather than Unix seconds.
โ€” event, eventid, key New. event names the event type, eventid is unique per event (use it to ignore duplicates), and key is the Auth Key you set on the webhook โ€” compare it to reject requests that aren't from Siptalk.

In PHP, the smallest change is to read the new names into the variables your code already uses:

$hook_time      = time();                    // legacy hook_time - not sent, use receipt time
$caller_id      = $_POST['callerid'];        // legacy caller_id
$service_number = $_POST['number'];          // legacy service_number

if ($_POST['key'] !== 'your-auth-key') {     // new: reject anything not from Siptalk
    http_response_code(403);
    exit;
}
โ„น The legacy platform only sent form-encoded data. If you keep the webhook's Format as Form URL-encoded, $_POST keeps working as above. If you switch to JSON, read the body with json_decode(file_get_contents('php://input'), true) instead.

Troubleshooting

The test fails with a connection error

  • The endpoint must be reachable from the public internet. A URL on your office network or localhost won't work.
  • Check the URL is complete, including https:// and any path.
  • If your firewall restricts inbound connections, allow Siptalk's request. The Test Endpoint dialog shows the address the test comes from.

The test works but real calls don't arrive

  • Check the webhook is assigned on the number's page โ€” creating it isn't enough.
  • Check the number is the one being called. Webhooks are per number, not per organisation.

My endpoint gets the request but the fields are empty

The format doesn't match what your code expects. Form URL-encoded data arrives as form fields ($_POST in PHP); JSON arrives in the request body and must be decoded. Check the webhook's Format setting against your code.

Verifying requests are from Siptalk

Set an Auth Key and have your endpoint reject any request whose key doesn't match. For stronger protection, add HTTP Basic auth as well, and only accept HTTPS.

Was this article helpful?