This guide provides a step-by-step walkthrough for building a feature within a Next.js application to send Multimedia Messaging Service (MMS) messages using AWS services. We'll leverage the AWS SDK for JavaScript v3, specifically interacting with the Amazon Pinpoint SMS and Voice v2 API (sms-voice
namespace). While the AWS console might still reference SNS for SMS history, this newer Pinpoint API is used for sending MMS messages programmatically. Amazon S3 will be used for media storage.
Project Overview and Goals
Goal: To create a simple Next.js application with a backend API endpoint capable of sending MMS messages containing text and an image hosted on Amazon S3 to a specified phone number.
Problem Solved: This provides a foundational implementation for applications needing to send rich media messages programmatically – useful for notifications, alerts, marketing campaigns, or user interactions involving images or other media.
Technologies:
- Next.js: A React framework providing server-side rendering, static site generation, and simplified API route creation. Chosen for its developer experience and ease of building full-stack applications.
- AWS SDK for JavaScript v3: The modern AWS SDK, used to interact with AWS services programmatically. We'll use clients for Amazon Pinpoint SMS and Voice v2 (
@aws-sdk/client-pinpoint-sms-voice-v2
). Chosen for its modularity and async/await support. - Amazon Pinpoint SMS and Voice v2 API: The specific AWS API endpoint (
SendMediaMessage
) required for sending MMS messages. - Amazon S3: Used to host the media files (e.g., images, videos) that will be included in the MMS messages. Chosen for its scalability, durability, and integration with other AWS services.
- Node.js: The runtime environment for Next.js and the AWS SDK.
Architecture:
+-----------------+ +------------------------+ +--------------------------------+ +---------------------+ +--------------------+
| User Browser |----->| Next.js Frontend UI |----->| Next.js API Route (/api/mms) |----->| AWS SDK (Pinpoint V2) |----->| AWS Pinpoint Service |
| (React Component)| | (pages/index.js) | | (pages/api/send-mms.js) | | (SendMediaMessage) | | (Sends MMS) |
+-----------------+ +------------------------+ +--------------------------------+ +---------------------+ +----------+---------+
| |
| | Reads Media
| Uses Credentials & Config +---v---+-------+
+------------------------------------------------------->| Amazon S3 |
| (Media File)|
+-------------+
Prerequisites:
- An AWS account with permissions to manage IAM, S3, and Pinpoint/SNS resources.
- Node.js (v18 or later recommended) and npm/yarn installed.
- AWS CLI installed and configured (useful for setup and testing).
- A basic understanding of Next.js, React, and asynchronous JavaScript.
- An MMS-capable phone number obtained through AWS (e.g., a 10DLC number, Toll-Free Number in supported regions like US/Canada).
Note: You will set up the S3 bucket and upload the media file in Section 2 as part of this guide.
Outcome: By the end of this guide, you will have a functional Next.js application with a simple UI allowing users to input a destination phone number, a message body, and a media URL (hosted on S3), which triggers an API call to send an MMS message via AWS.
1. Setting up the Project
Let's initialize our Next.js project and install the necessary dependencies.
-
Create Next.js App: Open your terminal and run:
npx create-next-app@latest nextjs-aws-mms --typescript # or # yarn create next-app nextjs-aws-mms --typescript
Follow the prompts (App Router recommended: Yes, Tailwind CSS: Optional,
src/
directory: Yes, Customize defaults: No). -
Navigate to Project Directory:
cd nextjs-aws-mms
-
Install AWS SDK v3 Clients: We need the client for the Pinpoint SMS and Voice v2 API.
npm install @aws-sdk/client-pinpoint-sms-voice-v2 # or # yarn add @aws-sdk/client-pinpoint-sms-voice-v2
-
Set Up Environment Variables: Create a file named
.env.local
in the root of your project. Never commit this file to version control if it contains sensitive credentials.# AWS Credentials (Best Practice: Use IAM Roles for EC2/ECS/Lambda or temporary credentials) # For local development, these can be used but are less secure. AWS_ACCESS_KEY_ID=YOUR_AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY=YOUR_AWS_SECRET_ACCESS_KEY # AWS Configuration AWS_REGION=us-east-1 # IMPORTANT: Use the SAME region as your S3 bucket and Origination Number # Application Specific # Get this from your AWS Pinpoint/SNS console after acquiring a number SNS_ORIGINATION_NUMBER=+1xxxxxxxxxx # Your MMS-capable AWS phone number in E.164 format S3_BUCKET_NAME=your-mms-media-bucket-name # The name of your S3 bucket
- Obtaining AWS Credentials:
- Navigate to the AWS IAM console.
- Create an IAM user with Programmatic access.
- Attach necessary permissions (see Step 5).
- Record the generated Access Key ID and Secret Access Key.
- Security Note: For production deployments on AWS (EC2, Lambda, etc.), use IAM Roles instead of storing long-lived credentials. The SDK automatically uses roles if available. For local development, these environment variables are common, but ensure
.env.local
is in your.gitignore
.
- AWS Region: Choose the AWS region where you will create your S3 bucket and where your origination phone number is registered. Consistency is crucial.
- Origination Number: Acquire an MMS-capable number through the AWS console (often under Pinpoint or SNS sections depending on your setup). Ensure it's active and MMS is enabled.
- S3 Bucket Name: The globally unique name of the S3 bucket you'll create in the next section.
- Obtaining AWS Credentials:
-
Configure IAM Permissions: The IAM user (or role) whose credentials your Next.js app uses needs permissions to:
- Send MMS messages via Pinpoint SMS/Voice v2.
- Read the media file from your S3 bucket.
Create an IAM policy (e.g.,
NextJsMmsAppPolicy
) with the following JSON structure, replacing placeholders:{ ""Version"": ""2012-10-17"", ""Statement"": [ { ""Sid"": ""AllowMmsSend"", ""Effect"": ""Allow"", ""Action"": ""sms-voice:SendMediaMessage"", ""Resource"": ""*"" }, { ""Sid"": ""AllowS3Read"", ""Effect"": ""Allow"", ""Action"": ""s3:GetObject"", ""Resource"": ""arn:aws:s3:::YOUR_MMS_MEDIA_BUCKET_NAME/*"" } ] }
- Replace
YOUR_MMS_MEDIA_BUCKET_NAME
with your actual bucket name. - Attach this policy to the IAM user you created in step 4 (or to the IAM role your deployment environment uses).
- Why these permissions?
sms-voice:SendMediaMessage
allows the application to use the specific API call for MMS.s3:GetObject
enables the Pinpoint service (acting on behalf of your call) to retrieve the media file from your bucket when constructing the MMS.
2. AWS Resource Setup (S3 Bucket and Media)
MMS messages require media files to be hosted somewhere accessible. AWS S3 is the standard choice.
-
Create S3 Bucket:
- Navigate to the AWS S3 console.
- Click ""Create bucket"".
- Enter a globally unique bucket name (this will be your
S3_BUCKET_NAME
in.env.local
). - Select the same AWS Region as specified in your
.env.local
and where your Origination Number resides. This is critical for MMS sending. - Keep default settings for Block Public Access (usually enabled – we will use IAM permissions for access, not public URLs).
- Click ""Create bucket"".
-
Upload Media File:
- Navigate into your newly created bucket in the S3 console.
- Click ""Upload"".
- Add your media file (e.g.,
cat-image.jpg
). Ensure it meets AWS MMS requirements (check Supported file types and sizes). Common formats like JPEG, PNG, GIF (under limits) are usually fine. - Click ""Upload"".
-
Get Media File S3 URI:
- Once uploaded, select the media file in the S3 console.
- Find the ""S3 URI"" (it looks like
s3://your-mms-media-bucket-name/cat-image.jpg
). You will need this URI later for the API call. Note: This is not the public Object URL.
3. Implementing the Backend API Route
We'll create a Next.js API route to handle the MMS sending logic.
-
Create API Route File: Create the file
src/app/api/send-mms/route.ts
(if using App Router) orsrc/pages/api/send-mms.ts
(if using Pages Router). This example uses the App Router structure. -
Implement the API Handler: Paste the following code into
src/app/api/send-mms/route.ts
:// src/app/api/send-mms/route.ts import { NextRequest, NextResponse } from 'next/server'; import { PinpointSMSVoiceV2Client, SendMediaMessageCommand, SendMediaMessageCommandInput, } from '@aws-sdk/client-pinpoint-sms-voice-v2'; // Ensure environment variables are loaded (especially outside Vercel) // require('dotenv').config({ path: '.env.local' }); // Uncomment if needed locally and not using Next.js auto-loading // Retrieve environment variables (checks happen within the handler) const { AWS_REGION, SNS_ORIGINATION_NUMBER } = process.env; // Create the Pinpoint SMS Voice v2 Client // Credentials will be automatically sourced from environment variables // (AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY) or IAM role if deployed on AWS infrastructure. const pinpointClient = new PinpointSMSVoiceV2Client({ region: AWS_REGION, // SDK will use AWS_REGION env var if not explicitly passed here }); export async function POST(request: NextRequest) { console.log('Received request to /api/send-mms'); // Validate required environment variables *per request* if (!AWS_REGION || !SNS_ORIGINATION_NUMBER) { console.error('Server configuration error: Missing AWS_REGION or SNS_ORIGINATION_NUMBER'); return NextResponse.json( { error: 'Server configuration error: Missing AWS environment variables.' }, { status: 500 } ); } let requestBody; try { requestBody = await request.json(); console.log('Request body:', requestBody); } catch (error) { console.error('Error parsing request body:', error); return NextResponse.json({ error: 'Invalid request body' }, { status: 400 }); } const { destinationPhoneNumber, messageBody, mediaUrl } = requestBody; // Basic Input Validation if (!destinationPhoneNumber || !mediaUrl) { return NextResponse.json( { error: 'Missing required fields: destinationPhoneNumber and mediaUrl are required.' }, { status: 400 } ); } // More robust validation could be added here (e.g., E.164 format check for phone number, S3 URI format check) if (!/^s3:\/\/[^\/]+\/.+/.test(mediaUrl)) { return NextResponse.json( { error: 'Invalid mediaUrl format. Must be an S3 URI (e.g., s3://bucket-name/key-name.jpg).' }, { status: 400 } ); } if (!/^\+\d{11,15}$/.test(destinationPhoneNumber)) { return NextResponse.json( { error: 'Invalid destinationPhoneNumber format. Must be E.164 format (e.g., +12223334444).' }, { status: 400 } ); } const params: SendMediaMessageCommandInput = { DestinationPhoneNumber: destinationPhoneNumber, // Provided in request OriginationIdentity: SNS_ORIGINATION_NUMBER, // From environment variable MessageBody: messageBody || '', // Optional message body from request MediaUrls: [mediaUrl], // S3 URI from request (must be an array) // ConfigurationSetName: 'YourOptionalConfigSetName' // If using configuration sets }; console.log('Attempting to send MMS with params:', params); try { const command = new SendMediaMessageCommand(params); const response = await pinpointClient.send(command); console.log('MMS sent successfully:', response); // The response contains the MessageId return NextResponse.json({ success: true, messageId: response.MessageId, message: 'MMS sent successfully', }, { status: 200 }); } catch (error: any) { console.error('Failed to send MMS:', error); // Provide more specific feedback if possible let errorMessage = 'Failed to send MMS.'; let statusCode = 500; if (error.name === 'ValidationException') { errorMessage = `Invalid request parameter: ${error.message}`; statusCode = 400; } else if (error.name === 'AccessDeniedException') { errorMessage = `Permission denied. Check IAM permissions for sms-voice:SendMediaMessage and s3:GetObject. ${error.message}`; statusCode = 403; } else if (error.name === 'ResourceNotFoundException') { errorMessage = `Resource not found (e.g., S3 object or Origination Number): ${error.message}`; statusCode = 404; } else if (error.name === 'ThrottlingException') { errorMessage = `Request throttled by AWS: ${error.message}`; statusCode = 429; } // Add more specific error handling as needed return NextResponse.json({ error: errorMessage, details: error.message }, { status: statusCode }); } }
Code Explanation:
- Imports
NextRequest
,NextResponse
and necessary AWS SDK v3 components. - Retrieves AWS configuration and the origination number from environment variables.
- Instantiates the
PinpointSMSVoiceV2Client
. Credentials are automatically handled by the SDK based on environment variables or IAM roles. - Defines an asynchronous
POST
handler function as required by Next.js App Router API routes. - Checks for required environment variables (
AWS_REGION
,SNS_ORIGINATION_NUMBER
) within the handler to ensure configuration is present for each request. - Parses the incoming JSON request body (
destinationPhoneNumber
,messageBody
,mediaUrl
). - Performs basic input validation (presence checks, S3 URI format, E.164 format).
- Constructs the
SendMediaMessageCommandInput
object using data from the request and environment variables. Note thatMediaUrls
must be an array of S3 URIs. - Creates a
SendMediaMessageCommand
with the parameters. - Calls
pinpointClient.send(command)
within atry...catch
block to execute the API call. - On success, logs the response and returns a JSON object with
success: true
and themessageId
. - On failure, logs the error and returns a JSON error response with an appropriate status code and message, attempting to provide specific feedback based on the AWS error type.
- Imports
4. Implementing the Frontend UI
Now, let's create a simple React component to interact with our API endpoint.
-
Create Component File: Create
src/components/MmsSender.tsx
. -
Implement the Component: Paste the following code:
// src/components/MmsSender.tsx 'use client'; // Required for components with interactivity (useState, onClick) in App Router import React, { useState } from 'react'; export default function MmsSender() { const [destinationPhoneNumber, setDestinationPhoneNumber] = useState(''); const [messageBody, setMessageBody] = useState(''); const [mediaUrl, setMediaUrl] = useState(''); // Should be an S3 URI entered by the user const [statusMessage, setStatusMessage] = useState(''); const [isLoading, setIsLoading] = useState(false); const handleSubmit = async (event: React.FormEvent<HTMLFormElement>) => { event.preventDefault(); setIsLoading(true); setStatusMessage('Sending MMS...'); // Basic validation reminder if (!destinationPhoneNumber || !mediaUrl) { // Check if mediaUrl is provided setStatusMessage('Error: Destination Phone Number and Media URL (S3 URI) are required.'); setIsLoading(false); return; } // E.164 format validation reminder if (!/^\+\d{11,15}$/.test(destinationPhoneNumber)) { setStatusMessage('Error: Invalid phone number format. Use E.164 (e.g., +12223334444).'); setIsLoading(false); return; } // S3 URI format validation reminder (basic) if (!/^s3:\/\/[^\/]+\/.+/.test(mediaUrl)) { setStatusMessage('Error: Invalid Media URL format. Must be an S3 URI (e.g., s3://bucket-name/key-name.jpg).'); setIsLoading(false); return; } try { const response = await fetch('/api/send-mms', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ destinationPhoneNumber, messageBody, mediaUrl: mediaUrl, // Use the S3 URI from state }), }); const result = await response.json(); if (response.ok && result.success) { setStatusMessage(`MMS sent successfully! Message ID: ${result.messageId}`); // Clear form on success setDestinationPhoneNumber(''); setMessageBody(''); setMediaUrl(''); } else { setStatusMessage(`Error: ${result.error || 'Failed to send MMS.'} ${result.details ? `(${result.details})` : ''}`); } } catch (error) { console.error('Network or unexpected error:', error); setStatusMessage('Error: Failed to connect to the API.'); } finally { setIsLoading(false); } }; return ( <div className=""max-w-md mx-auto mt-10 p-6 border rounded-lg shadow-lg""> <h2 className=""text-2xl font-semibold mb-4 text-center"">Send MMS via AWS</h2> <form onSubmit={handleSubmit}> <div className=""mb-4""> <label htmlFor=""destinationPhoneNumber"" className=""block text-sm font-medium text-gray-700 mb-1""> Destination Phone Number (E.164): </label> <input type=""tel"" id=""destinationPhoneNumber"" value={destinationPhoneNumber} onChange={(e) => setDestinationPhoneNumber(e.target.value)} placeholder=""+12223334444"" required className=""w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500"" /> </div> <div className=""mb-4""> <label htmlFor=""messageBody"" className=""block text-sm font-medium text-gray-700 mb-1""> Message Body (Optional): </label> <textarea id=""messageBody"" value={messageBody} onChange={(e) => setMessageBody(e.target.value)} rows={3} className=""w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500"" /> </div> <div className=""mb-4""> <label htmlFor=""mediaUrl"" className=""block text-sm font-medium text-gray-700 mb-1""> Media S3 URI: </label> <input type=""text"" id=""mediaUrl"" value={mediaUrl} onChange={(e) => setMediaUrl(e.target.value)} placeholder=""s3://your-bucket/image.jpg"" required className=""w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500"" /> <p className=""text-xs text-gray-500 mt-1"">Must be the S3 URI of the media file (e.g., s3://bucket-name/key).</p> </div> <button type=""submit"" disabled={isLoading} className=""w-full py-2 px-4 bg-indigo-600 text-white font-semibold rounded-md shadow hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 disabled:opacity-50"" > {isLoading ? 'Sending...' : 'Send MMS'} </button> </form> {statusMessage && ( <p className={`mt-4 text-sm ${statusMessage.startsWith('Error:') ? 'text-red-600' : 'text-green-600'}`}> {statusMessage} </p> )} </div> ); }
- Code Explanation: Uses
useState
to manage form inputs and status messages. ThehandleSubmit
function prevents default form submission, sets loading state, performs basic frontend validation checks (including for the S3 URI format), sends aPOST
request to/api/send-mms
usingfetch
, includes the form data (using the user-providedmediaUrl
) in the JSON body, and updates the status message based on the API response.
- Code Explanation: Uses
-
Use the Component: Replace the content of
src/app/page.tsx
(orsrc/pages/index.tsx
) with:// src/app/page.tsx import MmsSender from '@/components/MmsSender'; // Adjust path if needed export default function HomePage() { return ( <main className=""flex min-h-screen flex-col items-center justify-center p-4""> <MmsSender /> </main> ); }
5. Error Handling and Logging
Our API route includes basic error handling and logging:
- Input Validation: Checks for required fields and basic format validity (S3 URI, E.164) on the backend. This prevents unnecessary calls to AWS.
- AWS SDK Errors: The
try...catch
block aroundpinpointClient.send()
catches errors from the AWS API. - Specific Error Handling: We attempt to identify common errors (
ValidationException
,AccessDeniedException
,ResourceNotFoundException
,ThrottlingException
) and return more informative messages and appropriate HTTP status codes (400, 403, 404, 429). - Generic Errors: A fallback
500 Internal Server Error
is returned for unexpected issues. - Logging:
console.log
andconsole.error
are used to log request details, success messages, and errors on the server side (visible in your terminal during local development or in your deployment provider's logs).
Improvements:
- Structured Logging: Use a library like
pino
orwinston
for structured JSON logging, which is easier to parse and analyze in production logging systems (e.g., CloudWatch Logs, Datadog). - Error Tracking Services: Integrate services like Sentry or Bugsnag to capture and aggregate errors automatically.
- Retry Mechanisms: While the AWS SDK has built-in retries for transient network issues, you might implement application-level retries with exponential backoff (using libraries like
async-retry
) for specific scenarios (e.g., temporary throttling if you anticipate bursts), although this adds complexity.
6. Security Considerations
- IAM Least Privilege: Ensure the IAM user/role used by the application has only the permissions required (
sms-voice:SendMediaMessage
,s3:GetObject
on the specific bucket). Avoid usingAdministratorAccess
. - Input Sanitization/Validation: The backend API route must rigorously validate all inputs (
destinationPhoneNumber
,messageBody
,mediaUrl
) to prevent injection attacks or malformed requests. Check formats (E.164, S3 URI), lengths, and potentially disallowed characters. - Rate Limiting: Implement rate limiting on the
/api/send-mms
endpoint to prevent abuse and control costs. Libraries likerate-limiter-flexible
or platform features (Vercel, Cloudflare) can be used. - Authentication/Authorization: This guide assumes an open endpoint. In a real application, protect this API route. Ensure only authenticated and authorized users/systems can trigger MMS sending. Implement session management, JWT, or API keys depending on your architecture.
- Secrets Management: Never commit secrets (
.env.local
) to Git. Use environment variables provided by your hosting platform (Vercel, Netlify, AWS Systems Manager Parameter Store, Secrets Manager) for production deployments. - S3 Bucket Policy: Double-check that your S3 bucket is not publicly accessible unless intended. Rely on IAM permissions for the application to access media.
7. Testing
-
Local Development:
- Run
npm run dev
oryarn dev
. - Open
http://localhost:3000
in your browser. - Fill in a verified destination phone number (if your AWS account is still in the SMS/MMS sandbox) or any valid number (if out of the sandbox). Use the E.164 format (e.g.,
+15551234567
). - Enter an optional message.
- Enter the correct S3 URI for your uploaded media file (e.g.,
s3://your-mms-media-bucket-name/cat-image.jpg
). This is required. - Click ""Send MMS"".
- Check the status message on the UI.
- Check your terminal running the Next.js app for logs (
console.log
/console.error
output). - Check the destination phone for the MMS message.
- Run
-
API Testing (Curl): You can test the API endpoint directly using
curl
:curl -X POST http://localhost:3000/api/send-mms \ -H ""Content-Type: application/json"" \ -d '{ ""destinationPhoneNumber"": ""+1YOUR_TEST_PHONE_NUMBER"", ""messageBody"": ""Test message from curl!"", ""mediaUrl"": ""s3://YOUR_BUCKET_NAME/YOUR_MEDIA_KEY.jpg"" }'
Replace placeholders with your actual test number, bucket, and media key. Check the JSON response in your terminal.
-
Unit/Integration Tests (Advanced):
- Frontend: Use Jest and React Testing Library to test the
MmsSender
component's behavior (rendering, state changes, form submission). Mock thefetch
call. - Backend: Use Jest to test the API route handler (
POST
function). Mock the@aws-sdk/client-pinpoint-sms-voice-v2
client usingjest.mock
to simulate successful responses and various error conditions from AWS without making actual API calls. This verifies your logic, validation, and error handling.
- Frontend: Use Jest and React Testing Library to test the
8. Deployment
Deploying a Next.js application is typically straightforward.
-
Choose a Platform: Vercel (creator of Next.js), Netlify, AWS Amplify, or containerizing with Docker and deploying to AWS ECS/EKS/App Runner are common choices.
-
Environment Variables: This is the most critical step for deployment.
- Go to your chosen hosting provider's dashboard (e.g., Vercel Project Settings > Environment Variables).
- Add the following environment variables with their production values:
AWS_ACCESS_KEY_ID
AWS_SECRET_ACCESS_KEY
(Use IAM roles if deploying to AWS infrastructure like EC2/Lambda for better security)AWS_REGION
SNS_ORIGINATION_NUMBER
S3_BUCKET_NAME
- Ensure these variables are available to the ""Serverless Functions"" or backend environment.
-
Connect Git Repository: Link your GitHub/GitLab/Bitbucket repository to your hosting provider for automatic deployments on push.
-
Build & Deploy: The platform will typically build your Next.js application and deploy the static assets and API routes automatically.
-
Testing Production: Test the deployed application thoroughly, ensuring the environment variables are correctly configured and the application can communicate with AWS services. Check logs on your deployment platform.
9. Troubleshooting and Caveats
AccessDeniedException
:- Cause: IAM user/role lacks required permissions (
sms-voice:SendMediaMessage
ors3:GetObject
). - Solution: Verify the IAM policy attached to the user/role used by your application. Ensure the S3 resource ARN in the policy exactly matches your bucket (
arn:aws:s3:::your-bucket-name/*
).
- Cause: IAM user/role lacks required permissions (
ResourceNotFoundException
:- Cause: The specified
OriginationIdentity
(phone number) doesn't exist or isn't configured correctly in the specifiedAWS_REGION
, OR theMediaUrls
S3 object doesn't exist or the bucket name is wrong. - Solution: Double-check the
SNS_ORIGINATION_NUMBER
value and theAWS_REGION
. Verify the S3 URI (mediaUrl
) is correct, the bucket exists in the same region, and the object key is accurate.
- Cause: The specified
ValidationException
:- Cause: Incorrectly formatted parameters (e.g.,
DestinationPhoneNumber
not in E.164,MediaUrls
not an array or invalid S3 URI format, message too long, invalid file type/size). - Solution: Review the parameters being sent in the
SendMediaMessageCommandInput
. Check phone number format, ensureMediaUrls
is[s3Uri]
, and verify media file compliance with AWS limits. Check API logs for details.
- Cause: Incorrectly formatted parameters (e.g.,
- Region Mismatch (CRITICAL):
- Cause: The S3 bucket and the
OriginationIdentity
are in different AWS regions. - Solution: Ensure the S3 bucket, the origination number, and the
AWS_REGION
configured in your application/SDK client are all the SAME.
- Cause: The S3 bucket and the
- AWS SNS Sandbox Limits:
- Cause: New AWS accounts are often in the SMS/MMS sandbox, limiting sending to verified phone numbers only.
- Solution: Verify destination numbers in the AWS SNS console for testing, or request to move your account out of the sandbox via AWS Support for production use.
- MMS Capability:
- Cause: The
OriginationIdentity
(phone number) you acquired does not support MMS. - Solution: Verify the number's capabilities in the AWS console (Pinpoint/SNS). Acquire a number specifically enabled for MMS.
- Cause: The