Skip to content

fix(vapi): subscribe to end-of-call-report so SMS dispatch can fire#28

Merged
ByteStreams-AI merged 1 commit intomainfrom
fix/m11-vapi-server-messages
May 5, 2026
Merged

fix(vapi): subscribe to end-of-call-report so SMS dispatch can fire#28
ByteStreams-AI merged 1 commit intomainfrom
fix/m11-vapi-server-messages

Conversation

@ByteStreams-AI
Copy link
Copy Markdown
Owner

@ByteStreams-AI ByteStreams-AI commented May 5, 2026

Summary

Live SQL diagnosis surfaced the actual root cause behind every "didn't get the SMS" report in this session: voice_calls rows from real calls had ended_at=NULL and ended_reason=NULL. vapi_call_end never fired on any call. The entire post-call SMS dispatch path was dead code from the moment we moved to dynamic assistant config.

Root cause: Vapi assistants only deliver webhook events for message types declared in serverMessages. Static dashboard-defined assistants come with a default checklist; dynamic assistants returned via assistant-request inherit only what the response declares. Our response set endCallFunctionEnabled, voice, transcriber, model, tools — but never serverMessages. Vapi happily delivered tool-calls (because tools have explicit server.url configs) but end-of-call-report was silently filtered out.

That explains every previous failure mode in this branch:

Fix: declare the subscription list explicitly:

serverMessages: ['tool-calls', 'end-of-call-report', 'status-update', 'hang']

Integration test updated to assert serverMessages includes end-of-call-report and tool-calls, plus endCallFunctionEnabled === true.

Test plan

  • pnpm ci:fast — 299/299
  • After deploy: place a test call, complete the order flow, then verify with:
    select vapi_call_id, ended_at, ended_reason, order_id
    from voice_calls
    order by started_at desc limit 1;
    ended_at should be set, ended_reason populated.
  • Confirm the SMS payment link arrives.
  • Confirm sms_messages row exists with twilio_status='sent'.

🤖 Generated with Claude Code

Greptile Summary

Fixes a silent post-call SMS dispatch failure caused by Vapi not delivering end-of-call-report webhooks to dynamically-configured assistants that never declared serverMessages. The fix adds an explicit serverMessages subscription list (tool-calls, end-of-call-report, status-update, hang) to the handleAssistantRequest response; all four types are already handled in the existing routing switch. The integration test is updated to assert the two critical entries and endCallFunctionEnabled.

Confidence Score: 5/5

Safe to merge — targeted one-field addition with a matching regression test and no side-effects on existing handlers.

The change is minimal and directly corrects a well-diagnosed root cause. The four serverMessages values exactly match the message types already handled in the routing switch, so no dead subscriptions are introduced. Test coverage for the critical field is now in place.

No files require special attention.

Important Files Changed

Filename Overview
supabase/functions/vapi_call_start/index.ts Adds serverMessages subscription list to the dynamic assistant response so Vapi delivers end-of-call-report webhooks; all four declared message types are already handled in the routing switch.
packages/shared/test/db/voice.test.ts Extends the assistant-request response type and adds assertions for serverMessages containing both end-of-call-report and tool-calls, and for endCallFunctionEnabled === true.

Reviews (1): Last reviewed commit: "fix(vapi): subscribe to end-of-call-repo..." | Re-trigger Greptile

Live SQL pull from a fresh test call showed every voice_calls row
with ended_at=NULL and ended_reason=NULL. vapi_call_end never fired
on any call, which means the post-call SMS dispatch (and call-row
patching) was dead code from the moment we moved to dynamic
assistant config.

Root cause: Vapi assistants only deliver webhook events to the
serverUrl for message types listed in `serverMessages`. Static
dashboard-defined assistants typically have a default checklist;
dynamic assistants returned via assistant-request inherit only what
the response declares. Our response set `endCallFunctionEnabled`,
voice, transcriber, model, tools — but never `serverMessages`. So
Vapi delivered tool-calls (because tools have explicit server.url
configs) but nothing else.

Fix: declare the full subscription explicitly:
  serverMessages: ['tool-calls', 'end-of-call-report',
                   'status-update', 'hang']

Once deployed, end-of-call-report flows to the dispatcher →
handleEndOfCallReport → vapi_call_end → SMS dispatch lights up.

Integration test updated to assert that end-of-call-report and
tool-calls are subscribed and endCallFunctionEnabled is true.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@ByteStreams-AI ByteStreams-AI merged commit 1a53ed1 into main May 5, 2026
2 checks passed
@ByteStreams-AI ByteStreams-AI deleted the fix/m11-vapi-server-messages branch May 5, 2026 01:18
ByteStreams-AI added a commit that referenced this pull request May 5, 2026
…ort (#29)

PR #28 added serverMessages: ['tool-calls', 'end-of-call-report',
'status-update', 'hang'] which caused Vapi to reject the assistant
config entirely with "Couldn't get assistant" on the next call.
Vapi's enum validator rejected at least one of those values at
runtime even though they're documented; the cheapest path is to
ship only the value we actually need.

Reduced to ['end-of-call-report']. tool-calls is delivered via the
per-tool server.url config (not the assistant-level serverMessages
field), and status-update / hang are not load-bearing for our
pipeline. Re-add individually if a future need arises and verify
each value is accepted before bundling more.

Test assertion in voice.test.ts updated to reflect the minimal set
+ note the May 5 2026 regression in a comment.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
ByteStreams-AI added a commit that referenced this pull request May 5, 2026
Two new docs to anchor the May 5, 2026 governance reset:

- docs/project-status2.md: operational + governance snapshot.
  Captures why we're resetting (PR #28#30 regression chain),
  what's working in cloud, what's broken (#32 voice path), the
  new governance rules, and the step-by-step path to stable
  production via #33 (staging) → #31 (prod branch) → #34
  (umbrella checklist).

- docs/git-policy.md: authoritative branching, commit, PR,
  merge, hotfix, migration, and rollback policy. Codifies the
  four governance rules: prod branch as deploy gate, user-only
  staging/commits, Issues-before-PRs, and every PR must
  reference an Issue.

docs/project-status.md (milestone tracker) and AGENTS.md (repo
conventions and follow-ups) stay as-is; these are additive.
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.

1 participant