---
title: "Connect from Microsoft Dynamics 365"
description: "Send Dynamics 365 Contact Center and Copilot Studio calls to a ThunderPhone agent through your SBC with X-headers, and look up the agent's hand-off over HTTP."
---

Dynamics 365 Contact Center sends the caller to the agent through your session
border controller (SBC), with up to five X-headers including the conversation
id. When the agent is done, it hangs up, your SBC brings the caller back into
Dynamics 365, and an HTTP Request node fetches the hand-off.

## Before you start

- A ThunderPhone agent and an organization [API key](/guides/api-keys).
- The Dynamics 365 Contact Center voice channel, connected through Azure
  Communication Services **Direct Routing** to an SBC you run
  ([SBC requirements](https://learn.microsoft.com/en-us/azure/communication-services/concepts/telephony/direct-routing-infrastructure)):
  a supported SBC with a public certificate, TLS 1.2 on port 5061 and SRTP
  toward Microsoft.
- **SIP X-header support** on the **Transfer conversation** node
  ([Copilot Studio](https://learn.microsoft.com/en-us/microsoft-copilot-studio/voice-sip-x-header)).
  Confirm it is available in your environment.

Dynamics 365 transfers only to a phone number, and Microsoft authenticates
Direct Routing by certificate, not by password. Your SBC sits between
Microsoft and ThunderPhone and turns that number into the agent's SIP address.

## 1. Create the SIP endpoint

The call reaches ThunderPhone from your SBC, so create a standard endpoint for
the SBC to use:

```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": "Dynamics 365 SBC"}'
```

Store the `password` from the response straight away; it is shown once, and
your SBC answers ThunderPhone's challenge with it. If your SBC can't do digest
authentication, create the endpoint with `auth` set to `ip_allowlist` and your
SBC's public address instead; see
[Platforms that authenticate by source IP](/guides/sip-addresses#platforms-that-authenticate-by-source-ip).

## 2. Send calls with context

1. Pick a phone number for the agent on your SBC, such as `+14155550188`.
2. On the SBC, route calls to that number to the endpoint's `sip_uri`, for
   example `sip:agent-12-jhxmginwjx@sip.thunderphone.com`, over TLS on port
   `5061`. ThunderPhone finds the endpoint by the user part
   (`agent-12-jhxmginwjx`), which must be in both the request URI and the
   `To` header. Pass the X-headers through unchanged.
3. In Copilot Studio, add a **Transfer conversation** node with
   **External phone number transfer** to that number, and add the X-headers:
   - `X-Call-ID`: the Dynamics 365 conversation id. No Microsoft header carries
     it, so add it here; ThunderPhone stores it as the call's
     `external_call_id`. Confirm in Copilot Studio which variable holds the
     conversation id.
   - Up to four more, such as `X-Customer-Tier`.

Microsoft sends at most five X-headers, with names up to 64 characters and
values up to 256, using letters, digits and `= ; . ! % * _ + ~ -`. It writes
the prefix as lowercase `x-`; ThunderPhone matches header names ignoring case,
so `x-customer-tier` reaches the prompt as `{{sip_x_customer_tier}}`.

If you build the call with Azure Communication Services Call Automation
instead, `AddSipX` writes `X-MS-Custom-` names by default. Pass
`SipHeaderPrefix.X` so `X-Call-ID` keeps its name
([custom context](https://learn.microsoft.com/en-us/azure/communication-services/how-tos/call-automation/custom-context)).

## 3. Get the call back

The agent returns the call: it says goodbye and hangs up. Add this transfer
number (see
[Add the transfer number](/guides/contact-centers/overview#add-the-transfer-number)):

```json
{"name": "Dynamics 365", "action": "return"}
```

Bringing the caller back into Dynamics 365 is your SBC's job. Microsoft
documents one way in for a call with context: a new call through the SBC to a
number registered in Dynamics 365, with UUI whose keys match context variables
you set up under **Workstreams → Advanced settings**
([third-party IVR transfer](https://learn.microsoft.com/en-us/dynamics365/customer-service/administer/voice-channel-contextual-transfer-external-ivr)).
Work out with your SBC vendor how the caller reaches that number when
ThunderPhone hangs up, and carry the conversation id you sent in `X-Call-ID`
so the flow can find the call.

Then fetch the hand-off with a Copilot Studio **HTTP Request** node:

- `GET https://api.thunderphone.com/v1/calls/handoff?external_call_id=`
  followed by the conversation id.
- Header `Authorization: Bearer sk_live_…`.
- Parse the response into a variable and use `handoff.summary` and
  `handoff.intent`.

See [Look up a handed-off call](/api-reference/calls#look-up-a-handed-off-call).

## Test it

Call a Dynamics 365 number that reaches the transfer node, ask the agent for a
person, and check that the caller comes back into Dynamics 365. Then open the
call with `GET /v1/calls/{call_id}`: `sip_headers` should list `x-call-id`, and
`external_call_id` should be the conversation id. If the call never reaches the
agent and your SBC logs SIP `404`, its request URI or `To` header doesn't carry
the endpoint's user part.

## Limits

- An SBC is required. Microsoft can't dial a SIP address or answer a digest
  challenge.
- Microsoft sends at most five X-headers, and keeps only the first five on a
  call coming in. Dynamics 365 takes at most 128 characters of UUI.
- Microsoft doesn't document reading headers on a `BYE`, so the hand-off comes
  back only through the lookup.
