From 9a134a9fc6b5d9a6e6e8f3224e485bccccd1844a Mon Sep 17 00:00:00 2001 From: Madhu Ramasubramanian Date: Sat, 9 May 2026 15:27:26 -0400 Subject: [PATCH] VAPI-3161: Add BXML verb Adds support for the SIP REFER BXML verb to the python-sdk, mirroring the pattern but with the SIP-protocol-mandated success semantics: a successful REFER terminates the call on Bandwidth's side. Scope: BXML verb + unit tests only. The referCompleteCallback model will be regenerated from the OpenAPI spec once api-specs#2142 merges. Co-Authored-By: Claude Opus 4.7 (1M context) --- bandwidth/models/bxml/verbs/__init__.py | 1 + bandwidth/models/bxml/verbs/refer.py | 58 +++++++++++++++++++++++++ test/unit/models/bxml/test_refer.py | 38 ++++++++++++++++ 3 files changed, 97 insertions(+) create mode 100644 bandwidth/models/bxml/verbs/refer.py create mode 100644 test/unit/models/bxml/test_refer.py diff --git a/bandwidth/models/bxml/verbs/__init__.py b/bandwidth/models/bxml/verbs/__init__.py index 57902f63..63bf867d 100644 --- a/bandwidth/models/bxml/verbs/__init__.py +++ b/bandwidth/models/bxml/verbs/__init__.py @@ -11,6 +11,7 @@ from .phone_number import PhoneNumber from .play_audio import PlayAudio from .record import Record +from .refer import Refer from .redirect import Redirect from .resume_recording import ResumeRecording from .ring import Ring diff --git a/bandwidth/models/bxml/verbs/refer.py b/bandwidth/models/bxml/verbs/refer.py new file mode 100644 index 00000000..2b602320 --- /dev/null +++ b/bandwidth/models/bxml/verbs/refer.py @@ -0,0 +1,58 @@ +""" +refer.py + +Bandwidth's Refer BXML verb + +@copyright Bandwidth INC +""" +from ..nestable_verb import NestableVerb +from .sip_uri import SipUri + + +class Refer(NestableVerb): + + def __init__( + self, sip_uri: SipUri, + refer_complete_url: str=None, refer_complete_method: str=None, + tag: str=None + ): + """Initialize a verb + + The verb sends a SIP REFER to the remote endpoint, asking it + to redirect the call to a new SIP URI. Unlike , a successful + REFER terminates the call on Bandwidth's side: the remote endpoint + redirects away from Bandwidth entirely. This is a SIP protocol + property, not a Bandwidth design choice. As a result, BXML returned in + response to the referComplete callback is only meaningful for failure + handling — there is no live call to act on after success. + + Args: + sip_uri (SipUri): The SIP URI to refer the call to. Required. + Exactly one child element is allowed. + refer_complete_url (str, optional): URL to send the Refer Complete + event to when the REFER flow finishes (success or failure). + May be a relative URL. Defaults to None. + refer_complete_method (str, optional): The HTTP method to use for + the request to referCompleteUrl. GET or POST. Default value + is POST. Defaults to None. + tag (str, optional): A custom string that will be sent with this + and all future callbacks unless overwritten by a future tag + attribute or cleared. May be cleared by setting tag="". Max + length 256 characters. Defaults to None. + """ + self.sip_uri = sip_uri + self.refer_complete_url = refer_complete_url + self.refer_complete_method = refer_complete_method + self.tag = tag + super().__init__( + tag="Refer", + nested_verbs=[self.sip_uri] + ) + + @property + def _attributes(self): + return { + "referCompleteUrl": self.refer_complete_url, + "referCompleteMethod": self.refer_complete_method, + "tag": self.tag + } diff --git a/test/unit/models/bxml/test_refer.py b/test/unit/models/bxml/test_refer.py new file mode 100644 index 00000000..938b3b4f --- /dev/null +++ b/test/unit/models/bxml/test_refer.py @@ -0,0 +1,38 @@ +""" +test_refer.py + +Unit tests for the BXML verb + +@copyright Bandwidth Inc. +""" +import unittest + +from bandwidth.models.bxml import Refer, SipUri, Verb, NestableVerb + + +class TestRefer(unittest.TestCase): + + def setUp(self): + self.sip_uri = SipUri( + uri="sip:alice@atlanta.example.com" + ) + self.refer = Refer( + sip_uri=self.sip_uri, + refer_complete_url="https://example.com/handleRefer", + refer_complete_method="POST", + tag="test" + ) + + def test_instance(self): + assert isinstance(self.refer, Refer) + assert isinstance(self.refer, NestableVerb) + assert isinstance(self.refer, Verb) + + def test_to_bxml(self): + expected = 'sip:alice@atlanta.example.com' + assert expected == self.refer.to_bxml() + + def test_minimal(self): + minimal_refer = Refer(sip_uri=SipUri(uri="sip:bob@example.com")) + expected = 'sip:bob@example.com' + assert expected == minimal_refer.to_bxml()