Bring your own AWS account (BYO-AWS)
Deploy the same SES / S3 / Lambda / SNS resources Mailmark uses - but inside your own AWS account, via a CloudFormation Quick-Create stack. Keep full ownership of your email data.
When to use BYO-AWS
For most users, the default Mailmark infrastructure option is simpler and recommended - Mailmark operates the AWS resources for you and you pay only the Mailmark subscription. Pick BYO-AWS when you need one of:
- Data residency - raw inbound email objects live in your own S3 bucket, in the AWS region you choose.
- Your own SES reputation - you already have a warmed-up SES account with production access approved.
- Regulatory / compliance - mailbox contents must be stored in an account you control.
- Consolidated billing - you prefer SES and S3 costs on your existing AWS bill.
You can mix and match: some domains on Mailmark infrastructure, others on BYO-AWS. The choice is per-domain.
What gets provisioned
The CloudFormation stack creates the following resources in your AWS account:
- SES configuration set + identity registration for your domain.
- S3 bucket for inbound email storage. Objects remain in your account.
- Lambda forwarder that picks up new S3 objects and POSTs them to the Mailmark inbound webhook, signed with a per-account shared secret.
- SNS topics for bounce, complaint, and delivery notifications, pointing to the Mailmark sending webhook.
- IAM role trusted by Mailmark's AWS account, with an
ExternalIdcondition. Mailmark uses this role (viaSTS.AssumeRole) whenever it needs to call SES or S3 on your behalf.
ExternalId is generated per Mailmark account and baked into the trust policy. It prevents the confused deputy problem - even if someone else learned your role ARN, they couldn't assume it without the ExternalId.Step-by-step setup
There are two entry points to the connect wizard:
- Dashboard → Domains → Add domain → Use my own AWS account → Connect a new AWS account, or
- Dashboard → Settings → Connect AWS account.
The wizard has three steps:
1. Alias & region. Pick a friendly name for the account (e.g. Production) and the AWS region you want the resources provisioned in (e.g. us-east-1).
2. Deploy the CloudFormation stack. Click Open CloudFormation in AWS. The Quick-Create URL opens the AWS console and pre-fills every parameter for you:
ExternalId- unique per-account value Mailmark will present when assuming the role.MailmarkAwsAccountId- the Mailmark AWS account ID, used in the role trust policy.WebhookSecret- shared secret used to sign webhook payloads.InboundWebhookUrl/SendingWebhookUrl- the Mailmark endpoints your Lambda / SNS will POST to.
Sign in with the AWS account you want to use, review the parameters, acknowledge the IAM capabilities checkbox, and click Create stack.
3. Paste the outputs. When the stack status turns CREATE_COMPLETE, open the Outputs tab in the AWS console and copy two values:
RoleArn- e.g.arn:aws:iam::123456789012:role/mailmark-...BucketName- the S3 bucket for inbound email.
Paste both into the Mailmark wizard and click Verify. Mailmark will:
- Call
STS.AssumeRoleusing your RoleArn and ExternalId. - Confirm identity with
STS.GetCallerIdentity. - Probe
SES.GetAccountto detect whether your SES account is still in the sandbox.
On success the account status flips to Verified and it becomes available as an infrastructure target when adding domains.
Ongoing behavior
SES sandbox. Fresh AWS accounts start with SES in sandbox mode, which only allows sending to verified recipients. If Mailmark detects your account is in the sandbox, a SES sandbox badge is shown next to the account in Settings and the Add-Domain picker. Request production access in the AWS console to remove it.
Multiple domains, one AWS account. A connected AWS account can host any number of Mailmark domains. The Settings page lists every linked domain for each account.
Re-verification. Mailmark caches the verification result. If you rotate the IAM role, delete and recreate the stack, or change the ExternalId, the next operation that requires AssumeRole will fail and the account status will flip to Failed with the AWS error message shown inline.
Disconnecting an AWS account
To disconnect, go to Dashboard → Settings, find the account card, and click Disconnect → Confirm disconnect.
Disconnecting only removes the link between Mailmark and your AWS account. The CloudFormation stack in your AWS account is not deleted by Mailmark. To free up the resources, delete the stack yourself in the AWS console - it will remove the S3 bucket (only if empty), SES identity, Lambda, SNS topics, and IAM role together.
Troubleshooting
“AssumeRole failed” / “Could not assume role”
Mailmark could not call STS.AssumeRole on your role. Common causes:
- The role's trust policy does not allow Mailmark's AWS account as principal. Re-check the
MailmarkAwsAccountIdparameter on the stack matches the value shown in the wizard. - The
ExternalIdon the stack differs from the one Mailmark was issued. Recreate the stack with the exactExternalIdfrom the wizard, or re-run the wizard from scratch to get a fresh one. - You pasted a different role's ARN. The wizard expects the role ARN listed under the stack's Outputs tab, not a role you created by hand.
“TemplateURL must be a supported URL” when clicking Open CloudFormation in AWS
This is an operator-side configuration error: the Mailmark deployment is missing the MAILMARK_CFN_TEMPLATE_URL environment variable, or it points to a non-S3 URL. AWS CloudFormation only accepts template URLs hosted on S3. If you are self-hosting Mailmark, upload public/infra/byo-aws-cfn.yml to a public-readable S3 object and set MAILMARK_CFN_TEMPLATE_URL to that URL in Convex. Otherwise, contact Mailmark support.
SES sandbox badge keeps showing even after production access is granted
Mailmark rechecks SES.GetAccount on the next verification. Go to Settings and reconnect (or wait for the next scheduled re-check) to refresh the sandbox state.