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

Contact centers

Connect from Amazon Connect

Send Amazon Connect calls to a ThunderPhone agent over SIP with the contact id and UUI, and resume your flow with the agent

Your Amazon Connect flow sends the caller to the agent with the contact id and any UUI you set. When the agent is done, it hangs up, your flow carries on, and a Lambda fetches the hand-off by contact id.

Before you start

  • A ThunderPhone agent and an organization API key.
  • Amazon Connect's External voice transfer connector. Request the "External voice transfer connectors per account" quota increase first: the menu stays hidden until AWS approves it. AWS bills each connector per day ($100 a day since July 21, 2025) plus per-minute usage.

1. Create the SIP endpoint

The connector doesn't document SIP digest authentication, so create an endpoint that accepts calls by source address:

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": "Amazon Connect",
    "auth": "ip_allowlist",
    "allowed_source_cidrs": ["CONNECT_SIGNALLING_RANGE_1", "CONNECT_SIGNALLING_RANGE_2"]
  }'

Get the connector's SIP signalling ranges for your Region from AWS. Each must be a public IPv4 range of /16 or narrower (IPv6 /48 or narrower). See Platforms that authenticate by source IP for the rules and the risk of an address shared by every customer of the platform. Keep the response's sip_uri, for example sip:agent-12-jhxmginwjx@sip.thunderphone.com.

2. Send calls with context

  1. In the Amazon Connect console, open External Voice Systems → Voice Transfer Integrations and choose Create.
  2. Pick the destination type (an SBC) and add an outbound route with host sip.thunderphone.com, port 5061 and protocol TLS. The endpoint needs TLS or TCP (port 5060) and refuses UDP. AWS doesn't document which protocols the connector offers, so confirm with AWS that it can use one of them.
  3. In your flow, add a Transfer to phone number block with Transfer to set to External voice system, and set:
    • Destination ARN: the connector.
    • To User: the user part of the endpoint's sip_uri, such as agent-12-jhxmginwjx.
    • UUI (optional): context for the agent. Connect sends it hex-encoded.
    • Resume flow after disconnect: Yes, so the flow continues when the agent hangs up.

Connect sends the contact id in X-Amzn-ConnectContactId. ThunderPhone stores it as the call's external_call_id, which is how your flow finds the call later. The UUI reaches the prompt as {{sip_uui}}. Third-party integrations document the contact-id header; AWS's own docs don't, so check it on your first test call.

3. Get the call back

AWS documents no way for Connect to read headers on the BYE or to accept a REFER from the agent. So the agent returns the call: it says goodbye and hangs up, and your flow resumes after the transfer block. Add this transfer number (see Add the transfer number):

{"name": "Amazon Connect", "action": "return"}

Then fetch the hand-off with an Invoke AWS Lambda function block after the transfer. The Lambda looks the call up by contact id:

import json
import os
import urllib.error
import urllib.parse
import urllib.request
 
 
def lambda_handler(event, context):
    contact_id = event["Details"]["ContactData"]["ContactId"]
    url = "https://api.thunderphone.com/v1/calls/handoff?" + urllib.parse.urlencode(
        {"external_call_id": contact_id}
    )
    request = urllib.request.Request(
        url, headers={"Authorization": "Bearer " + os.environ["THUNDERPHONE_API_KEY"]}
    )
    try:
        with urllib.request.urlopen(request, timeout=5) as response:
            call = json.load(response)
    except (urllib.error.URLError, TimeoutError):
        return {"found": "false"}
    handoff = call.get("handoff") or {}
    return {
        "found": "true",
        "intent": handoff.get("intent") or "",
        "summary": handoff.get("summary") or "",
    }

The block returns a flat map, so the flow can branch on intent or set summary as a contact attribute for the person who takes the call. Run the block synchronously (up to 8 seconds) and store the API key encrypted, for example in AWS Secrets Manager. The response fields are in Look up a handed-off call.

Test it

Call a number on your Connect instance that reaches the transfer block, ask the agent for a person, and check that the flow resumes. Then open the call with GET /v1/calls/{call_id}: sip_headers should list x-amzn-connectcontactid, and external_call_id should equal the ContactId your Lambda receives. If the transfer fails with SIP 404, ThunderPhone didn't find the endpoint's user part: it must be in both the request URI and the To header.

Limits

  • AWS documents no way to read headers on the BYE, including X-TP-Handoff-Id, so the contact id is the only link back to the call.
  • AWS doesn't document a maximum UUI length for the connector.
  • A synchronous Lambda block times out after 8 seconds.