Amazon SES exposes a full REST/JSON control plane through the SES v2 API (API version 2019-09-27, service id sesv2) and ships first-party SDKs for 10 languages plus the AWS CLI v2. Every call is signed with SigV4 against a regional endpoint (https://email.{region}.amazonaws.com), so the same IAM policies, roles, and CloudTrail auditing that govern the rest of your AWS account also govern email sending. On the MCP front, however, AWS has not shipped a production-grade first-party MCP server dedicated to SES as of August 2026 — agents rely on the general-purpose AWS MCP Server (GA May 2026), the AWS-authored sample repo, or community wrappers.
sesv2, JSON payloads, SigV4 signing.Model Context Protocol lets AI agents call SES actions (send email, add contacts, list identities) as typed tools instead of raw REST calls. AWS shipped a general-purpose MCP surface in 2026 but has still not published a production-ready MCP server dedicated to SES — a gap worth flagging before wiring agents to your sending account.
AWS has not released a first-party, production-grade MCP server scoped to SES. The aws-samples/sample-for-amazon-ses-mcp repo exposes every public SES v2 action to MCP clients, but the README explicitly labels it a sample built on the in-development Smithy Java MCP integration and warns against production use. The closest first-party path is the general AWS MCP Server, which reached GA on May 15, 2026 and can drive SES alongside every other AWS service.
Five MCP entry points can address SES today. Only two carry an AWS label, and neither is a production-hardened SES-only server.
AWS-authored sample MCP server exposing all public SES v2 API actions to MCP clients (Amazon Q Developer CLI, Claude Desktop). Built on the Smithy Java MCP project and explicitly labelled a sample, not for production.
AWS’s general-purpose MCP server (GA May 2026) that lets agents interact with SES through AWS-native tooling, alongside every other AWS service — the closest first-party production path today.
TypeScript MCP server for composing and sending SES emails from Cursor or Claude Desktop, with plain-text/HTML, CC, BCC, reply-to, and scheduling support. Community-maintained, no AWS endorsement.
Community template MCP server that wraps AWS SES send operations, distributed via the Glama MCP registry. Intended as a starter template rather than a hardened production server.
Zapier’s hosted MCP endpoint exposes any of the 9,000+ Zapier apps — including Amazon SES — as MCP tools without writing code. Zero-infra option for agent-driven SES sends.
| Base URL | https://email.{region}.amazonaws.com — e.g. https://email.us-east-1.amazonaws.com. Regional endpoint is mandatory; there is no global SES endpoint. |
| API version | SES v2 (2019-09-27), service id sesv2. SES v1 (ses) remains for backward compatibility only. |
| Protocol | HTTPS REST/JSON, signed with AWS Signature Version 4. TLS 1.2+ required. |
| Response format | JSON. Errors return AWS-standard Code / Message shapes with X-Amzn-RequestId headers for support cases. |
| Timeout | Default SDK request timeout is 60 s. No fixed server-side max is documented per call; long operations expose asynchronous jobs. |
| Pagination | Cursor-based with NextToken. List calls accept PageSize and return NextToken when more results exist. |
| Idempotency | No global Idempotency-Key header. Most mutating actions (identity/template create) are naturally idempotent via unique names; SendEmail is not — retries on 5xx can double-send if not gated. |
| Bulk operations | SendBulkEmail accepts up to 50 Destinations per call against a templated message. |
| IPv6 | Dual-stack SMTP and API endpoints supported since May 2025. |
Resources are grouped by function — email identities, templates, contact lists, configuration sets, event destinations, suppression, dedicated IP pools, and account-level settings — and most operations map cleanly to REST verbs. The v2 API is intentionally leaner than v1: several legacy actions were dropped or renamed, so migrating from ses to sesv2 usually means changing the client, not just the endpoint.
Every SES v2 HTTPS call is signed with SigV4 using an AWS access key ID and secret access key. Long-lived IAM user keys are supported but not recommended for production — prefer roles. The SDKs handle SigV4 transparently once credentials are resolved via the default provider chain (environment, shared config file, EC2/ECS/EKS/Lambda role, IAM Identity Center).
Production workloads should assume a role through an EC2 instance profile, ECS task role, EKS IRSA, or Lambda execution role. Short-lived credentials are issued by STS automatically and rotated by the AWS runtime, and every SES action is auditable in CloudTrail with the role’s ARN. IAM Identity Center (AWS SSO) is the recommended human path for CLI-driven operations.
The SMTP endpoint uses a separate credential pair: a 20-char IAM SMTP username and a 44-char IAM SMTP password derived from an IAM user secret via HMAC-SHA256. These are region-specific and generated in the SES console or with the smtp_credentials_generate.py helper. Authentication is AUTH LOGIN over TLS; SES rejects unencrypted SMTP connections.
ses:SendEmail, ses:SendBulkEmail, and the specific list/manage actions each workload needs — a blanket ses:* policy against your production account is a common audit finding.| Limit | Value | Notes |
|---|---|---|
| Sandbox daily cap | 200 messages / 24 h | Hard cap for new accounts (and each new region) until production access is granted. Send only to verified addresses. |
| Sandbox send rate | 1 msg/sec | Per-second rate applied on top of the daily cap while in sandbox. |
| Production initial rate | 14 msg/sec | Typical starting rate after production access is granted; auto-scales upward on healthy sending patterns. |
| Recipients per message | 50 | Combined To + Cc + Bcc per message. SendBulkEmail allows up to 50 Destinations per call. |
| Message size | 40 MB | Raw payload after base64 encoding, SES v2 and SMTP. SES v1 legacy SendEmail still caps at 10 MB. Messages larger than 10 MB are bandwidth-throttled to about 40 MB/s. |
| Concurrent SMTP connections | Not published | AWS does not disclose a fixed concurrent SMTP count — throughput is expressed as msg/sec. |
| API requests per minute | Not published as a single RPM | Each administrative action has its own throttling profile plus the per-account send rate. |
SES throttles on the send rate (msg/sec) rather than a public RPM figure, and every administrative action has its own quota. For bursty workloads, batch through SendBulkEmail, use a configuration set with an SNS or CloudWatch event destination to observe rejections, and implement exponential backoff on Throttling and ThrottlingException errors — the AWS SDKs ship this retry policy by default but the intervals are worth tuning for spiky traffic.
AWS ships and maintains SES v2 clients across every mainstream backend language plus the CLI. All track the current sesv2 Smithy model, receive monthly-cadence releases, and are covered by AWS support.
| Language | Package | Install | Repo |
|---|---|---|---|
| Python | boto3 (client sesv2) | pip install boto3 | boto/boto3 |
| JavaScript / TypeScript (Node) | @aws-sdk/client-sesv2 | npm install @aws-sdk/client-sesv2 | aws/aws-sdk-js-v3 |
| Java | software.amazon.awssdk:sesv2 | Maven / Gradle dependency | aws/aws-sdk-java-v2 |
| Go | github.com/aws/aws-sdk-go-v2/service/sesv2 | go get github.com/aws/aws-sdk-go-v2/service/sesv2 | aws/aws-sdk-go-v2 |
| .NET (C#) | AWSSDK.SimpleEmailV2 | dotnet add package AWSSDK.SimpleEmailV2 | aws/aws-sdk-net |
| PHP | aws/aws-sdk-php (SesV2Client) | composer require aws/aws-sdk-php | aws/aws-sdk-php |
| Ruby | aws-sdk-sesv2 | gem install aws-sdk-sesv2 | aws/aws-sdk-ruby |
| Rust | aws-sdk-sesv2 | cargo add aws-sdk-sesv2 | awslabs/aws-sdk-rust |
| Kotlin | aws.sdk.kotlin:sesv2 | Gradle: implementation("aws.sdk.kotlin:sesv2:$version") | awslabs/aws-sdk-kotlin |
| CLI | aws sesv2 | Install AWS CLI v2 | aws/aws-cli |
ses, AWSSDK.SimpleEmail, SesClient) are still shipped and still work, but they cannot access newer resources (contact lists, VDM, event destinations v2, dedicated IP pools API) and cap raw messages at 10 MB. Any new SES integration in 2026 should target the sesv2 namespace across every SDK — older tutorials sometimes still import SESClient instead of SESv2Client.Because SES is API-first and covered by every AWS SDK, dedicated third-party SES SDKs are uncommon. Framework-level helpers are more useful: Laravel’s ses-v2 mail transport, Django’s django-ses, Rails’ aws-sdk-rails, WordPress WP Mail SMTP and WP Offload SES, and NestJS mailer modules all wrap the official SDKs and are the pragmatic path for stack-native integration. For agent workloads see the MCP directory above.
The full SES v2 REST reference lives in the Amazon SES v2 API Reference. The 15 endpoints below cover the resources most senders touch: sending, identities, templates, configuration sets and event destinations, contact lists, suppression, dedicated IPs, and account-level settings.
| Resource | Methods | Description |
|---|---|---|
| Email (transactional) /v2/email/outbound-emails | POST | SendEmail — send a formatted or raw email to up to 50 recipients. |
| Bulk email /v2/email/outbound-bulk-emails | POST | SendBulkEmail — templated message to up to 50 Destinations in one call. |
| Custom verification email /v2/email/outbound-custom-verification-emails | POST | SendCustomVerificationEmail — verification email using a custom template. |
| Email identities /v2/email/identities | GET, POST | ListEmailIdentities / CreateEmailIdentity — manage verified domains and addresses. |
| Email identity (single) /v2/email/identities/{EmailIdentity} | GET, DELETE | GetEmailIdentity / DeleteEmailIdentity — read or remove a verified identity. |
| Email templates /v2/email/templates | GET, POST | ListEmailTemplates / CreateEmailTemplate — manage reusable templates. |
| Email template (single) /v2/email/templates/{TemplateName} | GET, PUT, DELETE | Get, update, or delete a specific email template. |
| Configuration sets /v2/email/configuration-sets | GET, POST | ListConfigurationSets / CreateConfigurationSet — group sending options and event destinations. |
| Event destinations /v2/email/configuration-sets/{ConfigurationSetName}/event-destinations | GET, POST | Manage CloudWatch, SNS, Firehose, or EventBridge destinations for send events. |
| Contact lists /v2/email/contact-lists | GET, POST | ListContactLists / CreateContactList — manage list-of-contacts resources. |
| Contacts /v2/email/contact-lists/{ContactListName}/contacts | GET, POST | ListContacts / CreateContact — manage subscribers within a contact list. |
| Suppressed destinations /v2/email/suppression/addresses | GET, PUT, DELETE | Manage the account-level suppression list of hard-bounced and complained addresses. |
| Dedicated IP pools /v2/email/dedicated-ip-pools | GET, POST | ListDedicatedIpPools / CreateDedicatedIpPool — group dedicated IPs by purpose. |
| Dedicated IPs /v2/email/dedicated-ips | GET | GetDedicatedIps — list and inspect dedicated IPs assigned to the account. |
| Account /v2/email/account | GET, PUT | GetAccount / PutAccountDetails — read production status, quotas, update account details. |
import boto3
from botocore.exceptions import ClientError
# SES v2 client - region must match where your identity is verified
ses = boto3.client('sesv2', region_name='us-east-1')
# 1) Ping / sanity check: list verified identities
try:
resp = ses.list_email_identities(PageSize=10)
print('Verified identities:', [i['IdentityName'] for i in resp.get('EmailIdentities', [])])
except ClientError as e:
print('Ping failed:', e.response['Error']['Message'])
raise
# 2) Add a subscriber to a contact list
try:
ses.create_contact(
ContactListName='smtpedia-newsletter',
EmailAddress='reader@example.com',
UnsubscribeAll=False,
AttributesData='{"first_name":"Alaa"}'
)
print('Subscriber added.')
except ses.exceptions.AlreadyExistsException:
print('Contact already exists.')
except ClientError as e:
print('Create failed:', e.response['Error']['Message'])
raise
# 3) Send a transactional email
try:
ses.send_email(
FromEmailAddress='hello@yourdomain.com',
Destination={'ToAddresses': ['reader@example.com']},
Content={
'Simple': {
'Subject': {'Data': 'Welcome to SMTPedia'},
'Body': {'Text': {'Data': 'Thanks for signing up.'}}
}
}
)
print('Email sent.')
except ClientError as e:
print('Send failed:', e.response['Error']['Message'])
import { SESv2Client, CreateContactCommand, SendEmailCommand } from '@aws-sdk/client-sesv2';
const ses = new SESv2Client({ region: 'us-east-1' });
async function main() {
// 1) Add a subscriber to a contact list
try {
await ses.send(new CreateContactCommand({
ContactListName: 'smtpedia-newsletter',
EmailAddress: 'reader@example.com',
UnsubscribeAll: false,
AttributesData: JSON.stringify({ first_name: 'Alaa' })
}));
console.log('Subscriber added.');
} catch (err) {
if (err.name !== 'AlreadyExistsException') throw err;
console.log('Contact already exists.');
}
// 2) Send a transactional email
await ses.send(new SendEmailCommand({
FromEmailAddress: 'hello@yourdomain.com',
Destination: { ToAddresses: ['reader@example.com'] },
Content: {
Simple: {
Subject: { Data: 'Welcome to SMTPedia' },
Body: { Text: { Data: 'Thanks for signing up.' } }
}
}
}));
console.log('Email sent.');
}
main().catch(err => { console.error('SES error:', err); process.exit(1); });
SES SMTP requires a distinct IAM SMTP username/password pair, generated in the SES console or derived from an IAM user’s AWS secret via HMAC-SHA256. Using the raw AWS access key ID + secret against the SMTP endpoint will always return 535 authentication failed. The API side has the reverse trap: agents pointed at email.us-east-1.amazonaws.com using an SMTP password will fail SigV4 signing every time.
The SMTP password is derived using the region string as part of the signing key. Credentials generated for us-east-1 will fail against email-smtp.eu-west-1.amazonaws.com. Regenerate a separate set of SMTP credentials for every region you send from, and store them per-region in your secret manager — a single “SES password” env var is the wrong shape for a multi-region deployment.
New accounts (and each new region) start in the SES sandbox: 200 messages per 24 h, 1 msg/sec, and you can only send to verified addresses. Production access requires a support case describing use case, expected volume, and bounce/complaint handling — approvals typically take about 24 h but can be denied or partially granted. Automate the sandbox check into your bootstrap script (GetAccount exposes the flag).
SES puts accounts under review at a 5% bounce rate or 0.1% complaint rate and can pause sending at 10% bounces or 0.5% complaints. You must process bounce/complaint SNS notifications, honour the account-level suppression list, and never re-mail hard bounces. Wire an SNS or EventBridge event destination on day one, not after the first pause.
Amazon throttles outbound TCP 25 on EC2 instances by default. Use port 587 or 2587 for STARTTLS, or 465 / 2465 for TLS-wrapper, or file a “Request to Remove Email Sending Limitations” support case. Applications hard-coded to port 25 will silently time out on EC2 with no clear error — this is one of the most common false “SES is down” reports.
isBotEvent field in Open and Click event notifications, letting customers distinguish automated (MPP, security scanners) opens from real human engagement without extra configuration.Track future changes in the Amazon SES Developer Guide document history and the AWS Messaging & Targeting What’s New feed.
SES v2 (API version 2019-09-27, service id sesv2) is the current REST/JSON surface and receives all new features (contact lists, VDM, event destinations v2, dedicated IP pools API). SES v1 is the legacy Query API kept for backward compatibility; it caps raw messages at 10 MB versus 40 MB on v2 and lacks the newer resources. New integrations should target v2 (@aws-sdk/client-sesv2, boto3 client sesv2, AWSSDK.SimpleEmailV2).
Every HTTPS call is signed with AWS Signature Version 4 (SigV4). Credentials can come from a long-lived IAM user access key + secret, from an IAM role assumed via EC2 instance profile / ECS task role / EKS IRSA / Lambda execution role, or from short-lived STS credentials issued by IAM Identity Center. The SMTP interface uses a separate IAM SMTP username/password pair (AUTH LOGIN over TLS) derived from an IAM user secret — not the raw AWS access key.
SES v2 list operations use a cursor called NextToken. Send PageSize on the first request; if a NextToken is present in the response, pass it back on the next call to fetch the following page. Stop when the response omits NextToken. All official SDKs expose a paginator helper (ses.get_paginator('list_email_identities') in boto3, paginateListEmailIdentities in AWS SDK for JavaScript v3).
A single SendEmail or SMTP submission accepts up to 50 recipients combined across To/Cc/Bcc, and a raw payload up to 40 MB after base64 encoding. Beyond 10 MB, SES additionally applies a bandwidth ceiling of about 40 MB/s per connection. For fan-out to more than 50 addresses in one call, use SendBulkEmail against a template with up to 50 Destinations entries.
AWS ships and maintains SES v2 clients in Python (boto3, client sesv2), JavaScript/TypeScript (@aws-sdk/client-sesv2), Java (software.amazon.awssdk:sesv2), Go (aws-sdk-go-v2/service/sesv2), .NET (AWSSDK.SimpleEmailV2), PHP (SesV2Client), Ruby (aws-sdk-sesv2), Rust (aws-sdk-sesv2), and Kotlin (aws.sdk.kotlin:sesv2), plus the AWS CLI v2 (aws sesv2). All are actively maintained against the current sesv2 Smithy model.
No production-grade, first-party SES MCP server exists as of August 2026. AWS publishes aws-samples/sample-for-amazon-ses-mcp, but it is explicitly a sample, not for production. Agents wanting SES today can use the general-purpose AWS MCP Server (GA May 2026), a community wrapper like omd01/aws-ses-mcp, or expose SES through Zapier MCP. Contrast with Postmark and Mailgun — also community-only in 2026.
This review follows our email infrastructure testing methodology. We disclose affiliate relationships in our editorial independence policy.