Issue Description
The call is successfully established, but there is no audio during the call. Audio is not working in outbound call PSTN with SIP using Amazon Chime SDK (No SIM Required: Since this request is over HTTPS/Internet, no SIM is needed.)
Current Architecture
Browser/Client
↓
Backend API (create-meeting + make-call)
↓
AWS Chime SipMediaApplication (SMA)
↓
Lambda Function (smaHandler.mjs)
↓
Chime Meeting (Audio Bridge)
**
Steps to Verify AWS SMA and SIP Rule Configuration**
- Chime SDK voice connector created Encryption: enable Network Type: DUAL_STACK
- SIP media applications configure and attached the below lambda
- SIP roule created
Trigger type: Request URI hostname
Trigger value : Chime SDK voice connector (Outbound host name)
- Tried with another SIP rule
Trigger type: To phone number
Trigger value: Attached the phone number which we order from Chime SDK Phone number management
Note: We tried with both rule but same issue we have face.
Backend Flow
For Client side, Call is initiate from mobile app/ browser and they send an API request create-meeting to backend server
- In backend create the meeting (Using CreateMeetingCommand) and attendee(Using CreateAttendeeCommand), return meeting and attendee obj.
- After that browser/app call another API make-call to backend server to make outbound call to PSTN number using SIP with Amazon Chime SDK
In backend server, make-call API flow the below steps:
- Call the CreateSipMediaApplicationCallCommand function using AWS Chime SDK with below parameters
FromPhoneNumber
ToPhoneNumber
SipMediaApplicationId
ArgumentsMap { meetingId, joinToken, meetingRegion}
Lambda Function
In the Lambda function (smaHandler.mjs), handle the SIP Media Application events for bridging PSTN calls to Chime meetings.
let joinToken1 = null;
let meetingId1 = null;
export const handler = async (event) => {
// event.body is the JSON object sent by Chime (Lambda invocation)
const body = typeof event === 'string' ? JSON.parse(event) : event;
console.log('📞 SMA Lambda Event Received:', JSON.stringify(body, null, 2));
const { InvocationEventType, CallDetails, ActionData } = body;
const response1 = {
SchemaVersion: '1.0',
Actions: [],
};
if (InvocationEventType === 'NEW_OUTBOUND_CALL' || InvocationEventType === 'NEW_INBOUND_CALL') {
// Extract meeting and join token from ActionData.Parameters.Arguments
// This is where Chime SMA passes the meeting details
const meetingId = ActionData?.Parameters?.Arguments?.meetingId;
const joinToken = ActionData?.Parameters?.Arguments?.joinToken || ActionData?.Parameters?.Arguments?.attendeeJoinToken;
const meetingRegion = ActionData?.Parameters?.Arguments?.meetingRegion || 'us-east-1';
console.log('📍 Meeting Details:', { meetingId, joinToken: joinToken ? '✓ Present' : '✗ Missing', meetingRegion });
console.log('📱 Call Details:', {
from: CallDetails.Participants[0].From,
to: CallDetails.Participants[0].To,
callId: CallDetails.Participants[0].CallId,
sipMediaAppId: CallDetails.SipMediaApplicationId
});
if (!meetingId || !joinToken) {
console.error('❌ Missing meeting join data in ActionData.Parameters.Arguments');
return response(body, [
speak('Sorry, unable to connect your call right now. Meeting information is missing.'),
hangup()
]);
}
// Store for later use in CALL_ANSWERED event
joinToken1 = joinToken;
meetingId1 = meetingId;
// For NEW_OUTBOUND_CALL - Just acknowledge and wait for CALL_ANSWERED
console.log('✅ Outbound call initiated, waiting for callee to answer...');
return response(body, []);
}
if (InvocationEventType === "ACTION_SUCCESSFUL") {
console.log("✅ ACTION_SUCCESSFUL");
return response(body, []); // no additional action needed
}
if (InvocationEventType === "CALL_ANSWERED") {
console.log("📞 CALL ANSWERED BY CALLEE", joinToken1, meetingId1);
// Speak action - MUST include CallId for audio to work
response1.Actions.push({
Type: 'Speak',
Parameters: {
CallId: CallDetails.Participants[0].CallId,
Engine: 'neural',
Text: 'Hello, we are able to connect your call right now.',
VoiceId: 'Joanna',
LanguageCode: 'en-US'
}
});
// Pause action - gives time for client to stabilize before joining
response1.Actions.push({
Type: 'Pause',
Parameters: {
CallId: CallDetails.Participants[0].CallId,
ParticipantTag: "LEG-A",
DurationInMilliseconds: 5000
}
});
const legA = event.CallDetails.Participants
.find((p) => p.ParticipantTag === 'LEG-A');
// Join the Chime meeting after pause
response1.Actions.push({
Type: 'JoinChimeMeeting',
Parameters: {
CallId: legA.CallId,
ParticipantTag: "LEG-A",
JoinToken: joinToken1,
MeetingId: meetingId1,
}
});
}
if (InvocationEventType === "HANGUP") {
console.log("🔚 Call leg hung up");
return response(body, []);
}
console.log("===== Send Response =====", JSON.stringify(response1, null, 2));
return response1;
// For other events, return an empty set of actions (or Play/Collect DTMF etc.)
// return {
// statusCode: 200,
// body: JSON.stringify(response1)
// };
};
// ---- helpers for common actions ----
function speak(text) {
return {
Type: 'Speak',
Parameters: {
Engine: 'neural',
Text: text,
VoiceId: 'Joanna',
LanguageCode: 'en-US'
}
};
}
function hangup() {
return {
Type: 'Hangup',
Parameters: {
// ParticipantTag optional; default hangs up all legs
}
};
}
// Wrap actions into the response envelope
function response(event, actions) {
const resp = {
SchemaVersion: event.SchemaVersion || "1.0",
Actions: actions
};
console.log("===== SMA Response =====");
console.log(JSON.stringify(resp, null, 2));
return resp;
}
- Check CloudWatch logs for:
NEW_OUTBOUND_CALL event received ✓
Meeting details logged ✓
RINGING event received ✓
Once the call pick, CALL_ANSWERED event received and send Speak, Pause and JoinChimeMeeting Action in response ✓
Speak event received ✓
JoinChimeMeeting action queued ✓
ACTION_SUCCESSFUL event ✓
- NEW_OUTBOUND_CALL Lambda Event Received Log
{
"SchemaVersion": "1.0",
"Sequence": 1,
"InvocationEventType": "NEW_OUTBOUND_CALL",
"ActionData": {
"Type": "CallCreateRequest",
"Parameters": {
"Arguments": {
"joinToken": <token>,
"meetingId": <meetingId>,
"meetingRegion": <meetingRegion>
}
}
},
"CallDetails": {
"TransactionId": <TransactionId>,
"AwsAccountId": <AwsAccountId>,
"AwsRegion": "us-east-1",
"SipMediaApplicationId": <SipMediaApplicationId>,
"Participants": [
{
"CallId": <CallId>,
"ParticipantTag": "LEG-A",
"To": <To Number>,
"From": <From Number>,
"Direction": "Outbound",
"StartTimeInMilliseconds": "1763625172548"
}
]
}
}
- CALL_ANSWERED Lambda Event Received Log
{
"SchemaVersion": "1.0",
"Sequence": 3,
"InvocationEventType": "CALL_ANSWERED",
"CallDetails": {
"TransactionId": <TransactionId>,
"AwsAccountId": <AwsAccountId>,
"AwsRegion": "us-east-1",
"SipMediaApplicationId": <SipMediaApplicationId>,
"Participants": [
{
"CallId": <CallId>,
"ParticipantTag": "LEG-A",
"To": <To Number>,
"From": <From Number>,
"Direction": "Outbound",
"StartTimeInMilliseconds": "1763625172548",
"Status": "Connected"
}
]
}
}
{
"SchemaVersion": "1.0",
"Actions": [
{
"Type": "Speak",
"Parameters": {
"CallId": <CallId>,
"Engine": "neural",
"Text": "Hello, we are able to connect your call right now.",
"VoiceId": "Joanna",
"LanguageCode": "en-US"
}
},
{
"Type": "Pause",
"Parameters": {
"CallId": <CallId>,
"ParticipantTag": "LEG-A",
"DurationInMilliseconds": 5000
}
},
{
"Type": "JoinChimeMeeting",
"Parameters": {
"CallId": <CallId>,
"ParticipantTag": "LEG-A",
"JoinToken": <JoinToken>,
"MeetingId": <MeetingId>
}
}
]
}
- ACTION_SUCCESSFUL Lambda Event Received Log
{
"SchemaVersion": "1.0",
"Sequence": 4,
"InvocationEventType": "ACTION_SUCCESSFUL",
"ActionData": {
"Type": "JoinChimeMeeting",
"Parameters": {
"JoinToken": <JoinToken>,
"MeetingId": <MeetingId>
"CallId": <CallId>,
"ParticipantTag": "LEG-A"
}
},
"CallDetails": {
"TransactionId": <TransactionId>,
"AwsAccountId": "147997153143",
"AwsRegion": "us-east-1",
"SipMediaApplicationId": <SipMediaApplicationId>,
"Participants": [
{
"CallId": "736ae6f4-7cb1-454b-9084-04502e583368",
"ParticipantTag": "LEG-A",
"To": <To Number>, // Call Recipient Number
"From": <From Number>, // Verified Chime Number
"Direction": "Outbound",
"StartTimeInMilliseconds": "1763625172548",
"Status": "Connected"
},
{
"CallId": "a6641210-c38b-47ac-99a4-f7dfd4fe8f34",
"ParticipantTag": "LEG-B",
"To": <To Number>, // Chime Meeting Audio Bridge Number
"From": <From Number>, // Verified Chime Number
"Direction": "Outbound",
"StartTimeInMilliseconds": "1763625198258",
"Status": "Connected"
}
]
}
}