ThunderPhone 2.0 is live.Self-serve, from 2¢/min.Read the announcement

Contact centers

Connect from Zoom Contact Center

Send Zoom Contact Center calls to a ThunderPhone agent with a third-party voicebot connector, and take them back to a human agent with a SIP REFER.

A Bot widget in your Zoom Contact Center flow sends the caller to the agent with Zoom's call sid. When the caller needs a person, the agent sends a SIP REFER to Zoom's human-agent address, and your flow fetches the hand-off by call sid.

Before you start

  • A ThunderPhone agent and an organization API key.
  • A Zoom Marketplace General App, managed by your admin and made available to Contact Center, with a connector for the Bot widget (Zoom's third-party voicebot guide).
  • A SIP trunk from Zoom to the bot provider. Zoom's ISV team builds it in stages (discovery, a staging trunk, then production), and the app's Company Name must match the trunk record exactly. This setup starts with ThunderPhone support and Zoom, not in your Zoom account alone.

1. Create the SIP endpoint

Zoom's connector dials a 15-digit number starting with 999, not a SIP address. Saving the connector generates that number. Zoom's trunk uses TLS 1.2 on port 5061 with SRTP for audio, and is allowlisted by Zoom's subnets rather than a password. So create an ip_allowlist endpoint with the connector's number as a dial-in number and SRTP required (see Platforms that dial a phone number):

curl -X POST https://api.thunderphone.com/v1/sip-endpoints \
  -H "Authorization: Bearer sk_live_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "agent_id": 12,
    "name": "Zoom Contact Center",
    "auth": "ip_allowlist",
    "allowed_source_cidrs": ["ZOOM_SIGNALLING_RANGE_1", "ZOOM_SIGNALLING_RANGE_2"],
    "dial_in_numbers": ["999123456789012"],
    "srtp": "required"
  }'
  • allowed_source_cidrs: Zoom's SIP signalling subnets, from Zoom. Each must be a public IPv4 range of /16 or narrower (IPv6 /48 or narrower).
  • dial_in_numbers: the connector's number exactly as Zoom sends it, without a +.
  • srtp: required, since Zoom's trunk always uses SRTP.

Zoom's addresses are shared by all its customers, so any other Zoom customer who dials the number reaches your agent. Zoom assigns the number; don't publish it.

2. Send calls with context

Add a Bot widget to your flow and bind it to the connector. Ask Zoom's ISV team to point the trunk at sip.thunderphone.com, TLS on port 5061, with the connector's number in both the request URI and the To header. The endpoint refuses UDP, so make sure the trunk doesn't fall back to it.

Zoom sends:

  • X-ZOOM-Call-Sid, stored as the call's external_call_id. Zoom's own data mapping page doesn't list it; another AI provider's Zoom integration documents it, so check it on your first test call.
  • X-ZOOM-DNIS, the number the caller dialed, as {{sip_x_zoom_dnis}}.
  • X-ZOOM-WEBHOOK, the connector's Static URL.

3. Get the call back

The agent sends a REFER to the human-agent address on Zoom's SBC. Add this transfer number (see Add the transfer number), with the SBC address Zoom gives you:

{"name": "Zoom human agent", "number": "sip:HumanAgent@203.0.113.10:5061"}

Zoom's guide describes two ways a bot passes context: a POST to the connector's Static URL before the REFER (voicebot_metadata and similar events), and SIP headers mapped under Inbound from Bot. ThunderPhone doesn't post to the Static URL. Zoom's guide doesn't say whether it reads mapped headers from a REFER; if you want to try, add headers such as "X-Handoff-Id": "{{handoff_id}}" to the entry and check on a test call. Also confirm with Zoom that it accepts a REFER with no metadata POST before it.

To get the hand-off without relying on either, have your flow look it up after the Bot widget, with an HTTP Call or Script widget:

  • GET https://api.thunderphone.com/v1/calls/handoff?external_call_id= followed by the call sid.
  • Header Authorization: Bearer sk_live_….
  • Use handoff.summary and handoff.intent to route the caller and brief the person who takes the call.

Zoom doesn't publish field-level docs for these widgets, so confirm in your account that the widget can send an Authorization header. See Look up a handed-off call.

Test it

Call a number that reaches the Bot widget, ask the agent for a person, and check that the caller reaches a human agent and the widget returns the hand-off. Then open the call with GET /v1/calls/{call_id}: sip_headers should list x-zoom-call-sid, and external_call_id should match the call sid your flow uses.

Limits

  • ThunderPhone doesn't post to the connector's Static URL, so Zoom gets the hand-off through the lookup, or through mapped headers if your test shows Zoom reads them from the REFER.
  • The trunk is built by Zoom's ISV team, so you can't set it up or test it on your own.