AWS.SES reference
AccountSettings
Section titled “AccountSettings”Source:
src/AWS/SES/AccountSettings.ts
Account-level Amazon SES v2 settings — an account/region singleton that manages account-wide sending status, the account suppression list, and Virtual Deliverability Manager (VDM) configuration.
Only the aspects you specify are managed: omit sendingEnabled, suppression,
or vdm to leave that setting untouched.
Deleting this resource is a no-op — it leaves the account settings exactly as they are. Unlike a normal resource there is nothing to tear down: these are account-global toggles with no single safe default, and resetting them (e.g. disabling sending or clearing the suppression list) would affect live mail beyond this stack. Change the props and re-deploy to adjust them.
AccountSettings: Configuring the Account
Section titled “AccountSettings: Configuring the Account”Enable VDM with Engagement Tracking
import * as SES from "alchemy/AWS/SES";
const settings = yield* SES.AccountSettings("Account", { vdm: { enabled: "ENABLED", dashboardEngagementMetrics: "ENABLED", },});Configure the Suppression List
const settings = yield* SES.AccountSettings("Account", { suppression: { reasons: ["BOUNCE", "COMPLAINT"] },});Enable Guardian Optimized Shared Delivery
const settings = yield* SES.AccountSettings("Account", { vdm: { enabled: "ENABLED", guardianOptimizedSharedDelivery: "ENABLED", },});AccountSettings: Pausing Account-Wide Sending
Section titled “AccountSettings: Pausing Account-Wide Sending”Stop All Sending for the Account
// WARNING: this halts every outbound email in the account, including mail// sent by stacks and systems outside this one. Prefer a configuration// set's sendingEnabled to pause a single sending path.const settings = yield* SES.AccountSettings("Account", { sendingEnabled: false,});Manage Only VDM and Leave Sending Alone
// Omitted aspects are never touched — this deploy will not read or write// the account's sending status or suppression list.const settings = yield* SES.AccountSettings("Account", { vdm: { enabled: "ENABLED" },});ActiveReceiptRuleSet
Section titled “ActiveReceiptRuleSet”Source:
src/AWS/SES/ActiveReceiptRuleSet.ts
The account’s active Amazon SES receipt rule set — the single rule set (per account, per region) that SES actually evaluates against inbound mail.
This is an account-level singleton pointer, not a container: only one rule set can be active at a time. Deleting this resource deactivates email receiving (clears the pointer) only when the account is still pointed at the rule set this resource set; if something else has since become active, the delete is a no-op.
ActiveReceiptRuleSet: Activating a Rule Set
Section titled “ActiveReceiptRuleSet: Activating a Rule Set”import * as SES from "alchemy/AWS/SES";
const ruleSet = yield* SES.ReceiptRuleSet("Inbound", {});const active = yield* SES.ActiveReceiptRuleSet("Active", { ruleSetName: ruleSet.ruleSetName,});BatchGetMetricData
Section titled “BatchGetMetricData”Source:
src/AWS/SES/BatchGetMetricData.ts
Runtime binding for sesv2:BatchGetMetricData.
Fetches up to 10 aggregated deliverability metric time-series in one call —
sends, deliveries, bounces, complaints, opens, clicks — over a date range,
optionally sliced by dimension (ISP, configuration set, etc.). Requires VDM
(Virtual Deliverability Manager) to be enabled; a malformed query surfaces
the typed BadRequestException. Account-level operation. Provide the
implementation with Effect.provide(AWS.SES.BatchGetMetricDataHttp).
BatchGetMetricData: Deliverability Insights
Section titled “BatchGetMetricData: Deliverability Insights”// init — account-level binding, no resource argumentconst getMetrics = yield* SES.BatchGetMetricData();
// runtimeconst { Results } = yield* getMetrics({ Queries: [ { Id: "sends", Namespace: "VDM", Metric: "SEND", StartDate: new Date(Date.now() - 7 * 24 * 3600 * 1000), EndDate: new Date(), }, ],});ConfigurationSet
Section titled “ConfigurationSet”Source:
src/AWS/SES/ConfigurationSet.ts
An Amazon SES v2 configuration set — a named group of sending options (TLS policy, reputation metrics, suppression overrides) that you apply to outbound email, either per-message or as an identity’s default.
Attach event destinations with SES.ConfigurationSetEventDestination to
stream send/delivery/bounce/complaint events to SNS, EventBridge, or
CloudWatch.
ConfigurationSet: Creating Configuration Sets
Section titled “ConfigurationSet: Creating Configuration Sets”Basic Configuration Set
import * as SES from "alchemy/AWS/SES";
const configSet = yield* SES.ConfigurationSet("Default", {});Require TLS and Publish Reputation Metrics
const configSet = yield* SES.ConfigurationSet("Strict", { tlsPolicy: "REQUIRE", reputationMetricsEnabled: true,});Suppress Bounces and Complaints
const configSet = yield* SES.ConfigurationSet("Suppressing", { suppressedReasons: ["BOUNCE", "COMPLAINT"],});ConfigurationSet: Open and Click Tracking
Section titled “ConfigurationSet: Open and Click Tracking”// The redirect domain must be a verified subdomain you own with a valid// certificate. Omit `tracking` entirely to keep SES's current setting.const configSet = yield* SES.ConfigurationSet("Tracked", { tracking: { customRedirectDomain: "links.example.com", httpsPolicy: "REQUIRE", },});ConfigurationSet: Virtual Deliverability Manager
Section titled “ConfigurationSet: Virtual Deliverability Manager”// Requires account-level VDM — see SES.AccountSettings.const configSet = yield* SES.ConfigurationSet("Measured", { vdm: { dashboardEngagementMetrics: "ENABLED", guardianOptimizedSharedDelivery: "ENABLED", },});ConfigurationSet: Event Destinations
Section titled “ConfigurationSet: Event Destinations”const topic = yield* SNS.Topic("EmailEvents", {});const destination = yield* SES.ConfigurationSetEventDestination("ToSns", { configurationSetName: configSet.configurationSetName, matchingEventTypes: ["SEND", "DELIVERY", "BOUNCE", "COMPLAINT"], snsDestination: { topicArn: topic.topicArn },});ConfigurationSetEventDestination
Section titled “ConfigurationSetEventDestination”Source:
src/AWS/SES/ConfigurationSetEventDestination.ts
An event destination on an SES v2 configuration set — streams send/delivery/bounce/complaint (and open/click) events to SNS, EventBridge, or CloudWatch.
ConfigurationSetEventDestination: Creating Event Destinations
Section titled “ConfigurationSetEventDestination: Creating Event Destinations”Publish Bounce and Complaint Events to SNS
import * as SES from "alchemy/AWS/SES";import * as SNS from "alchemy/AWS/SNS";
const topic = yield* SNS.Topic("EmailEvents", {});const configSet = yield* SES.ConfigurationSet("Default", {});
const destination = yield* SES.ConfigurationSetEventDestination("ToSns", { configurationSetName: configSet.configurationSetName, matchingEventTypes: ["BOUNCE", "COMPLAINT"], snsDestination: { topicArn: topic.topicArn },});Publish Metrics to CloudWatch
const metrics = yield* SES.ConfigurationSetEventDestination("Metrics", { configurationSetName: configSet.configurationSetName, matchingEventTypes: ["SEND", "DELIVERY"], cloudWatchDestination: { dimensionConfigurations: [ { dimensionName: "campaign", dimensionValueSource: "MESSAGE_TAG", defaultDimensionValue: "none", }, ], },});Contact
Section titled “Contact”Source:
src/AWS/SES/Contact.ts
An Amazon SES v2 contact — a single email address on a SES.ContactList,
with its own topic subscription preferences and unsubscribe state.
Contact: Adding Contacts
Section titled “Contact: Adding Contacts”Basic Contact
import * as SES from "alchemy/AWS/SES";
const list = yield* SES.ContactList("Newsletter", {});const contact = yield* SES.Contact("Subscriber", { contactListName: list.contactListName, emailAddress: "reader@example.com",});Contact with Topic Preferences
const contact = yield* SES.Contact("Subscriber", { contactListName: list.contactListName, emailAddress: "reader@example.com", topicPreferences: [ { TopicName: "product-updates", SubscriptionStatus: "OPT_IN" }, { TopicName: "promotions", SubscriptionStatus: "OPT_OUT" }, ],});Unsubscribe a Contact from Everything
// unsubscribeAll overrides every per-topic preference.const contact = yield* SES.Contact("Subscriber", { contactListName: list.contactListName, emailAddress: "reader@example.com", unsubscribeAll: true,});Contact: Application Metadata
Section titled “Contact: Application Metadata”// Serialized to the JSON string SES stores; re-ordering the keys is not a// change, so this does not churn on every deploy.const contact = yield* SES.Contact("Subscriber", { contactListName: list.contactListName, emailAddress: "reader@example.com", attributes: { plan: "pro", signupSource: "docs" },});ContactList
Section titled “ContactList”Source:
src/AWS/SES/ContactList.ts
An Amazon SES v2 contact list — a named audience of email contacts with subscription topics, used with SES’s list-management and unsubscribe handling.
Add contacts with SES.Contact. Deleting the list deletes all of its
contacts.
SES allows only one contact list per AWS account, so renaming a list replaces it by deleting the old list (and its contacts) before creating the new one — a create-then-delete replacement would exceed the account limit.
ContactList: Creating Contact Lists
Section titled “ContactList: Creating Contact Lists”Basic Contact List
import * as SES from "alchemy/AWS/SES";
const list = yield* SES.ContactList("Newsletter", { description: "Weekly product newsletter",});Contact List with Topics
const list = yield* SES.ContactList("Newsletter", { topics: [ { TopicName: "product-updates", DisplayName: "Product Updates", DefaultSubscriptionStatus: "OPT_IN", }, { TopicName: "promotions", DisplayName: "Promotions", DefaultSubscriptionStatus: "OPT_OUT", }, ],});Contact List with Tags
const list = yield* SES.ContactList("Newsletter", { tags: { Team: "growth", Environment: "prod" },});ContactList: Populating the List
Section titled “ContactList: Populating the List”const list = yield* SES.ContactList("Newsletter", { topics: [ { TopicName: "product-updates", DisplayName: "Product Updates", DefaultSubscriptionStatus: "OPT_IN", }, ],});
for (const email of ["a@example.com", "b@example.com"]) { yield* SES.Contact(`Subscriber-${email}`, { contactListName: list.contactListName, emailAddress: email, topicPreferences: [ { TopicName: "product-updates", SubscriptionStatus: "OPT_IN" }, ], });}CustomVerificationEmailTemplate
Section titled “CustomVerificationEmailTemplate”Source:
src/AWS/SES/CustomVerificationEmailTemplate.ts
An Amazon SES v2 custom verification email template — the branded email SES
sends when you verify a new email-address identity via
SendCustomVerificationEmail.
Creating, reading, updating, and deleting the template works on any account. Actually sending a custom verification email requires the account to be out of the SES sandbox (production access).
CustomVerificationEmailTemplate: Creating Templates
Section titled “CustomVerificationEmailTemplate: Creating Templates”Branded Verification Email
import * as SES from "alchemy/AWS/SES";
const template = yield* SES.CustomVerificationEmailTemplate("Verify", { fromEmailAddress: "verify@example.com", templateSubject: "Please confirm your email", templateContent: "<html><body>Click the link to verify your address.</body></html>", successRedirectionURL: "https://example.com/verified", failureRedirectionURL: "https://example.com/verify-failed",});Explicit Template Name
// Without templateName a deterministic name is derived from app/stage/id.const template = yield* SES.CustomVerificationEmailTemplate("Verify", { templateName: "onboarding-verification", fromEmailAddress: "verify@example.com", templateSubject: "Please confirm your email", templateContent: "<html><body>Click the link to verify your address.</body></html>", successRedirectionURL: "https://example.com/verified", failureRedirectionURL: "https://example.com/verify-failed",});CustomVerificationEmailTemplate: Sending the Verification Email
Section titled “CustomVerificationEmailTemplate: Sending the Verification Email”// init — account-level binding, no resource argumentconst sendVerification = yield* SES.SendCustomVerificationEmail();
// runtime — SES emails the branded template to the address, and the// address becomes a verified identity once the recipient clicks through.const { MessageId } = yield* sendVerification({ EmailAddress: "new-user@example.com", TemplateName: yield* template.templateName,});DedicatedIpPool
Section titled “DedicatedIpPool”Source:
src/AWS/SES/DedicatedIpPool.ts
An Amazon SES v2 dedicated IP pool — a named group of dedicated IP addresses used to send email, so you can isolate the sending reputation of different kinds of mail (e.g. marketing vs. transactional).
STANDARD → MANAGED is an in-place scaling change. MANAGED → STANDARD
is not supported by AWS and replaces the pool.
DedicatedIpPool: Creating Pools
Section titled “DedicatedIpPool: Creating Pools”Standard Pool
import * as SES from "alchemy/AWS/SES";
const pool = yield* SES.DedicatedIpPool("Marketing", { scalingMode: "STANDARD",});Managed Pool
const pool = yield* SES.DedicatedIpPool("Transactional", { scalingMode: "MANAGED",});Explicit Pool Name
// Without poolName a deterministic lowercase name is derived from// app/stage/id. Pool names allow lowercase letters, numbers, and dashes.const pool = yield* SES.DedicatedIpPool("Marketing", { poolName: "acme-marketing",});DedicatedIpPool: Changing the Scaling Mode
Section titled “DedicatedIpPool: Changing the Scaling Mode”// STANDARD -> MANAGED is applied in place — the pool keeps its name and// its dedicated IPs.const pool = yield* SES.DedicatedIpPool("Marketing", { scalingMode: "MANAGED", // was "STANDARD"});
// MANAGED -> STANDARD has no AWS API, so it REPLACES the pool: a new pool// is created and the old one deleted, dropping its dedicated IPs.DedicatedIpPool: Isolating Reputation
Section titled “DedicatedIpPool: Isolating Reputation”// Give each kind of mail its own pool so a marketing reputation hit// cannot take down password resets.const marketing = yield* SES.DedicatedIpPool("Marketing", { scalingMode: "STANDARD",});const transactional = yield* SES.DedicatedIpPool("Transactional", { scalingMode: "MANAGED",});DeleteSuppressedDestination
Section titled “DeleteSuppressedDestination”Source:
src/AWS/SES/DeleteSuppressedDestination.ts
Runtime binding for sesv2:DeleteSuppressedDestination.
Removes an email address from the account-level suppression list — e.g.
after a recipient re-subscribes or a bounce is resolved. Fails with the
typed NotFoundException tag when the address is not on the list.
Account-level operation. Provide the implementation with
Effect.provide(AWS.SES.DeleteSuppressedDestinationHttp).
DeleteSuppressedDestination: Suppression List
Section titled “DeleteSuppressedDestination: Suppression List”// init — account-level binding, no resource argumentconst unsuppress = yield* SES.DeleteSuppressedDestination();
// runtimeyield* unsuppress({ EmailAddress: "resubscribed@example.com" }).pipe( Effect.catchTag("NotFoundException", () => Effect.void),);EmailIdentity
Section titled “EmailIdentity”Source:
src/AWS/SES/EmailIdentity.ts
An Amazon SES v2 email identity — a verified email address or domain that you send email from.
Creating the identity starts verification: email-address identities receive
a verification email, and domain identities get Easy DKIM tokens (exposed
as the dkimTokens attribute) to publish as CNAME records. The identity
is usable for sending once verificationStatus is SUCCESS.
EmailIdentity: Creating Identities
Section titled “EmailIdentity: Creating Identities”Domain Identity
import * as SES from "alchemy/AWS/SES";
const identity = yield* SES.EmailIdentity("Sender", { emailIdentity: "mail.example.com",});// publish identity.dkimTokens as CNAME records to verifyEmail Address Identity
const identity = yield* SES.EmailIdentity("Sender", { emailIdentity: "hello@example.com",});// SES emails hello@example.com a verification linkEmailIdentity: Configuration Set Association
Section titled “EmailIdentity: Configuration Set Association”const configSet = yield* SES.ConfigurationSet("Tracking", {});const identity = yield* SES.EmailIdentity("Sender", { emailIdentity: "mail.example.com", configurationSetName: configSet.configurationSetName,});EmailIdentity: DKIM and Feedback
Section titled “EmailIdentity: DKIM and Feedback”Turn Easy DKIM Signing Off
// Omit the prop entirely to leave SES's current setting alone.const identity = yield* SES.EmailIdentity("Sender", { emailIdentity: "mail.example.com", dkimSigningEnabled: false,});Stop Forwarding Bounces and Complaints by Email
// Turn this off once a configuration set event destination is handling// bounces and complaints, so they stop arriving as mail.const identity = yield* SES.EmailIdentity("Sender", { emailIdentity: "mail.example.com", feedbackForwardingEnabled: false,});EmailIdentity: Custom MAIL FROM Domain
Section titled “EmailIdentity: Custom MAIL FROM Domain”// mailFromDomain must be a subdomain of the identity, and needs MX and// SPF records published before SES will use it.const identity = yield* SES.EmailIdentity("Sender", { emailIdentity: "mail.example.com", mailFromDomain: "bounce.mail.example.com", // Reject rather than silently falling back to the SES default when the // MX record cannot be read. mailFromBehaviorOnMxFailure: "REJECT_MESSAGE",});EmailIdentity: Sending Email at Runtime
Section titled “EmailIdentity: Sending Email at Runtime”// initconst sendEmail = yield* SES.SendEmail(identity);
// runtimeconst result = yield* sendEmail({ FromEmailAddress: "hello@mail.example.com", Destination: { ToAddresses: ["customer@example.com"] }, Content: { Simple: { Subject: { Data: "Welcome!" }, Body: { Text: { Data: "Hello from SES." } }, }, },});EmailIdentityPolicy
Section titled “EmailIdentityPolicy”Source:
src/AWS/SES/EmailIdentityPolicy.ts
An Amazon SES v2 sending-authorization policy attached to an email identity — lets the identity owner authorize other AWS accounts or IAM principals to send email using the identity.
SES stores the policy document as JSON; Alchemy serializes the typed IAM policy at the API boundary and compares its normalized content for drift.
EmailIdentityPolicy: Attaching a Policy
Section titled “EmailIdentityPolicy: Attaching a Policy”Authorize Another Account to Send
import * as SES from "alchemy/AWS/SES";
const identity = yield* SES.EmailIdentity("Sender", { emailIdentity: "mail.example.com",});
const policy = yield* SES.EmailIdentityPolicy("AllowPartner", { emailIdentity: identity.emailIdentity, policy: { Version: "2012-10-17", Statement: [ { Effect: "Allow", Principal: { AWS: "arn:aws:iam::111122223333:root" }, Action: ["ses:SendEmail"], Resource: identity.identityArn, }, ], },});Explicit Policy Name
// Without policyName a deterministic name is derived from app/stage/id.const policy = yield* SES.EmailIdentityPolicy("AllowPartner", { emailIdentity: identity.emailIdentity, policyName: "partner-send", policy: { Version: "2012-10-17", Statement: [ { Effect: "Allow", Principal: { AWS: "arn:aws:iam::111122223333:root" }, Action: ["ses:SendEmail"], Resource: identity.identityArn, }, ], },});Restrict the Grant with Conditions
const policy = yield* SES.EmailIdentityPolicy("AllowPartnerScoped", { emailIdentity: identity.emailIdentity, policy: { Version: "2012-10-17", Statement: [ { Effect: "Allow", Principal: { AWS: "arn:aws:iam::111122223333:root" }, Action: ["ses:SendEmail", "ses:SendRawEmail"], Resource: identity.identityArn, Condition: { StringEquals: { "ses:FromAddress": "noreply@mail.example.com" }, }, }, ], },});Several Policies on One Identity
// Each policy is a separate resource keyed by its own name.for (const partner of ["111122223333", "444455556666"]) { yield* SES.EmailIdentityPolicy(`Allow${partner}`, { emailIdentity: identity.emailIdentity, policyName: `partner-${partner}`, policy: { Version: "2012-10-17", Statement: [ { Effect: "Allow", Principal: { AWS: `arn:aws:iam::${partner}:root` }, Action: ["ses:SendEmail"], Resource: identity.identityArn, }, ], }, });}EmailTemplate
Section titled “EmailTemplate”Source:
src/AWS/SES/EmailTemplate.ts
An Amazon SES v2 email template — reusable subject/text/HTML content with
{{variable}} personalization tags, rendered server-side when you send
templated email.
EmailTemplate: Creating Templates
Section titled “EmailTemplate: Creating Templates”import * as SES from "alchemy/AWS/SES";
const template = yield* SES.EmailTemplate("Welcome", { subject: "Welcome, {{name}}!", text: "Hi {{name}}, thanks for signing up.", html: "<h1>Hi {{name}}</h1><p>Thanks for signing up.</p>",});EmailTemplate: Sending Templated Email
Section titled “EmailTemplate: Sending Templated Email”const sendEmail = yield* SES.SendEmail(identity);
const result = yield* sendEmail({ Destination: { ToAddresses: ["customer@example.com"] }, Content: { Template: { TemplateName: "my-welcome-template", TemplateData: JSON.stringify({ name: "Ada" }), }, },});GetAccount
Section titled “GetAccount”Source:
src/AWS/SES/GetAccount.ts
Runtime binding for sesv2:GetAccount.
Retrieves the SES account’s sending status in the current region — the
send quota, whether sending is enabled, and whether the account has
production access (or is still in the sandbox). Useful to check remaining
quota before a large send. Account-level operation — invoked with no
arguments. Provide the implementation with
Effect.provide(AWS.SES.GetAccountHttp).
GetAccount: Account Status
Section titled “GetAccount: Account Status”// init — account-level binding, no resource argumentconst getAccount = yield* SES.GetAccount();
// runtimeconst account = yield* getAccount();// account.SendQuota?.Max24HourSend, account.ProductionAccessEnabledGetBlacklistReports
Section titled “GetBlacklistReports”Source:
src/AWS/SES/GetBlacklistReports.ts
Runtime binding for sesv2:GetBlacklistReports.
Retrieves, per dedicated-IP address, the list of anti-spam blacklists (RBLs)
that IP currently appears on, with the observation time. Useful for a
reputation dashboard over the account’s dedicated IPs. Account-level
operation. Provide the implementation with
Effect.provide(AWS.SES.GetBlacklistReportsHttp).
GetBlacklistReports: Deliverability Insights
Section titled “GetBlacklistReports: Deliverability Insights”// init — account-level binding, no resource argumentconst getBlacklists = yield* SES.GetBlacklistReports();
// runtime — the dedicated IP addresses to checkconst { BlacklistReport } = yield* getBlacklists({ BlacklistItemNames: ["192.0.2.1"],});GetDomainStatisticsReport
Section titled “GetDomainStatisticsReport”Source:
src/AWS/SES/GetDomainStatisticsReport.ts
Runtime binding for sesv2:GetDomainStatisticsReport.
Retrieves inbox-placement and engagement statistics for a domain identity
over a date range — the data behind the SES deliverability dashboard.
Requires the deliverability dashboard subscription; an unknown domain
surfaces the typed NotFoundException. Account-level operation. Provide the
implementation with Effect.provide(AWS.SES.GetDomainStatisticsReportHttp).
GetDomainStatisticsReport: Deliverability Insights
Section titled “GetDomainStatisticsReport: Deliverability Insights”// init — account-level binding, no resource argumentconst getReport = yield* SES.GetDomainStatisticsReport();
// runtimeconst report = yield* getReport({ Domain: "example.com", StartDate: new Date(Date.now() - 7 * 24 * 3600 * 1000), EndDate: new Date(),});GetMessageInsights
Section titled “GetMessageInsights”Source:
src/AWS/SES/GetMessageInsights.ts
Runtime binding for sesv2:GetMessageInsights.
Retrieves the delivery insights SES tracked for a single sent message —
the per-recipient event timeline (send, delivery, open, click, bounce,
complaint) plus the resolved subject, from-address, and tags. Requires VDM
(Virtual Deliverability Manager) to be enabled and the message to have been
sent within the retention window; an unknown message id fails with the
typed NotFoundException. Account-level operation. Provide the
implementation with Effect.provide(AWS.SES.GetMessageInsightsHttp).
GetMessageInsights: Deliverability Insights
Section titled “GetMessageInsights: Deliverability Insights”// init — account-level binding, no resource argumentconst getInsights = yield* SES.GetMessageInsights();
// runtime — MessageId returned by a prior SendEmailconst { Insights } = yield* getInsights({ MessageId: messageId });GetSuppressedDestination
Section titled “GetSuppressedDestination”Source:
src/AWS/SES/GetSuppressedDestination.ts
Runtime binding for sesv2:GetSuppressedDestination.
Retrieves a specific address from the account-level suppression list —
check whether (and why) an address is suppressed before attempting a
send. Fails with the typed NotFoundException tag when the address is
not on the list. Account-level operation. Provide the implementation with
Effect.provide(AWS.SES.GetSuppressedDestinationHttp).
GetSuppressedDestination: Suppression List
Section titled “GetSuppressedDestination: Suppression List”// init — account-level binding, no resource argumentconst getSuppressed = yield* SES.GetSuppressedDestination();
// runtimeconst { SuppressedDestination } = yield* getSuppressed({ EmailAddress: "bouncing@example.com",});// SuppressedDestination.Reason — "BOUNCE" | "COMPLAINT"ListSuppressedDestinations
Section titled “ListSuppressedDestinations”Source:
src/AWS/SES/ListSuppressedDestinations.ts
Runtime binding for sesv2:ListSuppressedDestinations.
Lists the addresses on the account-level suppression list, optionally
filtered by reason and date range. Account-level operation. Provide the
implementation with
Effect.provide(AWS.SES.ListSuppressedDestinationsHttp).
ListSuppressedDestinations: Suppression List
Section titled “ListSuppressedDestinations: Suppression List”// init — account-level binding, no resource argumentconst listSuppressed = yield* SES.ListSuppressedDestinations();
// runtimeconst { SuppressedDestinationSummaries } = yield* listSuppressed({ Reasons: ["BOUNCE"],});MultiRegionEndpoint
Section titled “MultiRegionEndpoint”Source:
src/AWS/SES/MultiRegionEndpoint.ts
An Amazon SES v2 multi-region endpoint (global endpoint) — a single sending endpoint that splits email traffic across a primary region (where the endpoint is created) and one or more secondary regions, improving resilience and deliverability.
There is no update API, so any change to the name or routes replaces the endpoint.
MultiRegionEndpoint: Creating Endpoints
Section titled “MultiRegionEndpoint: Creating Endpoints”Two-Region Endpoint
import * as SES from "alchemy/AWS/SES";
// The primary region is wherever the stack deploys; the route adds a// secondary region.const endpoint = yield* SES.MultiRegionEndpoint("Global", { regions: ["eu-west-1"],});Three-Region Endpoint
// Traffic is split across the primary region plus every listed route.const endpoint = yield* SES.MultiRegionEndpoint("Global", { regions: ["eu-west-1", "ap-southeast-2"],});Explicit Endpoint Name
const endpoint = yield* SES.MultiRegionEndpoint("Global", { endpointName: "acme-global", regions: ["eu-west-1"],});MultiRegionEndpoint: Waiting for READY
Section titled “MultiRegionEndpoint: Waiting for READY”import * as sesv2 from "@distilled.cloud/aws/sesv2";import * as Effect from "effect/Effect";import * as Schedule from "effect/Schedule";
const endpoint = yield* SES.MultiRegionEndpoint("Global", { regions: ["eu-west-1"],});
// Reconcile returns as soon as SES accepts the create, so status is// usually CREATING. Poll yourself when you need to block on readiness.const ready = yield* sesv2 .getMultiRegionEndpoint({ EndpointName: yield* endpoint.endpointName }) .pipe( Effect.repeat({ schedule: Schedule.spaced("30 seconds"), until: (r) => r.Status === "READY", times: 40, }), );PutSuppressedDestination
Section titled “PutSuppressedDestination”Source:
src/AWS/SES/PutSuppressedDestination.ts
Runtime binding for sesv2:PutSuppressedDestination.
Adds an email address to the account-level suppression list — the
data-plane half of bounce/complaint handling: consume feedback events and
suppress the offending address so SES never attempts it again.
Account-level operation. Provide the implementation with
Effect.provide(AWS.SES.PutSuppressedDestinationHttp).
PutSuppressedDestination: Suppression List
Section titled “PutSuppressedDestination: Suppression List”// init — account-level binding, no resource argumentconst suppress = yield* SES.PutSuppressedDestination();
// runtimeyield* suppress({ EmailAddress: "bouncing@example.com", Reason: "BOUNCE",});ReceiptFilter
Section titled “ReceiptFilter”Source:
src/AWS/SES/ReceiptFilter.ts
An Amazon SES receipt IP address filter — an account-level allow/block rule for the source IP of inbound mail. Block filters take precedence over allow filters.
Filters are immutable: there is no update API, so any change to the name or the IP rule replaces the filter.
ReceiptFilter: Creating Filters
Section titled “ReceiptFilter: Creating Filters”Block a CIDR Range
import * as SES from "alchemy/AWS/SES";
const filter = yield* SES.ReceiptFilter("BlockBadActors", { ipFilter: { policy: "Block", cidr: "10.0.0.0/24" },});Allow a Single Address
const filter = yield* SES.ReceiptFilter("AllowPartner", { ipFilter: { policy: "Allow", cidr: "192.0.2.10" },});ReceiptRule
Section titled “ReceiptRule”Source:
src/AWS/SES/ReceiptRule.ts
An Amazon SES receipt rule — a matcher plus an ordered list of actions that
SES applies to inbound email received through the parent
SES.ReceiptRuleSet.
Actions are passed as the raw distilled action shapes (no marshalling): the caller supplies bucket names, topic ARNs, and function ARNs directly.
ReceiptRule: Creating Rules
Section titled “ReceiptRule: Creating Rules”Deliver Matching Mail to S3
import * as SES from "alchemy/AWS/SES";
const ruleSet = yield* SES.ReceiptRuleSet("Inbound", {});const rule = yield* SES.ReceiptRule("ToBucket", { ruleSetName: ruleSet.ruleSetName, recipients: ["support@example.com"], actions: [ { S3Action: { BucketName: "my-inbound-mail" } }, ],});Invoke a Lambda and Add a Header
const rule = yield* SES.ReceiptRule("Process", { ruleSetName: ruleSet.ruleSetName, tlsPolicy: "Require", scanEnabled: true, actions: [ { AddHeaderAction: { HeaderName: "X-Inbound", HeaderValue: "ses" } }, { LambdaAction: { FunctionArn: fn.functionArn, InvocationType: "Event" } }, ],});ReceiptRule: Ordering Rules
Section titled “ReceiptRule: Ordering Rules”// A BounceAction's Sender must be a verified SES identity — SES rejects the// rule with IdentityNotVerified at create/update time otherwise.const first = yield* SES.ReceiptRule("First", { ruleSetName: ruleSet.ruleSetName, actions: [{ StopAction: { Scope: "RuleSet" } }],});const second = yield* SES.ReceiptRule("Second", { ruleSetName: ruleSet.ruleSetName, after: first.ruleName, actions: [{ BounceAction: { SmtpReplyCode: "550", Message: "Mailbox does not exist", Sender: "mailer-daemon@example.com", } }],});ReceiptRuleSet
Section titled “ReceiptRuleSet”Source:
src/AWS/SES/ReceiptRuleSet.ts
An Amazon SES receipt rule set — the ordered container for the receipt rules that decide what happens to inbound email (deliver to S3, invoke a Lambda, publish to SNS, bounce, etc.).
A rule set is an empty container on creation; add SES.ReceiptRules to it
and point the account at it with SES.ActiveReceiptRuleSet to start
processing mail. Email receiving is only available in a subset of regions
(e.g. us-east-1, us-west-2, eu-west-1).
ReceiptRuleSet: Creating Rule Sets
Section titled “ReceiptRuleSet: Creating Rule Sets”Basic Rule Set
import * as SES from "alchemy/AWS/SES";
const ruleSet = yield* SES.ReceiptRuleSet("Inbound", {});Named Rule Set
const ruleSet = yield* SES.ReceiptRuleSet("Inbound", { ruleSetName: "my-inbound-rules",});ReceiptRuleSet: Activating a Rule Set
Section titled “ReceiptRuleSet: Activating a Rule Set”const ruleSet = yield* SES.ReceiptRuleSet("Inbound", {});yield* SES.ActiveReceiptRuleSet("Active", { ruleSetName: ruleSet.ruleSetName,});RenderEmailTemplate
Section titled “RenderEmailTemplate”Source:
src/AWS/SES/RenderEmailTemplate.ts
Runtime binding for sesv2:TestRenderEmailTemplate.
Bind this operation to an EmailTemplate inside a function runtime to get
a callable that renders the template server-side with the given
personalization data — useful for previews and for validating template
data before a send. The binding grants the function
ses:TestRenderEmailTemplate scoped to the template.
RenderEmailTemplate: Rendering Templates
Section titled “RenderEmailTemplate: Rendering Templates”// initconst renderTemplate = yield* SES.RenderEmailTemplate(template);
// runtimeconst { RenderedTemplate } = yield* renderTemplate({ TemplateData: JSON.stringify({ name: "Ada" }),});SendBounce
Section titled “SendBounce”Source:
src/AWS/SES/SendBounce.ts
Runtime binding for ses:SendBounce (classic SES).
Generates and sends a bounce message to the sender of an email you received
through SES receiving — the data-plane counterpart to SES.ReceiptRule
inbound processing. You can only bounce a message within 24 hours of
receiving it, and only for mail SES actually received.
Account-level operation with no resource-level IAM scoping. Provide the
implementation with Effect.provide(AWS.SES.SendBounceHttp).
SendBounce: Bouncing Inbound Mail
Section titled “SendBounce: Bouncing Inbound Mail”// init — account-level binding, no resource argumentconst sendBounce = yield* SES.SendBounce();
// runtime — inside the Lambda that a ReceiptRule invokesyield* sendBounce({ OriginalMessageId: messageId, BounceSender: "mailer-daemon@example.com", BouncedRecipientInfoList: [ { Recipient: "nobody@example.com", BounceType: "DoesNotExist", }, ],});SendBulkEmail
Section titled “SendBulkEmail”Source:
src/AWS/SES/SendBulkEmail.ts
Runtime binding for sesv2:SendBulkEmail.
Bind this operation to an EmailIdentity (and optionally a
ConfigurationSet) inside a function runtime to get a callable that sends
one templated message to up to 50 destinations per call, with per-entry
replacement data. The binding grants the function the send actions scoped
to the identity, the account’s templates, and the configuration set.
Bulk sends always render a template — reference one via
DefaultContent.Template.
SendBulkEmail: Sending Email
Section titled “SendBulkEmail: Sending Email”// initconst sendBulkEmail = yield* SES.SendBulkEmail(identity, configSet);
// runtimeconst result = yield* sendBulkEmail({ DefaultContent: { Template: { TemplateName: "welcome-template", TemplateData: JSON.stringify({ name: "friend" }), }, }, BulkEmailEntries: [ { Destination: { ToAddresses: ["ada@example.com"] }, ReplacementEmailContent: { ReplacementTemplate: { ReplacementTemplateData: JSON.stringify({ name: "Ada" }), }, }, }, ],});// result.BulkEmailEntryResultsSendCustomVerificationEmail
Section titled “SendCustomVerificationEmail”Source:
src/AWS/SES/SendCustomVerificationEmail.ts
Runtime binding for sesv2:SendCustomVerificationEmail.
Sends the branded verification email defined by a
CustomVerificationEmailTemplate to a new email address, kicking off
the address-verification flow. Pass the template name and the address to
verify; SES takes the FROM address from the template.
Bind it to the identity the function is allowed to VERIFY — SES authorizes
this action against the identity of the address in the request, not against
the template’s FROM identity. Bind a domain to allow any address at it, or
a single address to allow exactly that one. Without a bound identity the
binding would let any holder send verification mail to arbitrary addresses.
The identity is not injected into the request. Optionally bind a
ConfigurationSet, which is injected into each request.
The identity may be a managed EmailIdentity or a plain reference —
{ emailIdentity: "signups.example.com" }. The reference form creates no
resource edge and takes no ownership, so it can scope the grant to an
identity the stack does not manage without a destroy ever deleting it.
Provide the implementation with
Effect.provide(AWS.SES.SendCustomVerificationEmailHttp).
Note: actually sending a custom verification email requires the account to
be out of the SES sandbox — in the sandbox the call fails with the typed
BadRequestException.
SendCustomVerificationEmail: Verifying Addresses
Section titled “SendCustomVerificationEmail: Verifying Addresses”Send a Custom Verification Email
// init — the function may verify addresses at this identity's domainconst sendVerification = yield* SES.SendCustomVerificationEmail(identity);
// runtimeconst { MessageId } = yield* sendVerification({ EmailAddress: "new-user@example.com", TemplateName: yield* template.templateName,});Scope to a Domain the Stack Does Not Manage
// Any address at signups.example.com may be verified. No resource edge and// no ownership — nothing is created, adopted, or destroyed.const sendVerification = yield* SES.SendCustomVerificationEmail({ emailIdentity: "signups.example.com",});Attribute the Send to a Configuration Set
// ConfigurationSetName is injected into every requestconst sendVerification = yield* SES.SendCustomVerificationEmail( identity, configSet,);SendEmail
Section titled “SendEmail”Source:
src/AWS/SES/SendEmail.ts
Runtime binding for sesv2:SendEmail.
Bind this operation to an EmailIdentity (and optionally a
ConfigurationSet) inside a function runtime to get a callable that sends
simple, raw, or templated email. The binding grants the function
ses:SendEmail (and the raw/templated variants) scoped to the identity,
the account’s templates, and the configuration set.
For an email-address identity, FromEmailAddress defaults to the identity
itself. For a domain identity, pass an address at the domain explicitly.
Note: while the account is in the SES sandbox, both the sender identity
must be verified and every recipient must be a verified identity or the
SES mailbox simulator (e.g. success@simulator.amazonses.com).
SendEmail: Sending Email
Section titled “SendEmail: Sending Email”Send a Simple Message
// initconst sendEmail = yield* SES.SendEmail(identity);
// runtimeconst result = yield* sendEmail({ Destination: { ToAddresses: ["success@simulator.amazonses.com"] }, Content: { Simple: { Subject: { Data: "Hello" }, Body: { Text: { Data: "Hello from SES." } }, }, },});// result.MessageIdSend Through a Configuration Set
const sendEmail = yield* SES.SendEmail(identity, configSet);Send a Templated Message
const result = yield* sendEmail({ Destination: { ToAddresses: ["customer@example.com"] }, Content: { Template: { TemplateName: "welcome-template", TemplateData: JSON.stringify({ name: "Ada" }), }, },});Tenant
Section titled “Tenant”Source:
src/AWS/SES/Tenant.ts
An Amazon SES v2 tenant — a logical container that groups related SES resources (email identities, configuration sets, templates) together, each with its own reputation metrics, sending status, and optional tenant-scoped suppression list. Useful for isolating email sending across customers or business units within a single SES account.
Associate resources with a tenant using SES.TenantResourceAssociation.
Deleting the tenant removes its resource associations but leaves the
underlying resources in place.
Tenant: Creating Tenants
Section titled “Tenant: Creating Tenants”Basic Tenant
import * as SES from "alchemy/AWS/SES";
const tenant = yield* SES.Tenant("CustomerA", {});Tenant with a Scoped Suppression List
// SES requires the reasons and the scope together, so they travel as one// prop rather than two independently-optional ones.const tenant = yield* SES.Tenant("CustomerA", { suppression: { reasons: ["BOUNCE", "COMPLAINT"], scope: "TENANT" },});Tenant with Tags
const tenant = yield* SES.Tenant("CustomerA", { tags: { Customer: "acme", CostCenter: "growth" },});Tenant: Associating Resources
Section titled “Tenant: Associating Resources”const tenant = yield* SES.Tenant("CustomerA", {});const identity = yield* SES.EmailIdentity("Sender", { emailIdentity: "mail.acme.example.com",});const configSet = yield* SES.ConfigurationSet("AcmeTracking", {});
// A resource must be associated before the tenant can send with it.yield* SES.TenantResourceAssociation("AcmeIdentity", { tenantName: tenant.tenantName, resourceArn: identity.identityArn,});yield* SES.TenantResourceAssociation("AcmeConfigSet", { tenantName: tenant.tenantName, resourceArn: configSet.configurationSetArn,});Tenant: Tenant Suppression Lists
Section titled “Tenant: Tenant Suppression Lists”// With scope "TENANT" the list is separate from the account's.const tenant = yield* SES.Tenant("CustomerA", { suppression: { reasons: ["BOUNCE", "COMPLAINT"], scope: "TENANT" },});
// init — account-level bindings, scoped per call via TenantNameconst suppress = yield* SES.PutSuppressedDestination();const listSuppressed = yield* SES.ListSuppressedDestinations();
// runtimeyield* suppress({ EmailAddress: "hard-bounce@example.com", Reason: "BOUNCE", TenantName: yield* tenant.tenantName,});const { SuppressedDestinationSummaries } = yield* listSuppressed({ TenantName: yield* tenant.tenantName,});TenantResourceAssociation
Section titled “TenantResourceAssociation”Source:
src/AWS/SES/TenantResourceAssociation.ts
An association between an Amazon SES v2 tenant and a resource — an email identity, configuration set, or email template. Once associated, the resource can be used when sending email on behalf of the tenant. A single resource can be associated with multiple tenants.
This is an existence-only link with no mutable properties: changing either the tenant or the resource replaces the association.
TenantResourceAssociation: Associating Resources
Section titled “TenantResourceAssociation: Associating Resources”Associate an Email Identity with a Tenant
import * as SES from "alchemy/AWS/SES";
const tenant = yield* SES.Tenant("CustomerA", {});const identity = yield* SES.EmailIdentity("Sender", { emailIdentity: "sender@example.com",});const association = yield* SES.TenantResourceAssociation("SenderLink", { tenantName: tenant.tenantName, resourceArn: identity.identityArn,});Associate a Configuration Set
const configSet = yield* SES.ConfigurationSet("AcmeTracking", {});yield* SES.TenantResourceAssociation("ConfigSetLink", { tenantName: tenant.tenantName, resourceArn: configSet.configurationSetArn,});Associate an Email Template
const template = yield* SES.EmailTemplate("Welcome", { subject: "Welcome, {{name}}!", text: "Thanks for signing up, {{name}}.",});yield* SES.TenantResourceAssociation("TemplateLink", { tenantName: tenant.tenantName, resourceArn: template.templateArn,});Share One Identity Across Two Tenants
// A resource can belong to any number of tenants.const acme = yield* SES.Tenant("Acme", {});const globex = yield* SES.Tenant("Globex", {});
yield* SES.TenantResourceAssociation("AcmeSender", { tenantName: acme.tenantName, resourceArn: identity.identityArn,});yield* SES.TenantResourceAssociation("GlobexSender", { tenantName: globex.tenantName, resourceArn: identity.identityArn,});