Docs/BYOC Setup
BYOC

Bring Your Own SES

BYOC routes email through your own AWS SES account via a short-lived IAM role. Your email content never touches our database. Your sender reputation is isolated from every other SendFleet customer.

BYOC requires SES production access. AWS sandbox accounts are limited to verified addresses only and a 200-email/day cap - not suitable for real sending. Request production access →

How it works

SendFleet operates two isolated AWS accounts. The BYOC relay uses AWS STS to assume your IAM role for a 15-minute session and dispatches your email directly through your SES. Neither your email content nor your recipient addresses ever reach our database.

Your App
POST /api/send/
SendFleet
Route → SQS
SendFleet relay
Secure role assumption
Your SES
ses:SendEmail
Email content exists only in the SQS message (consumed and discarded by our relay) and your SES account logs. We store only a monthly send counter.

Security - ExternalId & confused deputy

Every BYOC configuration has a unique, randomly generated ExternalId (48 hex characters = 192 bits of entropy). It must appear in your IAM trust policy's Condition block.

Without ExternalId, any party who learns your Role ARN could ask our infrastructure to assume it on their behalf - this is the confused deputy attack. With ExternalId, only requests carrying the correct value (only we know it) can assume your role.

Never skip the ExternalId Condition.An IAM role without it is vulnerable to the confused deputy attack. The ExternalId is generated automatically - it's shown in your account settings and pre-filled in the trust policy template.

Step 1 - Create the IAM Role

In your AWS Console, go to IAM → Roles → Create role. Select Custom trust policy and paste the policy below. The role name must be exactly SendFleet-BYOC-Role - our relay infrastructure is only permitted to assume roles with this exact name.

Trust policy

Copy your ExternalId from Dashboard → Settings → BYOC sectionfirst (it's generated when you first save your config).

IAM Trust Policy (paste into AWS Console)
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::979054355634:root"
      },
      "Action": "sts:AssumeRole",
      "Condition": {
        "StringEquals": {
          "sts:ExternalId": "<YOUR_EXTERNAL_ID_FROM_SETTINGS>"
        }
      }
    }
  ]
}
The exact Account ID and your personal ExternalId are shown pre-filled in Dashboard → Settings → BYOC. The template there is ready to paste - no manual substitution needed.

Required IAM permissions

Attach a permissions policy to the role with the following actions. Create a new inline policy or attach a custom managed policy:

IAM Permissions Policy
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "SendFleetSESPermissions",
      "Effect": "Allow",
      "Action": [
        "ses:SendEmail",
        "ses:SendRawEmail",
        "ses:VerifyDomainIdentity",
        "ses:VerifyDomainDkim",
        "ses:GetIdentityVerificationAttributes",
        "ses:GetIdentityDkimAttributes",
        "ses:GetIdentityMailFromDomainAttributes",
        "ses:SetIdentityMailFromDomain",
        "ses:DeleteIdentity",
        "ses:GetAccountSendingEnabled",
        "ses:GetSendQuota"
      ],
      "Resource": "*"
    }
  ]
}
PermissionUsed for
ses:SendEmail, ses:SendRawEmailDelivering email on your behalf
ses:VerifyDomainIdentity, ses:VerifyDomainDkimRegistering sending domains via the dashboard
ses:GetIdentityVerificationAttributes, ses:GetIdentityDkimAttributesChecking domain verification status
ses:GetIdentityMailFromDomainAttributes, ses:SetIdentityMailFromDomainConfiguring MAIL FROM subdomain
ses:DeleteIdentityRemoving domains when you delete them
ses:GetAccountSendingEnabled, ses:GetSendQuotaSandbox detection during validation

Step 2 - Connect in SendFleet

Go to Dashboard → Settings → BYOC section. Enter your Role ARN and select your SES region. Click Validate & save.

We validate by:

  • Calling sts:AssumeRole with your ExternalId - confirms the trust policy is correct
  • Calling ses:GetAccountSendingEnabled - confirms sending is enabled
  • Calling ses:GetSendQuota- confirms you're out of sandbox mode (production quota must exceed 200 emails/day)
IAM changes take ~10 seconds to propagate globally. If validation fails immediately after creating the role, wait 15 seconds and try again.

Step 3 - Add a sending domain

Once the role is validated, go to Dashboard → Domains → Add BYOC domain. Enter your root domain (e.g. acme.com - not a subdomain).

We will:

  • Call ses:VerifyDomainIdentity in your account - gives you a TXT token to add to DNS
  • Call ses:VerifyDomainDkim in your account - gives you 3 CNAME tokens to add to DNS
  • Configure a MAIL FROM subdomain (send.yourdomain.com) for bounce routing

Add all DNS records shown. Then use the per-record Verify buttons in the domain detail page. DNS propagation can take up to 48 hours. See the full DNS Records guide →

Step 4 - Send

Once domain ownership and DKIM are both verified, you're ready to send. Use the domain's address in from_email:

First BYOC send
curl -X POST https://sendfleet.net/api/send/ \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "to":         "customer@example.com",
    "from_email": "hello@acme.com",
    "from_name":  "Acme",
    "subject":    "Welcome!",
    "message":    "Thanks for joining Acme."
  }'

# Response will include "mode": "byoc"

Privacy guarantee

DataStored by SendFleet?
Recipient email addressNever
Email subjectNever
Email body (text or HTML)Never
Monthly send countYes (one number, Starter plan only)
IAM Role ARNYes (required for routing)
ExternalIdYes (required for STS)

Troubleshooting

ErrorFix
Could not assume roleCheck the trust policy's Principal.AWS matches Account's ID exactly and the sts:ExternalId condition matches your ExternalId from settings.
Sending is disabled on your SES accountGo to AWS Console → SES → Account dashboard and enable sending.
SES account is in sandbox modeYour 24-hour send quota is ≤ 200. Request production access →
Missing ses:GetSendQuota permissionEnsure your permissions policy includes all 11 actions listed in Step 1.
IAM change not taking effectWait 15 seconds for global IAM propagation and try validation again.
byoc_not_configured (503)SendFleet Account credentials are misconfigured on our end. Contact support.