Skip to content

VAPI-4041: send Twilio's action params from /bw/continue - #31

Open
mramasubramanian-bw wants to merge 2 commits into
mainfrom
VAPI-4041/continue-action-params
Open

mramasubramanian-bw wants to merge 2 commits into
mainfrom
VAPI-4041/continue-action-params

Conversation

@mramasubramanian-bw

Copy link
Copy Markdown
Contributor

Summary

/bw/continue now answers each verb's action with the params Twilio would send, so customer handlers branch the same way they did on Twilio.

  • Dial — transferComplete maps to DialCallStatus and DialBridged. DialCallSid and DialCallDuration come from the dialed leg's transferDisconnect. That event arrives at a new /bw/transfer-leg endpoint and is joined to the parent's transferComplete, waiting up to transferLegWaitMs (default 400 ms) because Bandwidth documents no ordering between the two. If the leg event never shows up, those two fields are left out rather than made up. Translated Dial targets now carry transferDisconnectUrl, with the callback creds, whenever the Dial has an action.
  • Record — recordComplete sends RecordingUrl, RecordingSid and RecordingDuration, and the recording can now be fetched through the Recordings facade. Bandwidth has no equivalent of Twilio's Digits, so it is left out.
  • Gather — when a Gather ends with no digits or speech, the action is not requested, matching Twilio. The server remembers each call's last TwiML document and re-translates the verbs after that Gather. Each Gather's position travels on its gatherUrl (&gather=N). actionOnEmptyResult="true" (&onEmpty=1) requests the action with Digits="" instead. Bandwidth's speech-timeout text counts as no input. A translator instance that hasn't seen the document (after a restart, or on another replica) falls back to requesting the action.

AGENTS.md and the verb matrix notes are updated to describe the new behavior.

Testing

  • New: test/server-continue.test.ts, test/translate-continue.test.ts
  • Updated: test/server-inbound.test.ts (gatherUrl now includes &gather=1)
  • npm run typecheck is clean; npm test gives 389 passed, 6 skipped

- Dial: map transferComplete to DialCallStatus/DialBridged, and join the
  dialed leg's transferDisconnect (new /bw/transfer-leg) for DialCallSid and
  DialCallDuration, waiting up to transferLegWaitMs (default 400 ms).
- Record: send RecordingUrl, RecordingSid, RecordingDuration from
  recordComplete and register the recording with the Recordings facade.
- Gather: on no digits or speech, resume the document after that Gather
  instead of requesting the action, unless actionOnEmptyResult="true".
@mramasubramanian-bw
mramasubramanian-bw requested review from a team as code owners October 1, 2026 17:50
@bwappsec

bwappsec commented Oct 1, 2026 •

Copy link
Copy Markdown

✅ Snyk checks have passed. No issues have been found so far.

Status Scan Engine Critical High Medium Low Total (0)
✅ Open Source Security 0 0 0 0 0 issues
✅ Licenses 0 0 0 0 0 issues
✅ Code Security 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

Review fixes for the Dial action join:
- A multi-number Dial reported whichever leg disconnected first, usually a
  cancelled loser. Now all of the Dial's legs are considered and the one
  that answered wins; if the parent bridged but the winner has not
  reported, no leg is reported rather than a loser.
- Leftover or late leg events were applied to the next Dial on the call.
  Each Dial now gets a key (<doc nonce>.<index>) on both its
  transferCompleteUrl and its legs' transferDisconnectUrl, so legs only
  join their own Dial. The wait ends early once every expected leg has
  reported, and a call's unread legs are dropped on disconnect.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants