Skip to content

Chime SDK PSTN not working.

0

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"
            }
        ]
    }
}
  • Send Response Log
{
    "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"
            }
        ]
    }
}

asked 9 months ago126 views

1 Answer
0

Based on your description and logs, your call is being established correctly, but you're experiencing audio issues. Let's analyze the potential problems:

  1. Lambda Function Response Structure: In your Lambda function, there appears to be inconsistent response handling. For some event types (like NEW_OUTBOUND_CALL), you're using a helper function response() that properly formats the response, but for CALL_ANSWERED, you're directly returning response1 without using the helper function. This inconsistency might be causing issues with the audio flow.

  2. CallId Parameter: For the Speak action, you're correctly including the CallId parameter, which is essential for audio to work. However, make sure all actions that involve audio have the correct CallId specified.

  3. Timeout Issues: The Amazon Chime SDK PSTN audio service waits 5 seconds for Lambda functions to respond before retrying, and if no response is received after 20 seconds, it will issue a hangup. Check if your Lambda function is responding within these timeframes.

  4. Error Handling: Your Lambda function doesn't appear to have comprehensive error handling. If an error occurs during the execution, it might not be properly communicated back to the Chime SDK PSTN service.

  5. Action Failure Handling: When an action fails, the SIP media application invokes the Lambda function with an ACTION_FAILED event type. Your code doesn't seem to handle this event type specifically, which could lead to issues if any action in your sequence fails.

  6. Meeting Connection: The JoinChimeMeeting action appears to be successful based on your logs, but there might be issues with the meeting configuration or the join token that prevent proper audio bridging.

Recommendations:

  1. Ensure consistent response formatting throughout your Lambda function.
  2. Add explicit error handling for all possible event types, including ACTION_FAILED.
  3. Verify that your meeting is properly configured to accept PSTN participants.
  4. Check that the join token has the necessary permissions for audio.
  5. Consider adding logging for audio-specific events to better diagnose the issue.
  6. Ensure your Lambda function is responding within the required timeframes to avoid timeouts.
    Sources
    Making an outbound call for using Amazon Chime SDK PSTN audio service - Amazon Chime SDK
    Specifying actions in response to telephony events for the Amazon Chime SDK PSTN audio service - Amazon Chime SDK
    Understanding the Amazon Chime SDK PSTN audio service programming model - Amazon Chime SDK
    Understanding timeouts and retries for the Amazon Chime SDK PTSN audio service - Amazon Chime SDK

answered 9 months ago

You are not logged in. Log in to post an answer.

A good answer clearly answers the question and provides constructive feedback and encourages professional growth in the question asker.