---
title: "Connect from Zoom Contact Center"
description: "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](/guides/api-keys).
- 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](https://developers.zoom.us/docs/contact-center/library/guides/3p-voicebot/)).
- 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](/guides/sip-addresses#platforms-that-dial-a-phone-number)):

```bash
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](/guides/contact-centers/overview#add-the-transfer-number)),
with the SBC address Zoom gives you:

```json
{"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](/api-reference/calls#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.
