Route inbound calls from Amazon Connect to a Vapi AI assistant using AWS Chime SDK Voice Connector and SIP trunking. Pass contact flow variables as custom SIP headers so Vapi knows caller context.
sequenceDiagram
participant Caller
participant Connect as Amazon Connect
participant CL as Connect Lambda
participant DDB as DynamoDB
participant Chime as Chime Phone<br/>(SIP Rule → SMA)
participant SL as SMA Lambda
participant VC as Voice Connector
participant Vapi as Vapi AI
Caller->>Connect: Calls Connect phone number
Connect->>Connect: Contact flow sets attributes<br/>(e.g. CustomerName)
Connect->>CL: Invoke Lambda
CL->>DDB: Store attributes keyed by caller number
CL-->>Connect: OK
Connect->>Chime: Transfer to Chime phone number
Chime->>SL: NEW_INBOUND_CALL
SL->>DDB: Query attributes by caller number
DDB-->>SL: {CustomerName: "John"}
SL-->>Chime: CallAndBridge with SipHeaders:<br/>x-customername: John
Chime->>VC: SIP INVITE
VC->>Vapi: SIP INVITE to<br/>sip:user@{id}.sip.vapi.ai
Vapi-->>VC: 200 OK
Vapi->>Caller: AI assistant answers
graph LR
subgraph Amazon Connect
CF[Contact Flow]
CPN[Connect Phone Number]
end
subgraph Lambda
CL[Connect Lambda<br/>stores metadata]
SL[SMA Lambda<br/>CallAndBridge + headers]
end
subgraph Chime SDK
VC[Voice Connector<br/>origination → Vapi]
SMA[SIP Media Application]
SR[SIP Rule]
CP[Chime Phone Number]
end
subgraph Storage
DDB[(DynamoDB<br/>call metadata)]
end
subgraph Vapi
VSIP[SIP Credential<br/>+ Phone Number]
VA[AI Assistant]
end
CPN --> CF
CF --> CL
CL --> DDB
CF --> CP
CP --> SR
SR --> SMA
SMA --> SL
SL --> DDB
SL --> VC
VC --> VSIP
VSIP --> VA
- AWS Account with CLI access configured
- Amazon Connect instance with a claimed phone number
- Vapi account with an AI assistant created
- AWS CLI v2 installed
In the AWS Console or via CLI:
aws chime-sdk-voice create-voice-connector \
--name "Vapi SIP Trunk" \
--aws-region us-west-2 \
--require-encryption falseSave the VoiceConnectorId from the response. You'll need it for config.env.
Why
require-encryption: false? Vapi's SIP endpoint accepts unencrypted UDP on port 5060. If your security requirements demand TLS/SRTP, you'll need to coordinate with Vapi support.
You need these IPs to whitelist in Vapi. The Voice Connector has a DNS hostname:
# Replace with your Voice Connector ID
dig +short YOUR_VC_ID.voiceconnector.chime.awsThis returns a set of IPs (typically in a /24 range, e.g. 99.77.253.0/24). Note these for the Vapi setup.
Create config.env:
# Required
AWS_REGION=us-west-2
VOICE_CONNECTOR_ID=your-voice-connector-id
# The user part of your Vapi SIP URI
# If your Vapi SIP URI is sip:myuser@abc123.sip.vapi.ai, set this to "myuser"
VAPI_SIP_URI_USER=myuser
# Optional
AWS_ACCOUNT_ID= # auto-detected if blank
CALL_TIMEOUT_SECONDS=30
AREA_CODE=503 # area code for the Chime phone numberRun the deployment script:
chmod +x deploy.sh
./deploy.shThis creates all AWS resources in order:
| Step | Resource | Purpose |
|---|---|---|
| 1 | IAM Role | Lambda execution role with CloudWatch + DynamoDB permissions |
| 1b | DynamoDB Table | Stores call metadata between Connect Lambda and SMA Lambda |
| 2 | SMA Lambda | Handles CallAndBridge to route calls through Voice Connector |
| 3 | Connect Lambda | Stores contact attributes from Connect flow in DynamoDB |
| 4 | VC Origination | Points Voice Connector at your Vapi SIP endpoint |
| 5 | VC Termination | Enables inbound SIP signaling on the Voice Connector |
| 6 | SIP Media Application | Links the SMA Lambda to Chime call processing |
| 7 | Chime Phone Number | The number Connect will transfer calls to |
| 8 | SIP Rule | Routes calls to the Chime phone number through the SMA |
The script is idempotent -- safe to run multiple times.
The deploy script configures this automatically, but for reference, the origination route points to your Vapi SIP endpoint:
{
"Routes": [{
"Host": "<credential-id>.sip.vapi.ai",
"Port": 5060,
"Protocol": "UDP",
"Priority": 1,
"Weight": 1
}],
"Disabled": false
}Important: The
Hostmust match the Vapi credential domain. If your Vapi SIP URI issip:user@abc123.sip.vapi.ai, the host isabc123.sip.vapi.ai. Update the origination route indeploy.shaccordingly.
The Connect Lambda must be associated with your Connect instance:
aws connect associate-lambda-function \
--instance-id YOUR_CONNECT_INSTANCE_ID \
--function-arn arn:aws:lambda:us-west-2:ACCOUNT:function:chime-connect-metadata-handlerAn example contact flow is included at contact-flow.json. Before importing, replace these placeholders:
REPLACE_WITH_CONNECT_LAMBDA_ARN-- the ARN of the Connect Lambda (chime-connect-metadata-handler) from the deploy outputREPLACE_WITH_CHIME_PHONE_NUMBER-- the Chime phone number from the deploy output (e.g.+15035551234)
To import:
- Open the Amazon Connect console → Contact flows
- Create a new contact flow → click the dropdown arrow next to Save → Import flow
- Select
contact-flow.json - Update the Lambda ARN and phone number placeholders in the blocks
- Save and publish
graph LR
A[Set Contact<br/>Attributes] --> B[Invoke<br/>Lambda]
B --> C[Transfer to<br/>Phone Number]
C -->|Error| D[Play Error<br/>Message]
D --> E[Disconnect]
C -->|Success| E
Block details:
-
Set contact attributes -- Set any key/value pairs you want forwarded to Vapi:
CustomerName= "John"AccountId= "12345"Language= "en"
-
Invoke AWS Lambda function -- Select the Connect Lambda (
chime-connect-metadata-handler). This stores the attributes in DynamoDB. -
Transfer to phone number -- Enter the Chime phone number from the deploy output (e.g.
+15035551234). SetThirdPartyConnectionTimeLimitSecondsto 30. -
Error handling -- Play an error message and disconnect if the transfer fails.
Associate your Connect phone number with this flow.
Create a BYO SIP trunk credential in Vapi. The gateway IPs are the Voice Connector's outbound IPs you found in step 1.2. These tell Vapi which IPs to expect inbound SIP traffic from.
curl -X POST https://api.vapi.ai/credential \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_VAPI_API_KEY" \
-d '{
"provider": "byo-sip-trunk",
"name": "Amazon Chime Voice Connector",
"gateways": [
{"ip": "99.77.253.57", "inboundEnabled": true},
{"ip": "99.77.253.24", "inboundEnabled": true},
{"ip": "99.77.253.36", "inboundEnabled": true}
]
}'Save the returned id -- this is your credential ID. Your Vapi SIP endpoint becomes:
sip:<user>@<credential-id>.sip.vapi.ai
Note on IP whitelisting: The Voice Connector resolves to many IPs. You need to whitelist all of them as gateways. Run
dig +short YOUR_VC_ID.voiceconnector.chime.awsto get all current IPs. Whitelisting by domain name is not yet supported by Vapi at the time of writing. Failing to whitelist all signaling IPs will result in401 Unauthorizederrors on inbound calls.
Associate a phone number with the SIP trunk credential and link it to your Vapi assistant:
curl -X POST https://api.vapi.ai/phone-number \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_VAPI_API_KEY" \
-d '{
"provider": "byo-phone-number",
"name": "Chime Inbound",
"number": "myuser",
"numberE164CheckEnabled": false,
"credentialId": "YOUR_CREDENTIAL_ID",
"assistantId": "YOUR_ASSISTANT_ID"
}'The number field must match VAPI_SIP_URI_USER in your config.env -- this is the user part of the SIP URI that the Voice Connector sends (e.g. sip:myuser@<credential-id>.sip.vapi.ai). Set numberE164CheckEnabled to false since this is a SIP URI user, not an E.164 phone number.
Make sure the Voice Connector origination host matches the credential-specific domain:
aws chime-sdk-voice put-voice-connector-origination \
--voice-connector-id YOUR_VC_ID \
--origination '{
"Routes": [{
"Host": "<credential-id>.sip.vapi.ai",
"Port": 5060,
"Protocol": "UDP",
"Priority": 1,
"Weight": 1
}],
"Disabled": false
}'The key value of this integration is passing context from Amazon Connect to Vapi through custom SIP headers. Here's how the data flows:
graph TD
A["Connect Flow<br/>Set Attributes:<br/>CustomerName=John<br/>AccountId=12345"] -->|Invoke Lambda| B["Connect Lambda<br/>Stores in DynamoDB:<br/>{CustomerName: John,<br/>AccountId: 12345}"]
B --> C["Connect transfers to<br/>Chime Phone Number"]
C -->|SIP Rule + SMA| D["SMA Lambda<br/>Reads DynamoDB<br/>Builds SipHeaders"]
D -->|"CallAndBridge<br/>SipHeaders:<br/>x-customername: John<br/>x-accountid: 12345"| E["Voice Connector"]
E -->|"SIP INVITE with<br/>x-customername: John<br/>x-accountid: 12345"| F["Vapi AI Assistant"]
Step 1: Connect flow sets contact attributes
In the contact flow, use the "Set contact attributes" block to define key-value pairs. These can be static values or dynamic references (e.g. from a CRM lookup, Lex bot, or customer input).
Step 2: Connect Lambda stores attributes in DynamoDB
The Connect Lambda (connect-handler.mjs) receives all contact attributes and stores them in DynamoDB, keyed by the caller's phone number with a 5-minute TTL:
// connect-handler.mjs (simplified)
const attributes = event.Details.ContactData.Attributes; // {CustomerName: "John", ...}
await ddb.send(new PutItemCommand({
TableName: TABLE_NAME,
Item: {
CallerNumber: { S: callerNumber },
Timestamp: { N: String(Date.now()) },
Attributes: { S: JSON.stringify(attributes) },
TTL: { N: String(Math.floor(Date.now() / 1000) + 300) },
},
}));Step 3: SMA Lambda reads attributes and sets SIP headers
When the call arrives at the SMA, the Lambda (index.mjs) queries DynamoDB by caller number, builds x- prefixed SIP headers, and includes them in the CallAndBridge action:
// index.mjs (simplified)
const metadata = await getMetadata(callFrom); // {CustomerName: "John", AccountId: "12345"}
const sipHeaders = {};
for (const [key, value] of Object.entries(metadata)) {
sipHeaders[`x-${key.toLowerCase()}`] = String(value);
}
// sipHeaders = {"x-customername": "John", "x-accountid": "12345"}
return {
SchemaVersion: "1.0",
Actions: [{
Type: "CallAndBridge",
Parameters: {
CallTimeoutSeconds: 30,
CallerIdNumber: callFrom,
Endpoints: [{
BridgeEndpointType: "AWS",
Arn: VOICE_CONNECTOR_ARN,
Uri: VAPI_SIP_URI_USER,
}],
SipHeaders: sipHeaders,
},
}],
};Per AWS documentation:
- Custom headers must use the
x-prefix (lowercase) - The prefix
x-amznis reserved and will cause an error - Header values are limited to 2048 characters
- The
User-to-UserandDiversionstandard headers are also supported
On the Vapi side, the SIP headers arrive with the inbound call. You can access them in your Vapi assistant's server URL webhook, tool calls, or assistant configuration to personalize the AI conversation based on the caller's context.
- Call your Amazon Connect phone number
- The flow sets attributes, invokes the Lambda, then transfers to the Chime number
- The SMA Lambda bridges the call to Vapi with SIP headers
- Your Vapi assistant answers
Monitor each stage in CloudWatch:
# Connect Lambda (stores metadata)
aws logs tail /aws/lambda/chime-connect-metadata-handler --follow
# SMA Lambda (CallAndBridge)
aws logs tail /aws/lambda/chime-sma-vapi-handler --followTo see the actual SIP INVITE and responses:
aws chime-sdk-voice put-voice-connector-logging-configuration \
--voice-connector-id YOUR_VC_ID \
--logging-configuration EnableSIPLogs=true
# After a test call, check logs:
aws logs filter-log-events \
--log-group-name "/aws/ChimeVoiceConnectorSipMessages/YOUR_VC_ID" \
--start-time $(date -v-10M +%s000)| Symptom | Cause | Fix |
|---|---|---|
CallRejected in Lambda logs |
Vapi doesn't recognize the SIP URI | Verify VAPI_SIP_URI_USER matches the Vapi phone number's sipUri user part |
CallRejected instantly |
Voice Connector IPs not whitelisted in Vapi | Run dig +short YOUR_VC_ID.voiceconnector.chime.aws and add all IPs to the Vapi credential |
| No metadata in SIP headers | Connect Lambda not invoked before transfer | Ensure the contact flow invokes the Lambda before the transfer block |
| Call stays open after hangup | SMA Lambda not handling HANGUP event |
Return a Hangup action for HANGUP and ACTION_FAILED events |
InvalidActionParameter |
Reserved SIP header name used | Don't use x-amzn prefix; use x- with your own naming |
To tear down all created resources:
chmod +x cleanup.sh
./cleanup.shThis removes (in reverse order): SIP Rule, SMA, Chime phone number, Voice Connector config, Lambda functions, DynamoDB table, and IAM role. The Voice Connector itself is not deleted.
amazon-chime-sip-calls/
├── config.env.example # Configuration template (copy to config.env)
├── deploy.sh # Creates all AWS resources
├── cleanup.sh # Tears down all resources
├── contact-flow.json # Example Amazon Connect contact flow (importable)
├── lambda/
│ ├── index.mjs # SMA Lambda (CallAndBridge + SIP headers)
│ └── connect-handler.mjs # Connect Lambda (stores metadata in DynamoDB)
└── README.md # This file