code examples
code examples
Send MMS with AWS SNS and Next.js: A Developer Guide
A step-by-step guide for building a Next.js application to send MMS messages using AWS Pinpoint SMS and Voice v2 API, AWS SDK v3, and S3 for media storage.
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:
bashnpx create-next-app@latest nextjs-aws-mms --typescript # or # yarn create next-app nextjs-aws-mms --typescriptFollow the prompts (App Router recommended: Yes, Tailwind CSS: Optional,
src/directory: Yes, Customize defaults: No). -
Navigate to Project Directory:
bashcd nextjs-aws-mms -
Install AWS SDK v3 Clients: We need the client for the Pinpoint SMS and Voice v2 API.
bashnpm 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.localin the root of your project. Never commit this file to version control if it contains sensitive credentials.env# 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.localis 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:json{ ""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_NAMEwith 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:SendMediaMessageallows the application to use the specific API call for MMS.s3:GetObjectenables 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_NAMEin.env.local). - Select the same AWS Region as specified in your
.env.localand 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:typescript// 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,NextResponseand 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
POSThandler 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
SendMediaMessageCommandInputobject using data from the request and environment variables. Note thatMediaUrlsmust be an array of S3 URIs. - Creates a
SendMediaMessageCommandwith the parameters. - Calls
pinpointClient.send(command)within atry...catchblock to execute the API call. - On success, logs the response and returns a JSON object with
success: trueand 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:
typescript// 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
useStateto manage form inputs and status messages. ThehandleSubmitfunction prevents default form submission, sets loading state, performs basic frontend validation checks (including for the S3 URI format), sends aPOSTrequest to/api/send-mmsusingfetch, 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:typescript// 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...catchblock 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 Erroris returned for unexpected issues. - Logging:
console.logandconsole.errorare 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
pinoorwinstonfor 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:GetObjecton 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-mmsendpoint to prevent abuse and control costs. Libraries likerate-limiter-flexibleor 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 devoryarn dev. - Open
http://localhost:3000in 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.erroroutput). - Check the destination phone for the MMS message.
- Run
-
API Testing (Curl): You can test the API endpoint directly using
curl:bashcurl -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
MmsSendercomponent's behavior (rendering, state changes, form submission). Mock thefetchcall. - Backend: Use Jest to test the API route handler (
POSTfunction). Mock the@aws-sdk/client-pinpoint-sms-voice-v2client usingjest.mockto 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_IDAWS_SECRET_ACCESS_KEY(Use IAM roles if deploying to AWS infrastructure like EC2/Lambda for better security)AWS_REGIONSNS_ORIGINATION_NUMBERS3_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:SendMediaMessageors3: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 theMediaUrlsS3 object doesn't exist or the bucket name is wrong. - Solution: Double-check the
SNS_ORIGINATION_NUMBERvalue 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.,
DestinationPhoneNumbernot in E.164,MediaUrlsnot 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, ensureMediaUrlsis[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
OriginationIdentityare in different AWS regions. - Solution: Ensure the S3 bucket, the origination number, and the
AWS_REGIONconfigured 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
Frequently Asked Questions
How to send MMS with Next.js and AWS?
You can send MMS messages by creating a Next.js API route that interacts with the AWS Pinpoint SMS and Voice V2 API. This API, along with the AWS SDK for JavaScript v3, allows you to send messages containing text and media hosted on Amazon S3.
What is the Amazon Pinpoint SMS and Voice v2 API?
The Amazon Pinpoint SMS and Voice V2 API is the AWS service used for sending MMS messages programmatically. While the AWS console might still reference SNS for message history, the Pinpoint API is what's used for sending MMS from your application.
Why use Amazon S3 for MMS media?
Amazon S3 is used to store the media files (images, videos) included in MMS messages. It's chosen for its scalability, durability, and seamless integration with other AWS services, making it ideal for handling media in your MMS workflow.
When should I use the sms-voice namespace in AWS SDK?
The `sms-voice` namespace in the AWS SDK for JavaScript v3 is specifically for interacting with the Amazon Pinpoint SMS and Voice V2 API. Use this namespace when you need to send MMS messages or other SMS/voice communications programmatically.
Can I use a different React framework with AWS for MMS?
While this guide uses Next.js for its ease of API route creation and full-stack capabilities, you can adapt the core logic to work with other React frameworks. You'll still need the AWS SDK for JavaScript v3 and interact with the Pinpoint API.
How to set up AWS credentials for sending MMS?
For local development, you can set `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY` in a `.env.local` file. However, for production, it is best to use IAM roles for enhanced security. The AWS SDK will automatically use roles if available in your environment. Never commit credentials to version control.
What AWS permissions are needed for sending MMS?
Your IAM user or role needs the `sms-voice:SendMediaMessage` permission to send MMS via Pinpoint and `s3:GetObject` to allow the service to retrieve media from your designated S3 bucket.
How to structure the API request for sending an MMS?
Send a POST request to your API endpoint with a JSON body containing `destinationPhoneNumber` (in E.164 format), an optional `messageBody`, and `mediaUrl` which must be an S3 URI pointing to your media file.
What is the correct format for the mediaUrl in the API request?
The `mediaUrl` must be an S3 URI, for example, `s3://your-bucket-name/image.jpg`. It must follow this specific format so the Pinpoint API can retrieve the file.
Why does the AWS region matter for MMS?
Your S3 bucket, Origination Number, and the AWS region specified in your application/SDK client *must* all be the same. Mismatched regions will cause errors when trying to send MMS messages.
How to troubleshoot AccessDeniedException for sending MMS?
This error indicates missing IAM permissions. Verify your IAM user/role has `sms-voice:SendMediaMessage` and `s3:GetObject` permissions, ensuring the S3 resource ARN in the policy accurately specifies your bucket.
What if I get a ResourceNotFoundException when sending MMS?
This could mean your origination phone number (SNS_ORIGINATION_NUMBER) is incorrect, doesn't exist in the specified AWS region, or the media file specified by the S3 URI (mediaUrl) is not found or the bucket is incorrect.
How to fix ValidationException errors when sending MMS messages?
Check that the `destinationPhoneNumber` is in E.164 format, the `mediaUrl` is a valid S3 URI formatted as an array (`[s3Uri]`), and verify the media file meets AWS size and type limits. Ensure the origination number is also correct.
What are AWS SMS sandbox limitations for sending MMS?
New AWS accounts are often in a sandbox, restricting sending to verified phone numbers. Verify the destination numbers in the AWS SNS console or request to be moved out of the sandbox via AWS Support for production use.