Skip to main content

Webhooks

Send every finished call to your own endpoint. Set one up, choose what it carries, read the payload, and check deliveries.

What you will achieve

Each time one of your agents finishes a call, Phynxa will post the call's summary, transcript and details to a web address you control. You can also have it told when a call starts and each time a tool runs.

Prerequisites

  • A public http or https endpoint that accepts the request and answers with a 2xx status. Private and internal addresses are refused.
  • The owner or admin role.

Step-by-step setup

  1. Open Tools and the Webhooks tab, and choose New webhook.
  2. Enter a Name (for your reference, for example CRM sync) and tick the Agents that should send to it. An agent uses one webhook, so ticking an agent that has another moves it here.
  3. Choose to create it. The webhook's page opens.
  4. Under Where to send it, choose the Method (POST by default) and enter the URL, for example https://yoursite.com/hooks/calls.
  5. Set a Signing secret. It is optional but recommended: it lets you prove each request came from Phynxa. See Verify webhook signatures.
  6. Under What to send, tick the parts you want (see below). Under Limits set Timeout and Attempts.
  7. Choose Save changes.
  8. On each agent, switch on Webhook under Tools, Built-in, and choose it under Send to.

What each delivery carries

The event name and the call id are always sent. Tick any of these to add them:

PartContents
Call detailsAgent, direction, numbers, start and end time, duration, number of turns, latency and outcome.
SummaryThe review: summary, intent, tone, outcome label, score and review status.
TranscriptEvery line spoken, both sides, with role and time.
Action itemsWhat the AI decided still needs doing.
Captured detailsNames, numbers and emails picked out.

If nothing is ticked, everything is sent.

Event types

A webhook can receive three events. Tick the ones you want under Events; Call completed is ticked for a new webhook.

EventWhen it is sentWhat it carries
call.startedAs the call connectsThe Call details part: agent, direction, numbers and start time.
tool.calledEach time the agent runs one of your toolsThe call, and the tool: its name, whether it worked, and how long it took. Not what the tool was asked or what it returned.
call.completedOnce the call has been reviewedEverything described under What each delivery carries.

The What to send ticks apply to every event, but only to parts that event has: a call.started has call details and nothing to add a transcript to. A tool.called always carries its tool block.

How the live events behave:

  • In order. call.started, then any tool.called, then call.completed.
  • Never in the caller's way. They are sent in the background. A slow or unreachable endpoint cannot make the caller wait. If more than 50 events are waiting for one call, the newest are dropped. When the call ends, Phynxa waits a few seconds for what is queued, then sends call.completed.
  • Free when you do not use them. An agent whose webhook has not ticked them does no extra work during a call.
  • Attempts. Attempts applies, but a live event waits at most 5 seconds for an answer, whatever Timeout says.
  • Which calls. The webhook is read as the call starts, so ticking an event applies to calls that start after you save. A paused webhook sends nothing.

call.started:

json
{
  "event": "call.started",
  "call_id": "b130a71059eb44e3bfff982ecc035585",
  "call": {
    "id": "b130a71059eb44e3bfff982ecc035585",
    "agent_id": "230af209-7759-45af-839e-55cf5dcda8fd",
    "agent_name": "Reception Marina",
    "direction": "inbound",
    "from_number": "+971501234567",
    "to_number": "+97145551234",
    "started_at": "2026-10-12T09:14:03.120000+00:00"
  }
}

tool.called:

json
{
  "event": "tool.called",
  "call_id": "b130a71059eb44e3bfff982ecc035585",
  "call": {
    "id": "b130a71059eb44e3bfff982ecc035585",
    "agent_id": "230af209-7759-45af-839e-55cf5dcda8fd",
    "agent_name": "Reception Marina",
    "direction": "inbound"
  },
  "tool": { "id": "5d1f0c7a", "name": "check_order", "ok": true, "duration_ms": 420 }
}

Example payload

With every part ticked (values are an example):

json
{
  "event": "call.completed",
  "call_id": "b130a71059eb44e3bfff982ecc035585",
  "call": {
    "id": "b130a71059eb44e3bfff982ecc035585",
    "agent_id": "230af209-7759-45af-839e-55cf5dcda8fd",
    "agent_name": "Reception Marina",
    "direction": "inbound",
    "from_number": "+971501234567",
    "to_number": "+97145551234",
    "started_at": "2026-10-12T09:14:03.120000+00:00",
    "ended_at": "2026-10-12T09:15:41.880000+00:00",
    "seconds": 98.8,
    "turns": 9,
    "p50_latency_ms": 820,
    "p95_latency_ms": 1311,
    "outcome": "completed"
  },
  "review": {
    "summary": "Caller asked to book a cleaning on Thursday afternoon and was booked for 2:30 pm.",
    "intent": "book an appointment",
    "sentiment": "positive",
    "outcome": "appointment_booked",
    "score": 5,
    "status": "complete"
  },
  "transcript": [
    { "role": "assistant", "text": "Thank you for calling Acme Dental. How can I help?", "at": "2026-10-12T09:14:04+00:00" },
    { "role": "user", "text": "Hi, can I book a cleaning this Thursday?", "at": "2026-10-12T09:14:09+00:00" }
  ],
  "action_items": ["Send a reminder the day before"],
  "entities": { "name": "Sara K.", "email": "sara@example.com" }
}

Field notes:

  • direction is inbound, outbound or web (a browser test).
  • Transcript role is assistant (the agent) or user (the caller).
  • review.status is complete, pending (the review has not finished) or off (Call summary is switched off for the agent). The other keys keep their shape either way.
  • outcome inside review is one of resolved, needs_followup, information_given, appointment_booked, no_answer, abandoned or unclear.
  • With only Summary ticked you receive event, call_id and review, and nothing else.

Request details

Phynxa sends a request to your URL with these headers:

HeaderValue
Content-Typeapplication/json
X-Voice-Agent-Eventcall.completed, call.started or tool.called, whichever this is
X-Voice-Agent-TimestampUnix time in seconds (only when a secret is set)
X-Voice-Agent-Signature-V2HMAC-SHA256 hex of <timestamp>.<raw body> (only when a secret is set)
X-Voice-Agent-SignatureHMAC-SHA256 hex of the raw body (older form; only when a secret is set)

Retries and what counts as delivered

  • Delivered: your endpoint answers with a 2xx status.
  • Retried: timeouts, connection errors and 5xx answers, up to Attempts tries (1 to 5; new webhooks start at 3), with a short pause between.
  • Not retried: a 4xx answer. Phynxa assumes your endpoint rejected the request and will keep rejecting it.
  • Timeout: how long Phynxa waits for an answer, up to 60 seconds (new webhooks start at 10).
  • Redirects are not followed.

A webhook that is down never loses the call record: the call is stored in History either way.

Check deliveries

Under Recent deliveries (the last 50 attempts) each row shows the event, whether it succeeded, the status code, the time it took and any error. On an agent, the Logs tab lists the deliveries for the webhook it sends to.

Testing

On the webhook's page choose Send test. It posts a sample for each event you ticked (the end-of-call sample if you ticked none), carrying only the parts you ticked, signed exactly like a real one. It shows the result, and if a sample fails it names the event. Each sample is a row under Recent deliveries. Save first: the test uses the saved version.

Expected result

Your endpoint receives the request, answers 2xx, and the row under Recent deliveries reads as succeeded.

Troubleshooting

See Webhook delivery issues.

Something here does not match what you see in the dashboard? Let us know.