After the call ends
Runs once after the call is over, however it ended: the caller hung up, the flow hung up or finished, the call was transferred or answered by an agent, or…
What it does
Runs its chain once, after the call is over — however it ended: the caller hung up, a Hang up step ended it, the flow ran to its end, the call was transferred or answered by an agent, or the AI receptionist ended it.
Everything the call collected is still readable — {{vars.x}}, a step's
result {{steps.<id>.ok}} — plus two values the trigger adds:
{{vars.callEndReason}}—caller_hangup,flow_hangup,transferred,completedorerror.{{vars.callDurationSecs}}— seconds from the answer to the hang-up.
Nobody is on the line any more, so only steps that need no caller can go below it: Call an API, Set variable, Branch, Business hours, Look up contact, Run sub-flow, Custom script, Notify agent and No-op. Anything that plays, listens, records, dials or transfers is refused there.
One per flow. It is never the flow's start.
When to use it
- Log every call's outcome and length to your CRM or a webhook
- Send a "missed you" follow-up when
vars.callEndReasoniscaller_hangupbefore the caller reached anyone - Put whatever used to hang off a node's old "Caller hung up" path here — nodes on the call no longer have a hang-up path
Configuration
_label and _note are author-only metadata; the trigger has no runtime
configuration of its own.
Runs once after the call is over, however it ended: the caller hung up, the flow hung up or finished, the call was transferred or answered by an agent, or the AI receptionist ended it. Everything the call collected is still readable ({{vars.x}}, {{steps.<id>.ok}}), plus why and how long: {{vars.callEndReason}} and {{vars.callDurationSecs}}. Nobody is on the line any more, so only steps that need no caller can go below it: Call an API, Set variable, Branch, Business hours, Look up contact, Run sub-flow, Custom script, Notify agent and No-op. One per flow.
| Field | Label | Type | Required | Default | Notes |
|---|---|---|---|---|---|
| _label | Trigger label | text | Optional | — | Friendly name shown in the canvas and run logs. Optional. |
| _note | Internal note | textarea | Optional | — | Notes for your team. |
Outgoing events: triggered
Examples
Post the outcome to a webhook
{
"id": "after-call",
"type": "afterCall",
"config": { "_label": "Log the call" },
"on": { "triggered": "post-outcome" }
}
{
"id": "post-outcome",
"type": "apiCall",
"config": {
"mode": "http",
"method": "POST",
"url": "https://crm.example.com/calls",
"body": "{ \"from\": \"{{event.from}}\", \"reason\": \"{{vars.callEndReason}}\", \"seconds\": {{vars.callDurationSecs}} }"
}
}
[afterCall] ──► [apiCall POST crm.example.com/calls]
Gotchas
- One per flow. The designer disables it in the palette once the flow
has one; the validator reports a second as
after_call_duplicate. - Never the start. A flow whose
startis this trigger fails validation (after_call_is_start). - Only steps that need no caller below it. The designer offers only those
when you add a step from the chain and refuses a wire to any other ("Say
can't run after the call ends — nobody is on the line"); the validator
reports one as
after_call_node_not_allowedand the walker won't run it. - Nothing connects into it. It starts on its own when the call ends.
- Old "Caller hung up" edges. Flows saved before this trigger existed may
have
call.hangupedges on Say, Gather, Play music and similar nodes. They never run. On save the server turns one into the node's normal next path when that path is free; otherwise it stays, flagged by thecall_hangup_edgewarning. Move that logic under this trigger.
