# SendOps Documentation > SendOps is the observability and management layer for Amazon SES — analytics, Git-based template management, collaboration, and deliverability monitoring on top of your AWS SES account. This documentation covers setup, sending, templates, deliverability, billing, and AWS integration. # What is SendOps? Source: https://help.sendops.dev/getting-started/what-is-sendops Section: Getting Started > An overview of SendOps — the observability and management layer for Amazon SES that gives your team visibility into every email you send. SendOps is a web-based SaaS dashboard that sits on top of **Amazon Simple Email Service (SES)**. It gives your team real-time observability, GitHub-integrated template management, collaboration tools, and alerting — without changing a single line of your sending code. Your application continues to send email through AWS SES using the AWS SDK you already have in place. SendOps **observes** the events SES produces and turns them into actionable insights. You never route mail through SendOps servers. ## Why SendOps? AWS SES is a powerful, cost-effective email sending service — but it gives you very little visibility out of the box. Monitoring deliverability, tracking engagement, managing templates across environments, and collaborating as a team all require custom tooling that takes months to build and maintain. SendOps fills that gap. Connect your AWS account, and within minutes you have: - **Deliverability monitoring** — bounce rates, complaint rates, delivery metrics, ISP breakdown, and a reputation score at a glance. - **Message-level tracing** — follow a single email from send to delivery, open, and click. - **GitHub-integrated template management** — sync and manage your SES email templates from a connected GitHub repository. - **Engagement analytics** — track opens, clicks, and template performance across your sending. - **Multi-identity and multi-domain support** — manage multiple sending domains and email identities from a single dashboard. - **Channel-based organization** — channels map to SES Configuration Sets, letting you organize sending by purpose (Transactional, Marketing, Onboarding, or Custom) with per-channel tracking and processing. - **Team collaboration** — invite teammates and assign one of five roles (Owner, Admin, Developer, Marketer, Viewer) so the right people see the right data. - **Webhook and Slack integrations** — connect external systems through the Connections section, including outbound webhooks and Slack notifications. - **Audit logging** — track every significant action taken in your account for security and compliance. - **Public API** — a read-only REST API for messages, reports, suppressions, and configuration. See the [API reference](https://developers.sendops.dev) for the full catalogue. ## Who is SendOps for? SendOps is built for three audiences that often work together but have different day-to-day needs: | Audience | Role | What SendOps gives them | |---|---|---| | **Developers** | Build and maintain the application that sends email through SES | Message tracing, template management via GitHub, event logs, and a [public REST API](https://developers.sendops.dev) for programmatic access | | **Marketers & Team Members** | Monitor campaign performance and email engagement | Dashboards, engagement metrics (opens, clicks), template performance tracking, deliverability reports, and scheduled notifications | | **Infrastructure / AWS Admins** | Manage AWS accounts, IAM policies, DNS, and compliance | One-click CloudFormation setup, cross-account IAM role visibility, domain verification status, and audit logs | ## Key features ### Observability Every SES event — sends, deliveries, bounces, complaints, opens, and clicks — is captured and stored in a high-performance analytics engine. Query millions of events in seconds. Deliverability monitoring includes ISP breakdown and a reputation score to help you stay ahead of issues. ### Template management Sync your SES email templates from a connected GitHub repository. Track versions, preview renders, and manage templates across environments — all driven by your existing Git workflow. ### Channels Organize your sending into **Channels** that map to SES Configuration Sets. Each channel has a Purpose type (Default, Transactional, Marketing, Onboarding, or Custom) and controls how emails are tracked and processed. Identities are assigned to channels for per-channel metrics and reporting. ### Team collaboration Invite your team and assign one of five roles — **Owner**, **Admin**, **Developer**, **Marketer**, or **Viewer** — so the right people see the right data. Developers get event logs; marketers get engagement dashboards; admins get infrastructure controls. ### Connections Integrate SendOps with your existing tools through the Connections section. Set up outbound **webhooks** to push event data to external systems, or connect **Slack** to receive notifications in your team's channels. ### Notifications & alerts Configure alerts for the metrics that matter — bounce rate thresholds, complaint spikes, domain verification failures, and more. Receive them in the dashboard, via email, or through your connected integrations. ## Plans SendOps offers three tiers — **Free**, **Team**, and **Business** — so you can start at no cost and scale as your sending grows. Visit the [pricing page](https://sendops.dev/pricing) for current plan details and limits. ## What's next? Ready to understand how the pieces fit together? Continue to [How SendOps Works](/getting-started/how-sendops-works) for a walkthrough of the architecture, or jump straight to [Creating Your Account](/getting-started/creating-your-account) to get started. --- # How SendOps Works Source: https://help.sendops.dev/getting-started/how-sendops-works Section: Getting Started > Understand the architecture behind SendOps — how it connects to your AWS account, captures SES events, and turns them into actionable analytics. This page is optional. You don't need to understand the architecture to use SendOps. SendOps works by observing the events that Amazon SES already produces. It does not sit in the sending path — your application sends email exactly the way it does today. SendOps captures what happens after. SendOps is a read-only observer. Your application sends email through AWS SES using the AWS SDK. SendOps connects to your AWS account via a cross-account IAM role, listens for SES events through EventBridge, and presents everything in the dashboard. ## The event flow Here is the end-to-end path of an email event, from your application to the SendOps dashboard: ### Step by step 1. **Your application sends an email** using the AWS SDK (SES v2 API, SMTP, or any supported method). Nothing changes here. 2. **SES processes the email** and generates lifecycle events — `Send`, `Delivery`, `Bounce`, `Complaint`, `Open`, `Click`, and more — based on what happens with the message. 3. **A Configuration Set** is attached to your sending calls (or set as the account-level default). In SendOps, each Channel maps to an SES Configuration Set. The configuration set publishes events to Amazon EventBridge. 4. **An EventBridge rule in your AWS account** matches those events and forwards them to the SendOps webhook endpoint at `api.sendops.dev`. 5. **SendOps ingests the event**, enriches it with metadata (domain, channel, template), and writes it to a high-performance analytics engine built for fast aggregation over large volumes of event data. 6. **The SendOps dashboard** at `app.sendops.dev` queries the analytics engine in real time to power dashboards, reports, message tracing, and alerting. ## Connecting your AWS account SendOps uses **cross-account IAM role assumption** to interact with your AWS account. This is the same pattern AWS recommends for granting third-party access — no long-lived API keys are stored. During setup, you can connect your AWS account using one of two methods: - **Automated (CloudFormation)** — a one-click stack deployment that creates all required resources. - **Manual** — step-by-step IAM role creation for organizations that require manual provisioning. Either method creates: | Resource | Purpose | |---|---| | **IAM Role** | A role in your account that SendOps assumes. Scoped to read-only SES actions and the specific resources SendOps needs. | | **EventBridge Rule** | Routes SES events from the configuration set to the SendOps webhook. | | **Webhook Target** | The EventBridge API destination that delivers events to `api.sendops.dev`. | | **SES Configuration Set** | Captures event types and publishes them to EventBridge. | The IAM role follows the principle of least privilege. SendOps only requests the permissions it needs to read SES configuration and receive events. It cannot send email on your behalf. See [IAM Permissions Explained](/aws-setup/iam-permissions) for the full policy breakdown. ### Why IAM roles instead of API keys? - **No secrets to rotate.** There are no access keys stored in SendOps. The role is assumed on demand using AWS STS. - **Revocable at any time.** Delete the CloudFormation stack and access is immediately revoked. - **Auditable.** Every assumption is logged in AWS CloudTrail. ## Continuous AWS sync Once connected, SendOps regularly syncs with your AWS SES account to discover new domains, email identities, and configuration sets. If something is added or removed in SES, SendOps detects it automatically and updates your dashboard. See [AWS Account Sync](/aws-setup/account-sync) for details. ## Templates SendOps manages email templates through **GitHub integration**. Connect a GitHub repository, and SendOps syncs your templates automatically. Changes pushed to the repository are reflected in SendOps, keeping your templates version-controlled and aligned with your development workflow. If you already have email templates in SES, SendOps can [export them as Git-ready files](/templates/importing-from-ses) so you can commit them to a repository and start managing them through SendOps right away. ## What SendOps stores SendOps stores **SES event data** — delivery status, bounce details, complaint feedback, open/click tracking — in its analytics engine. It also stores metadata you configure in the dashboard: domains, channels, templates, team members, and notification rules. SendOps does **not** store email content or message bodies. The events SES publishes contain headers, timestamps, and delivery metadata, but not the full message payload. ## Programmatic access Everything the dashboard shows is also available through the [SendOps Public API](https://developers.sendops.dev) — a read-only REST API at `api.sendops.dev` for messages, reports, suppressions, channels, templates, identities, and account state. Authentication is via bearer API keys you create in your account settings. The API is server-to-server only (no browser-origin calls) and never sends mail — sending continues to go through AWS SES directly. ## Infrastructure at a glance | Component | Technology | Role | |---|---|---| | Dashboard | `app.sendops.dev` | Web application — observability, templates, team management | | API | `api.sendops.dev` | Receives webhook events, serves the dashboard | | Analytics storage | SendOps Analytics Engine | High-speed event aggregation and querying | | Event transport | Amazon EventBridge | Routes SES events from your account to SendOps | | Auth to your AWS | IAM cross-account role | Secure, keyless access via STS | ## What's next? Now that you understand how the pieces fit together: - [Create your SendOps account](/getting-started/creating-your-account) to get started. - [Connect your AWS account](/aws-setup/connecting-aws) to deploy the CloudFormation stack. - [Learn about IAM permissions](/aws-setup/iam-permissions) to review exactly what access SendOps requests. --- # Creating Your Account Source: https://help.sendops.dev/getting-started/creating-your-account Section: Getting Started > Sign up for SendOps, explore the demo workspace, and complete the Setup Guide to connect your AWS account and start monitoring email. Getting started with SendOps takes just a few minutes. Create an account, explore the demo workspace to familiarize yourself with the product, then complete the Setup Guide to connect your own AWS environment. ## Sign up Open [app.sendops.dev](https://app.sendops.dev) in your browser. Click **Sign up** to begin. You can create your account using: - **GitHub** — sign in with your GitHub account. - **Google** — sign in with your Google Workspace or personal Google account. - **Email and password** — enter your full name, email address, and choose a password (minimum 8 characters). Check the box to agree to the Terms of Service and Privacy Policy, then click **Create account**. After creating your account, you are redirected to the sign-in page. Enter the credentials you just created and click **Sign in**. ## Demo workspace After your first sign-in, you land in the **Demo Workspace** — a fully populated environment with sample data so you can explore the product before connecting your own AWS account. The demo includes example reports, identities, channels, and templates so you can see how everything fits together. A banner at the top of the page reads **"Exploring with sample data."** When you are ready to set up your own workspace, click **Switch to your workspace**. ## The Setup Guide When you switch to your own workspace, you land on the **Setup Guide** — a four-phase process that connects your AWS account and confirms everything is working end to end. You can always return to the Setup Guide from the sidebar. Connect your AWS account to SendOps. Choose between two methods: - **Automated Setup** Recommended — select your AWS region, then click **Launch CloudFormation Stack** to open the stack in your AWS console. The stack provisions the IAM role automatically. Once the stack is created, a link to the IAM role in CloudFormation appears to help you find and copy the Role ARN. - **Manual Setup** — create the IAM role yourself using the policy document provided. The Setup Guide shows the full policy in both CloudFormation and Terraform formats with a copy button. Both methods include: - **AWS Region selector** — choose the region where your SES is configured. - **Permission breakdown** — expandable categories showing exactly which permissions are requested: Account & Identities, Configuration Sets, Email Templates, Suppression List, Resource Tagging, and EventBridge. Each category shows the number of permissions it contains. - **"Never accessed" badges** — a list of AWS services SendOps will never touch (EC2, S3, RDS, Lambda, Billing, IAM write, CloudWatch Logs) for transparency. - **Role ARN field** — after creating the role (via either method), paste the Role ARN and click **Validate** to confirm permissions. Click **"Delegate this step"** to send setup instructions to a teammate or AWS admin who has the required access. They will receive everything they need to complete the AWS Integration on your behalf. See [AWS Integration](/aws-setup/connecting-aws) for a detailed walkthrough of both methods. Verify that your CloudFormation stack deployed correctly and your SES configuration is active. Click **Run Validation** to check all components. The validation runs through 11 checks: - **SES Configuration Set** — confirms the configuration set exists. - **EventBridge Rule** — verifies the event rule is found. - **Webhook Target** — checks the webhook endpoint is connected. - **Channel Management** — validates configuration set management permissions. - **Channel Settings** — confirms channel settings permissions. - **Email Templates** — verifies email template management permissions. - **Contact Lists** — verifies contact list access (`ses:ListContactLists`) so your SES contact list can sync. - **SES enabled in region** — confirms SES is active in your selected region. - **Account status** — reports whether you have production access or are in sandbox mode. - **Sending limits** — displays your daily sending quota and max send rate. - **Verified identities** — detects how many identities are already verified in your SES account. Each check shows a green pass or red fail status. If your account is in SES sandbox mode, the **Account status** check passes with a yellow warning instead of green. If any check fails, click **Retry Validation** after resolving the issue, or go **Back** to Phase 1 to adjust your configuration. Add and verify your sending domains and email identities, and review the other SES resources SendOps picked up: - **Domain Identities** — your verified sending domains. Columns: Identity and Verification status. Click **Add Domain** to register a new one. Each domain row is expandable to show DNS details and has per-row re-sync and delete actions. - **Email Identities** — individual email addresses verified in SES. Columns: Identity and Verification status. Click **Add Email Identity** to verify a new address. Each row has re-sync and delete actions. - **Configuration Sets** — SES configuration sets discovered in your account, which you can adopt as SendOps channels. - **Contact Lists** — a read-only summary of the SES contact list SendOps imported, showing the total contacts and topics. Contact lists can be large, so individual contacts are not listed here and there is nothing to adopt — they import automatically. If you already have identities in SES, click **Sync from AWS** (shown on first visit) or the **Re-sync** button to import them. You can also re-sync individual identities using the icon on each row. The delete action on each row removes the identity from your AWS SES account, not just from SendOps. This is permanent and cannot be undone. See [Adding a Domain](/domains/adding-a-domain) for detailed instructions on domain setup and DNS configuration. Confirm the full pipeline is working by sending a test email. Enter a recipient address in the **Send test to** field and click **Send Test Email**. Once delivered, you will see an **"Email delivered and confirmed"** success message showing the From and To addresses and a timestamp — confirming end-to-end delivery through your SES setup. You can click **Send another test** to verify additional addresses. New SES accounts start in **sandbox mode**, which limits you to sending only to verified email addresses. You will need to request production access from AWS to send to any recipient. See [SES Sandbox & Production](/sending-email/ses-sandbox) for details. ## After setup Once all four phases are complete, your dashboard begins populating with live email data. From here you can: - **Explore the Messages dashboard** to trace individual emails and review delivery events. - **Set up Channels** to organize your sending identities by purpose. - **Invite team members** and assign roles so your colleagues can access the data they need. - **Configure notifications** to get alerted when bounce rates spike or domains need attention. ## What's next? If you have not yet connected your AWS account, head to [AWS Integration](/aws-setup/connecting-aws) to begin the setup process. For a closer look at the permissions SendOps requests, see [IAM Permissions Explained](/aws-setup/iam-permissions). --- # Reports Overview Source: https://help.sendops.dev/reports/messages-dashboard Section: Reports & Analytics > Navigate the SendOps Reports page to view messages, deliverability metrics, and engagement data across tabs with filtering, export, and auto-refresh. Reports in SendOps is a single page with three main tabs: **Messages**, **Deliverability**, and **Engagement**. Each tab provides a focused view of your email activity, and they share common controls for filtering and exporting data. ## Shared controls The following features are available across all tabs: - **Auto-refresh toggle** — enable automatic data refresh to keep the view up to date as new events arrive. - **Channel filter** — use the dropdown to limit results to a specific [channel](/channels/understanding-channels). - **Date range picker** — select a custom date range to focus on a specific period. - **Export** — download the current view's data for offline analysis or sharing. ## Messages tab The Messages tab has two sub-tabs: **Messages** and **Event Log**. ### Messages sub-tab The Messages sub-tab shows a list of individual messages processed through your connected AWS SES account. Each row includes: | Column | Description | |--------|-------------| | **Status** | The current delivery status of the message (delivered, bounced, complained, etc.). | | **Recipient** | The email address the message was sent to. | | **Subject** | The subject line of the message. | | **Timestamps** | When the message was sent and when key events occurred. | Click any row to open the message detail view with the full event timeline, headers, and delivery information. ### Event Log sub-tab The Event Log sub-tab displays a raw stream of all SES events — deliveries, bounces, complaints, opens, and clicks — in chronological order. Use the Event Log to see exactly what happened and when, without the per-message grouping of the Messages view. The Event Log supports filtering to narrow down to specific event types, making it useful for investigating delivery issues or tracking engagement patterns in real time. SendOps stores event data in a high-performance analytics engine designed for sub-second aggregation across billions of rows. Whether your account sends a hundred emails a day or a hundred thousand, queries return quickly. ## Deliverability tab The Deliverability tab shows bounce rates, complaint rates, reputation metrics, and ISP-level breakdowns. For a detailed walkthrough of every metric on this tab, see [Deliverability Reports](/reports/deliverability-reports). ## Engagement tab The Engagement tab tracks open rates and click rates over time, with breakdowns by domain and template. For a detailed walkthrough, see [Engagement Metrics](/reports/engagement-metrics). ## What's next? - Review [Deliverability Reports](/reports/deliverability-reports) to monitor bounce and complaint rates. - Check [Engagement Metrics](/reports/engagement-metrics) for open and click tracking data. - If you notice high bounce rates, see [Deliverability Problems](/troubleshooting/deliverability-problems) for troubleshooting steps. --- # Deliverability Reports Source: https://help.sendops.dev/reports/deliverability-reports Section: Reports & Analytics > Track bounce rates, complaint rates, reputation score, per-provider deliverability with trend deltas, delivery latency, and suppression data in the SendOps Deliverability tab. The Deliverability tab on the [Reports](/reports/messages-dashboard) page helps you understand whether your email is reaching inboxes. SendOps tracks every bounce and complaint event from AWS SES and surfaces key metrics, per-provider deliverability with trend deltas, latency data, and suppression information. ## Headline metrics The top of the Deliverability tab displays the following metrics for the selected date range: | Metric | Description | |--------|-------------| | **Hard Bounce %** | The percentage of sent messages that resulted in a hard bounce (permanent delivery failure). | | **Soft Bounce %** | The percentage of sent messages that resulted in a soft bounce (temporary delivery failure). | | **Total Bounce %** | The combined bounce rate (hard + soft bounces). | | **Complaint Rate** | The percentage of delivered messages that generated a spam complaint. | | **Reputation Score** | An overall sender reputation indicator based on your bounce rate, complaint rate, and delivery success. | AWS monitors your sending reputation continuously. If your bounce rate exceeds **5%** or your complaint rate exceeds **0.1%**, SES may place your account under review, limit your sending, or suspend it entirely. SendOps displays these thresholds as reference lines so you can see how close you are at a glance. ## Bounce Reasons A bar chart shows categorized bounce reasons, breaking down why messages bounced. This helps you distinguish between list quality issues (invalid addresses), mailbox-full conditions, policy rejections, and other bounce categories so you can take targeted corrective action. ## Complaint Reasons A breakdown of complaint reasons with percentages, showing why recipients reported your messages as spam. Use this to identify patterns — for example, a high percentage of "moved to junk" complaints may indicate content or frequency issues. ## Deliverability by Provider The Deliverability by Provider table shows per-provider sending volume, deliverability rates, and how those rates have shifted versus the equal-length period immediately before the one you're viewing. The top eight providers by current-period sends are listed individually, with the long tail aggregated under **Other**. The provider list is derived from your actual sending data — your top destinations show up automatically, whether they're US providers (Gmail, Microsoft, Yahoo, Apple), European providers (Free.fr, Orange, GMX, Web.de, Yandex, Mail.ru, Libero, Seznam, etc.), or APAC providers (Naver, QQ, NetEase, Sina, Daum, Rediff, etc.). SendOps recognises common alias domains (gmail.com + googlemail.com → Gmail; outlook.com + hotmail.com + live.com + msn.com + regional TLDs → Microsoft; Yahoo regional TLDs → Yahoo; etc.) so they collapse under one canonical name. Domains SendOps doesn't recognise appear under the raw recipient domain, so nothing is hidden from view. | Column | Description | |--------|-------------| | **Provider** | The receiving provider, derived from the recipient email domain. | | **Sends** | Total messages sent to recipients at this provider. | | **Deliveries** | Messages confirmed delivered. | | **Bounces** | Messages that bounced. | | **Bounce %** | Bounces as a percentage of Sends. The annotation underneath shows the change in percentage points versus the prior period. | | **Complaints** | Messages that generated spam complaints. | | **Complaint %** | Complaints as a percentage of Sends, with the prior-period delta underneath. | | **Rejects** | Messages that AWS SES refused to send (typically due to content rejection or recipient suppression). | | **Reject %** | Rejects as a percentage of attempted messages (Sends + Rejects, since rejected messages are not counted in Sends), with the prior-period delta underneath. | Click any column header to sort by that column. **Other** always appears at the bottom regardless of sort, since it is a residual rather than a ranked provider. The trend deltas (the small `+0.40pp` / `-0.05pp` annotations under each rate) are the primary signal for spotting deliverability issues: a provider whose bounce or complaint rate has crept up versus the prior period is the one to investigate, even if the absolute rate is still under the SES thresholds. Positive deltas (rates getting worse) appear in red; negative deltas (rates improving) appear in green. Brand-new providers with no prior-period activity show no delta, since there's nothing to compare against. ## Delivery Latency The Delivery Latency section shows how long it takes for messages to be accepted by recipient mail servers. Three percentile metrics are displayed: | Metric | Description | |--------|-------------| | **P50** | The median delivery latency — half of messages are delivered faster than this. | | **P95** | 95% of messages are delivered within this time. | | **P99** | 99% of messages are delivered within this time. Only the slowest 1% take longer. | A chart visualizes latency trends over the selected date range, making it easy to spot degradation or improvement over time. ## Suppression List The Suppression List viewer shows email addresses that have been suppressed. Suppressed addresses are those that SES will not attempt to deliver to, typically due to previous hard bounces or complaints. Use this section to review which addresses are suppressed and understand the reasons. ## Improving deliverability If your rates are trending in the wrong direction, here are the most effective steps: 1. **Clean your recipient list.** Remove addresses that have hard-bounced. Implement double opt-in for new subscribers to ensure valid addresses. 2. **Monitor complaint sources.** Check which campaigns or message types are generating complaints. A high complaint rate on a specific channel or template is a signal to revisit the content or frequency. 3. **Use a dedicated domain for transactional email.** Separate transactional email (order confirmations, password resets) from marketing email by using different domains or subdomains. This isolates reputation issues. 4. **Authenticate your email properly.** Ensure SPF, DKIM, and DMARC are correctly configured. See [DNS Configuration](/domains/dns-configuration) for setup details. 5. **Warm up new domains gradually.** If you add a new sending domain, start with low volume and increase over days or weeks. Sudden high-volume sending from an unknown domain triggers ISP suspicion. For a deeper walkthrough of common deliverability issues and how to resolve them, see [Deliverability Problems](/troubleshooting/deliverability-problems). ## What's next? - Review the [Reports Overview](/reports/messages-dashboard) to investigate individual bounced or complained messages. - Check [Engagement Metrics](/reports/engagement-metrics) to see how recipients interact with your delivered email. - Set up [Notifications](/notifications/configuring-notifications) to get alerted when bounce or complaint rates cross a threshold. --- # Engagement Metrics Source: https://help.sendops.dev/reports/engagement-metrics Section: Reports & Analytics > Track open rates, click rates, and recipient engagement with time-series charts, domain breakdowns, and template performance tables in SendOps. The Engagement tab on the [Reports](/reports/messages-dashboard) page shows you what happens after your email lands in the inbox. SendOps tracks opens and clicks for every message that passes through AWS SES and presents the data through dedicated charts and tables. ## Open Rate Over Time A time-series chart showing open rates across the selected date range. Use this to spot trends in recipient engagement — a declining open rate may indicate list fatigue or deliverability issues pushing messages to spam. ## Click Rate Over Time A time-series chart showing click rates over the same period. This tracks how often recipients click links in your emails, giving you a measure of content effectiveness. ## Engagement by Domain A bar chart breaking down engagement metrics by recipient domain. This shows which domains (e.g., gmail.com, outlook.com, yahoo.com) have the highest and lowest engagement, helping you identify domain-specific issues or patterns. ## Engagement by Template A bar chart showing engagement broken down by template. Compare how different templates perform to identify high-performing content and templates that may need revision. ## Template Performance Breakdown A detailed table listing every template with its key performance metrics: | Column | Description | |--------|-------------| | **Template** | The template name. | | **Sent** | Total messages sent using this template. | | **Open%** | The percentage of delivered messages that were opened. | | **Click%** | The percentage of delivered messages where at least one link was clicked. | | **Bounce%** | The percentage of messages that bounced. | | **Complaint%** | The percentage of delivered messages that generated a spam complaint. | This table gives you a single view of how each template performs across all key metrics, making it easy to compare templates and identify ones that need attention. ## How tracking works SES provides open and click tracking natively. When tracking is enabled on your configuration set, SES modifies outgoing messages before delivery: ### Open tracking SES inserts a tiny, invisible **tracking pixel** (a 1x1 transparent image) at the end of the HTML body. When the recipient's email client loads images, it requests the pixel from SES, which records an `Open` event. That event flows through EventBridge to SendOps just like any other SES event. ### Click tracking SES rewrites links in the HTML body to point to a **redirect URL**. When the recipient clicks a link, the request goes to SES first, which records a `Click` event with the original destination URL, then redirects the recipient to the intended page. The redirect is fast and transparent to the recipient. If you have configured a [tracking domain](/domains/tracking-domains), SES uses your custom domain for both the tracking pixel and redirect URLs instead of the default SES domain. This improves deliverability and provides a more professional appearance. Open tracking requires the message to be **HTML email** — plain-text messages have no image support. Additionally, many email clients block external images by default or use privacy proxies (such as Apple Mail Privacy Protection) that pre-fetch images regardless of whether the recipient actually reads the message. Treat open rates as a directional signal rather than an exact count. ## Reading the data A few patterns to watch for: - **High open rate, low click rate** — recipients are opening but not engaging with the content. Consider whether calls to action are clear and above the fold. - **Declining open rate over time** — may indicate list fatigue. Recipients are losing interest or your messages are being filtered to spam. Check your [deliverability reports](/reports/deliverability-reports) for rising complaint rates. - **Sudden drop in open rate** — could reflect a deliverability issue (messages going to spam) rather than a content issue. Cross-reference with bounce and complaint trends. ## What's next? - Set up a [Tracking Domain](/domains/tracking-domains) so tracking URLs use your own domain instead of the default SES domain. - Review the [Reports Overview](/reports/messages-dashboard) to see open and click events on individual messages. - Check [Deliverability Reports](/reports/deliverability-reports) to ensure your messages are reaching the inbox in the first place. --- # Undeliverable List Source: https://help.sendops.dev/reports/undeliverable-list Section: Reports & Analytics > View permanent-failure addresses (bounces, complaints, rejects) and configurable rule-based detections across your account, search them, and clear individual addresses so SendOps allows delivery to them again. The **Undeliverable** page lists every recipient address that has produced an undeliverable signal in your retention window — permanent bounces, complaints, rejects, plus the configurable rules you have enabled (repeated transient bounces, undetermined bounces, and soft-bounce accumulation) — along with any addresses you have explicitly cleared. It complements the [Deliverability Reports](/reports/deliverability-reports) by giving you a per-address operational view rather than aggregate rates. ## What gets listed An address appears on the Undeliverable list when SendOps has ingested events that match an enabled classification rule. Three rules are always-on; three are configurable. ### Always-on rules | Event | Condition | Reason | |-------|-----------|--------| | Bounce | `bounceType = Permanent` (any sub-type — `NoEmail`, `General`, `Suppressed`, `OnAccountSuppressionList`, etc.) | `permanent_bounce` | | Complaint | Any complaint | `complaint` | | Reject | Any reject (content refused before sending) | `rejected` | These three rules cannot be disabled. Complaints are locked on for compliance reasons (CAN-SPAM, CASL, GDPR); permanent bounces and rejects are the AWS-confirmed signals at the core of any suppression discipline. ### Configurable rules | Rule | What it catches | Default | |------|-----------------|---------| | `repeated_transient` | Receivers that accept the message then asynchronously reject it (Mimecast / Office 365 / Proofpoint accept-then-NDR), surfaced as `Transient/General` bounces in SES | On — 3 events in 30 days | | `undetermined` | SES couldn't classify the bounce (rare, ~1% of events) | Off — 2 events in 14 days | | `soft_bounce_accumulation` | Transient bounces with sub-type `MailboxFull`, `MessageTooLarge`, `ContentRejected`, or `AttachmentRejected` | Off — 5 events in 14 days | Each configurable rule has two knobs: **events** (how many qualifying events must occur) and **window** (how many days back to look). When ≥ N qualifying events have happened for an address in a rolling M-day window ending now, the rule fires. Tune both from the [Classification Rules](/reports/classification-rules) page. Successful events (`Send`, `Delivery`, `Open`, `Click`) never put an address on this list. `DeliveryDelay` never qualifies on its own either — only bounces, complaints, and rejects do. This list is different from the AWS SES suppression list. AWS only auto-suppresses permanent/General bounces and complaints, and entries there expire after 14 days by default. SendOps's Undeliverable list includes a wider set of signals (and your configurable rules), with no automatic expiry — so it tends to be broader and longer-lived. ## Reading a row Each row shows the address along with: | Column | What it tells you | |--------|-------------------| | **Email** | The recipient address. | | **Status** | `Listed` — currently failing delivery. `Excluded` — you've cleared this address and SendOps will allow delivery to it again. | | **Reason** | The rule that matched this address. When multiple rules match, the highest-priority one wins: `permanent_bounce` > `complaint` > `rejected` > `repeated_transient` > `undetermined` > `soft_bounce_accumulation`. For permanent bounces and complaints, a sub-type is appended (e.g. `permanent bounce · NoEmail`, `complaint · abuse`). | | **Events** | How many qualifying events SendOps has seen for this address in your retention window. Useful for triage — an address with 1,698 hits jumps out instantly. | | **Last change** | When the row last changed state. For listed rows this is the most recent failure event; for excluded rows it's when you cleared the address. | | **Last diagnostic** | The SMTP response from the most recent event (e.g. `550 5.1.1 : Recipient address rejected: User unknown`). Distinguishes "user unknown" from "domain offline" from "policy refused", which matters when you go talk to a customer. For excluded rows this column shows the operator note instead, if one was recorded. | ## Filtering and search Three controls narrow what you see: - **Search email** — case-insensitive substring match against the recipient address. - **Status** — `Listed` (default), `Excluded` (just your cleared addresses), or `All` (both). - **Reason** — restrict listed rows to any of the six reason values. Filters compose. Searching `acme` with `Status: All` will show every `@acme.com` address regardless of whether you have cleared it. ## Allowing delivery to an address If you're confident an address is fine — a customer typo you've corrected on their end, a fixed inbox after IT work, or a false positive — you can clear the address from the list. SendOps will allow delivery to it again from that point onward. 1. Find the address in the list (use search if needed). 2. Click **Allow** on the row. 3. Optionally add a short note (e.g. "Customer fixed mailbox after IT migration"). Notes are recorded in the audit log alongside the operator who took the action. 4. Confirm. The row immediately moves to `Excluded` and disappears from the default view. You can see it again by switching the status filter to `Excluded` or `All`. Allowing delivery to an address that is genuinely undeliverable will likely cause it to bounce again. If that happens, SendOps will automatically put the address back on the list with `Listed` status — your clearing decision is overridden by the new failure. There is no penalty for the round-trip, but for high-volume addresses you may want to verify the underlying issue is fixed before allowing. ## Re-listing a cleared address If you change your mind, switch the status filter to `Excluded`, find the address, and click **Re-list**. The exclusion record is removed; the address returns to the undeliverable view if any qualifying events still exist in your retention window. ## Configuring which rules apply Click **Configure rules** in the top-right of the page (or visit `/undeliverable/rules`) to choose between the **Strict** / **Standard** / **Aggressive** preset profiles or to set individual rule thresholds. See [Classification Rules](/reports/classification-rules) for the full configuration guide. A live preview on that page shows how the list size and breakdown would change before you save — useful when you're considering turning on a new rule. ## Permissions | Action | Required permission | |--------|---------------------| | View the Undeliverable page | `undeliverable.view` | | Allow delivery to an address (clear it) | `undeliverable.manage` | | Re-list a cleared address | `undeliverable.manage` | | Change classification rules | `undeliverable.configure` | By default, **Owner** and **Org Admin** roles have all three permissions. **Member** and **Support** roles can view but not modify. Operators without a permission will see the action buttons disabled with an explanatory tooltip. Every clear / re-list / rules-change action is recorded in the [Audit Log](/team/audit-log) with the operator's identity, before/after snapshots where relevant, and any note they added. ## Retention The listed portion of the page is bounded by your plan's retention window — an address that last bounced 13 months ago on a 12-month-retention plan will fall off the list. Cleared addresses (excluded rows) live in durable storage and survive retention; they remain visible under `Status: Excluded` regardless of how long ago you cleared them. For the windowed rules (`repeated_transient`, `undetermined`, `soft_bounce_accumulation`), the "window" is the rule's own `window_days` knob — for example, with `repeated_transient` set to 3 events in 30 days, only events within the last 30 days count toward the threshold. Older events don't keep an address on the list past the window; the row falls off automatically once recent events no longer satisfy the rule. Cleared exclusions are unaffected. ## What's next? - Tune which signals make an address undeliverable on the [Classification Rules](/reports/classification-rules) page. - Use [Deliverability Reports](/reports/deliverability-reports) to see aggregate bounce and complaint rates over time. - Configure [Notifications](/notifications/configuring-notifications) to be alerted when your bounce or complaint rates cross a threshold. - If you're seeing many undeliverable addresses from a single provider, the [Deliverability by Provider](/reports/deliverability-reports#deliverability-by-provider) table can help identify the source. --- # Classification Rules Source: https://help.sendops.dev/reports/classification-rules Section: Reports & Analytics > Tune which bounce, complaint, and rejection signals put an address on your undeliverable list. Choose a Strict / Standard / Aggressive profile or configure each rule individually, with a live preview of the impact before you save. The **Classification Rules** page controls which signals put an address on your [Undeliverable List](/reports/undeliverable-list). Different operators have legitimately different tolerance for false positives — a bank cares about every legitimate send, a marketing team cares more about list hygiene — so the rules are configurable per organization. You can reach this page from the **Configure rules** button in the top-right of the Undeliverable list, or directly at `/undeliverable/rules`. ## Profiles Pick a profile to apply the recommended defaults. The "Custom" indicator appears automatically when your individual rule settings don't match any preset. | Profile | What it includes | Typical fit | |---------|------------------|-------------| | **Strict** | Locked rules only — permanent bounces, complaints, rejects | Banks, strictly transactional senders. Lowest false-positive risk. | | **Standard** *(default)* | Locked rules + repeated transient bounces (3 events in 30 days) | Most orgs. Catches Mimecast / Office 365 / Proofpoint accept-then-NDR patterns. | | **Aggressive** | Standard + undetermined (2 in 14 days) + soft-bounce accumulation (5 in 14 days) | Marketing or bulk senders where bounce-rate reputation is existential. | | **Custom** | Whatever you've configured in the Advanced section | Auto-selected when knobs deviate from a preset. | Picking a named profile resets every configurable rule to that profile's defaults. If you have unsaved changes, SendOps will confirm before discarding them. ## Always-on (locked) rules Three rules cannot be disabled. They are the core compliance and correctness signals. - **Permanent bounces** (any sub-type — `NoEmail`, `General`, `Suppressed`, `OnAccountSuppressionList`, etc.) — the AWS-confirmed signal that an address is dead. - **Complaints** — required for compliance (CAN-SPAM, CASL, GDPR). Disabling these would be a legal footgun. - **Rejects** — SES already refused the send. Suppressing is correctness, not policy. These appear in the Advanced section as read-only with a lock icon. ## Configurable rules Each configurable rule has two knobs: - **Events** — how many qualifying events must occur (integer, 1–100) - **Within (days)** — how many days back to look (integer, 1–365) When ≥ events have happened for an address in a rolling window of the last `window_days` ending right now, the rule fires. ### Repeated transient bounces Catches the receiving MTA returning `250 OK` to SES at SMTP time, then asynchronously generating a Non-Delivery Report. SES emits this as `Bounce / Transient / General` even though the address is effectively permanently undeliverable. Mimecast, Office 365, and Proofpoint all behave this way. - **Counts**: Transient bounces with sub-type `General` only - **Default**: 3 events in 30 days, on under Standard This is the rule most likely to recover undelivered addresses from your real traffic. For one early customer it surfaced 116 missing addresses that were generating thousands of bounces but never appearing on the Strict-only list. ### Undetermined bounces SES couldn't classify the bounce. Rare (~1% of events) but real — and sometimes the only signal you get for a domain that intermittently refuses mail. - **Counts**: Bounces with `bounceType = Undetermined` - **Default**: 2 events in 14 days, off Turning this on cleans the long tail at the cost of occasional false positives where a transient infrastructure problem produced the events. ### Soft-bounce accumulation For mailboxes that keep failing for the same reason — quotas, message size, content rejection. The address isn't dead, but continuing to send is wasted budget and bad reputation. - **Counts**: Transient bounces with sub-type `MailboxFull`, `MessageTooLarge`, `ContentRejected`, or `AttachmentRejected` - **Default**: 5 events in 14 days, off `ChannelLimitExceeded` is intentionally excluded — that's a sender-side capacity issue, not a recipient signal. ## Live preview As you adjust knobs, the **Live preview** panel re-fires within ~300ms and shows: - **Current list size** — how many addresses are on the list right now under your active rules - **With these rules** — how many would be on the list with the candidate rules, with a `+added, −removed` delta - **Breakdown** — by-reason counts for the candidate list - **Show sample additions** — up to 10 specific addresses that would be added, with their event counts, so you can sanity-check that the rule change catches the right kind of failures The preview is read-only — nothing is saved until you click **Save rules**. If a preview call fails (the panel will say "Computing…" indefinitely), the saved rules are unaffected. ## Reason priority An address often matches multiple rules. The wire `reason` field on the Undeliverable list (and the `/v1/undeliverable` API response) is the highest-priority match, in this order: 1. `permanent_bounce` 2. `complaint` 3. `rejected` 4. `repeated_transient` 5. `undetermined` 6. `soft_bounce_accumulation` So if an address has both a permanent bounce and 5 repeated transient bounces, it appears with `reason: permanent_bounce` — not both. ## Saving and the audit trail **Save rules** persists the rule body and stamps a 6-character version hash. Every change is captured in the [Audit Log](/team/audit-log) with before/after snapshots of the rule JSON and the operator who made the change. API consumers can pin against the `version` field returned by `GET /v1/undeliverable/rules` (and the `X-SendOps-Rules-Version` response header on `/v1/undeliverable`) — a change to the rules means downstream callers may want to re-sync their local suppression list. ## Removing an address from the list Changing rules controls **which** addresses are detected. To remove a specific address (without changing the rules for everyone), use the **Allow** action on the [Undeliverable List](/reports/undeliverable-list#allowing-delivery-to-an-address). Allowed addresses are recorded as exclusions and override the rule outcome until a new failure event comes in. ## Permissions | Action | Required permission | |--------|---------------------| | View the Classification Rules page | `undeliverable.view` | | Save changes | `undeliverable.configure` | By default only **Owner** and **Org Admin** roles can change rules — changing classification policy affects every API caller, every team member's view of the list, and every notification rule that's keyed off undeliverable counts. Other roles see the page in view-only mode with a banner. The **Allow / Re-list** actions on the Undeliverable list itself use a separate permission (`undeliverable.manage`), so a member-level operator can clear individual addresses without being trusted to change the org-wide rule set. ## What's next? - See [Undeliverable List](/reports/undeliverable-list) for the day-to-day workflow of triaging individual addresses. - The [Deliverability Reports](/reports/deliverability-reports) show aggregate bounce and complaint rates — useful for picking rule thresholds based on real volumes. - For programmatic access to the rules (read-only), see `GET /v1/undeliverable/rules` in the API reference. --- # Configuring Notifications Source: https://help.sendops.dev/notifications/configuring-notifications Section: Notifications & Alerts > Manage your notification inbox, set delivery preferences, use presets, and configure team-wide notification settings in SendOps. The SendOps notification system is organized into four tabs: **Inbox**, **Preferences**, **Presets**, and **Team**. Each tab serves a different purpose in managing how you and your team receive alerts. ## Inbox The Inbox tab displays a chronological list of your notifications with read and unread status indicators. From here you can: - **View notifications** — each notification shows the event details, timestamp, and source. - **Mark as read or unread** — click a notification to mark it as read, or use bulk actions to manage multiple notifications at once. - **Navigate to source** — click a notification to jump directly to the relevant page in the dashboard (e.g., a deliverability alert takes you to the affected domain's metrics). ## Preferences The Preferences tab lets you control which notification types you receive and how they are delivered. Each notification type has toggles for the available delivery channels. Navigate to **Notifications → Preferences** in the SendOps dashboard. Each notification type has toggles for the delivery channels available to you. Enable or disable each combination to control exactly which alerts you receive and how. Changes are saved automatically. Certain critical notifications — such as SES health events and billing alerts — are always delivered to workspace Owners and Admins regardless of their individual preference settings. This ensures no critical alert goes unseen. ## Presets The Presets tab provides reusable notification preference templates. Presets let you save a specific combination of notification preferences and apply them quickly — useful for standardizing notification settings across your team or switching between different configurations. ## Team The Team tab is only visible to Owners and Admins. The Team tab lets administrators view and manage notification settings for all workspace members. From here you can: - See each team member's current notification preference configuration. - Adjust notification settings on behalf of team members. - Ensure that critical alerts are properly configured across the team. ## Webhooks and integrations Webhook configuration is not part of the Notifications section. To set up webhooks for receiving SendOps events programmatically, navigate to **Connections → Webhooks** in the product sidebar. See [Webhooks](/notifications/webhooks) for full setup details. Similarly, Slack and other third-party integrations are configured under **Connections → Integrations** in the product sidebar. ## What's next? - Review the full list of available alerts in [Notification Types](/notifications/notification-types). - Set up programmatic event handling with [Webhooks](/notifications/webhooks). - Manage who has access to your workspace in [Team Members & Roles](/team/members-and-roles). --- # Notification Types Source: https://help.sendops.dev/notifications/notification-types Section: Notifications & Alerts > A complete reference of every notification SendOps can send, organized by category — from deliverability alerts and SES health warnings to billing events and team activity. SendOps supports over 40 notification types across eight categories. Each notification can be delivered through any combination of channels you configure — in-app, email, Slack, or outbound [webhooks](/notifications/webhooks). This page is a complete reference. If you are looking for how to turn notifications on or off, see [Configuring Notifications](/notifications/configuring-notifications). ## Notification Presets SendOps provides **Notification Presets** — reusable templates for notification preferences that you can apply to quickly configure notification settings. Instead of toggling individual notifications one by one, select a preset that matches your needs and apply it. You can also create custom presets tailored to your workflow. ## Team notification management Admins can view and manage notification preferences for all team members from the **Team** tab in the Notifications section. This gives administrators a centralized view of who is receiving which notifications, and the ability to adjust preferences on behalf of team members — useful for ensuring critical alerts reach the right people. ## Deliverability Deliverability notifications fire when your sending reputation metrics cross thresholds or show concerning trends. | Type | Key | Description | |------|-----|-------------| | Bounce rate threshold | `deliverability.bounce_rate_threshold` | The bounce rate for a domain or channel has exceeded the configured threshold. | | Complaint rate threshold | `deliverability.complaint_rate_threshold` | The complaint (spam report) rate has exceeded the configured threshold. | | Bounce rate trend | `deliverability.bounce_rate_trend` | An upward trend in bounce rates has been detected over the evaluation window. | | Complaint rate trend | `deliverability.complaint_rate_trend` | An upward trend in complaint rates has been detected over the evaluation window. | | Blocklist detection | `deliverability.blocklist_detection` | An IP address or domain associated with your sending has been found on a blocklist. | | Suppression list growth spike | `deliverability.suppression_list_growth_spike` | Your SES suppression list is growing faster than normal, indicating a potential list quality issue. | Default thresholds are based on AWS SES best-practice guidelines (bounce rate under 5%, complaint rate under 0.1%). You can adjust these per-domain or per-channel in notification preferences. ## SES Account Health These notifications relate to the health and limits of your AWS SES account. They often require immediate action to avoid sending interruptions. | Type | Key | Description | |------|-----|-------------| | Sending quota approaching | `ses_account_health.sending_quota_approaching` | Your account is using 85% or more of its 24-hour SES sending quota. | | Sending quota reached | `ses_account_health.sending_quota_reached` | Your account has hit the SES sending quota. No further emails can be sent until the quota resets. | | Account review / probation | `ses_account_health.account_review_probation` | AWS has placed your SES account under review or probation. Sending may still work, but you should address the underlying issue. | | Sandbox status change | `ses_account_health.sandbox_status_change` | Your SES account's sandbox status has changed (e.g., moved to production access or back to sandbox). | | SES config sync failure | `ses_account_health.ses_config_sync_failure` | SendOps was unable to sync your SES configuration. This may indicate a permissions issue or API error. | When SES places your account under review or probation, address the root cause immediately. Review bounce and complaint details in your [Deliverability Reports](/reports/deliverability-reports). ## Template Lifecycle Template notifications track changes to your email templates, including syncs from your connected GitHub repository. | Type | Key | Description | |------|-----|-------------| | Sync completed | `template_lifecycle.sync_completed` | A GitHub sync completed successfully and templates have been updated. | | Sync failed | `template_lifecycle.sync_failed` | A sync from your connected [GitHub repository](/templates/github-integration) failed. Error details are included. | | PR opened | `template_lifecycle.pr_opened` | A pull request has been opened for template changes. | | PR approved | `template_lifecycle.pr_approved` | A template pull request has been approved. | | PR merged | `template_lifecycle.pr_merged` | A template pull request has been merged. | | Validation error | `template_lifecycle.validation_error` | A template failed validation after sync. Check the template for syntax or variable errors. | | GitHub disconnected | `template_lifecycle.github_disconnected` | The connection to your GitHub repository has been lost. Re-connect from **Templates** to resume syncing. | ## Channel Events Channel notifications inform you when the structure or health of your [Channels](/channels/understanding-channels) changes. | Type | Key | Description | |------|-----|-------------| | Channel created | `channel_events.channel_created` | A new channel has been created. | | Channel settings changed | `channel_events.channel_settings_changed` | A channel's configuration (engagement tracking, event destinations, etc.) has been updated. | | Channel health degraded | `channel_events.channel_health_degraded` | A channel's health metrics have degraded below acceptable thresholds. | | Channel paused | `channel_events.channel_paused` | A channel has been automatically paused due to health issues. | | Domain removed | `channel_events.domain_removed` | A domain has been removed from a channel. | | Tracking domain verified | `channel_events.tracking_domain_verified` | A custom tracking domain has been successfully verified. | | Tracking domain failed | `channel_events.tracking_domain_failed` | A tracking domain verification attempt has failed. Check your DNS records. | | Tracking domain drift | `channel_events.tracking_domain_drift` | A previously verified tracking domain's DNS records are no longer valid. | ## Reporting Digests Digest notifications let you know when scheduled reports are ready. | Type | Key | Description | |------|-----|-------------| | Daily digest | `reporting_digests.daily_digest` | Your daily sending summary is available. | | Weekly digest | `reporting_digests.weekly_digest` | Your weekly deliverability and engagement report is available. | | Monthly executive summary | `reporting_digests.monthly_executive_summary` | Your monthly executive report is available with full metrics for the period. | ## System & Infrastructure System notifications indicate infrastructure-level problems that may affect data collection or integrations. | Type | Key | Description | |------|-----|-------------| | EventBridge connection issue | `system_infrastructure.eventbridge_connection_issue` | The EventBridge connection to your AWS account has been lost. Events may not be reaching SendOps. | | Event ingestion lag | `system_infrastructure.clickhouse_ingestion_lag` | Event ingestion is lagging behind. Recent events may take longer to appear in reports. | | API error rate spike | `system_infrastructure.api_error_rate_spike` | The SendOps API is experiencing an elevated error rate. | | Scheduled maintenance | `system_infrastructure.scheduled_maintenance` | A maintenance window has been scheduled. | | CloudFormation drift | `system_infrastructure.aws_template_drift` | The CloudFormation stack has drifted from its expected configuration. See [AWS Integration](/aws-setup/connecting-aws). | | High duplicate event rate | `system_infrastructure.duplicate_event_rate` | A higher than normal rate of duplicate events is being detected. | ## User Access User access notifications are sent when team membership or permissions change. These are useful for security auditing alongside the [Audit Log](/team/audit-log). | Type | Key | Description | |------|-----|-------------| | User invited | `user_access.user_invited` | A new team member has been invited to the workspace. | | Role changed | `user_access.role_changed` | A team member's role has been changed (e.g., from Marketer to Admin). | ## Billing Billing notifications relate to payment events and plan limits. | Type | Key | Description | |------|-----|-------------| | Payment failed | `billing.payment_failed` | A payment attempt was unsuccessful. Update your payment method to avoid service interruption. | | Payment action required | `billing.payment_action_required` | A payment requires additional authentication (e.g., 3D Secure). Complete the action to process the payment. | | Invoice overdue | `billing.invoice_overdue` | An invoice is overdue. Update your payment method or contact support. | | Payment method expiring | `billing.payment_method_expiring` | Your payment method is expiring soon. Update it to avoid failed payments. | | Plan limit approaching | `billing.plan_limit_approaching` | You are approaching the limits of your current plan (seats, templates, or event retention). | ## What's next? - Learn how to control which of these notifications you receive in [Configuring Notifications](/notifications/configuring-notifications). - Set up outbound webhooks under [Webhooks](/notifications/webhooks) for programmatic event consumption. --- # Webhooks Source: https://help.sendops.dev/notifications/webhooks Section: Notifications & Alerts > Receive SendOps notifications programmatically via HTTP webhooks. Learn how to create endpoints, verify HMAC-SHA256 signatures, and handle retries. Webhooks let you receive SendOps [notifications](/notifications/notification-types) as HTTP POST requests to an endpoint you control. This is useful for feeding alerts into incident management tools, triggering automation workflows, or syncing notification data with internal systems. ## How webhooks work When a notification is triggered and you have a webhook configured for that [notification type](/notifications/notification-types), SendOps sends an HTTP POST request to your configured endpoint with a JSON payload describing the event. Each request includes an HMAC-SHA256 signature header so your server can verify the payload came from SendOps. ## Creating a webhook endpoint In the SendOps dashboard, navigate to **Connections → Webhooks** in the sidebar and click **Add Endpoint**. Provide the HTTPS URL where SendOps should deliver notifications. The endpoint must be publicly accessible and respond to POST requests. Choose which notification types should be sent to this endpoint. You can select individual types or entire categories. See [Notification Types](/notifications/notification-types) for the full list. You can create multiple webhook endpoints and route different notification types to each one. After creating the endpoint, SendOps generates a unique **signing secret** for it. Copy this secret and store it securely — you will need it to verify incoming webhook payloads. The secret is only shown once. If you lose it, you can rotate it from the endpoint settings, which invalidates the previous secret. Treat the webhook signing secret like a password. Store it in environment variables or a secrets manager — never hard-code it in your application source or commit it to version control. Anyone with the secret can forge valid-looking webhook payloads. ## Payload structure Every webhook delivery sends a JSON payload with the following structure: ```json { "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "notification_type": "deliverability.bounce_rate_threshold", "severity": "warning", "account_id": "x7y8z9a0-b1c2-3456-defg-hi7890123456", "timestamp": "2026-03-14T10:30:00Z", "title": "Bounce rate threshold exceeded", "body": "Bounce rate for example.com has exceeded the 5.0% threshold at 6.2% over the last 1 hour.", "payload": { "domain": "example.com", "channel": "transactional", "metric": "bounce_rate", "current_value": 6.2, "threshold": 5.0 } } ``` | Field | Description | |-------|-------------| | `id` | A unique identifier (UUID) for this notification event. Use this for deduplication. | | `notification_type` | The notification type in `category.event_name` format. | | `severity` | One of `info`, `warning`, `error`, or `critical`. | | `account_id` | Your SendOps organization identifier (UUID). | | `timestamp` | ISO 8601 / RFC 3339 timestamp of when the event occurred. | | `title` | A human-readable title for the notification. | | `body` | A human-readable description of the event. | | `payload` | An optional object containing event-specific metadata. The shape varies by notification type. | ## Verifying webhook signatures Every webhook request includes two headers for signature verification: | Header | Description | |--------|-------------| | `X-SendOps-Signature` | The HMAC-SHA256 signature, prefixed with `v1=`. | | `X-SendOps-Timestamp` | The Unix timestamp (seconds since epoch) when the request was sent. | The signature is computed over the timestamp and the raw request body joined by a `.` separator. This binds the timestamp to the payload, preventing replay attacks. Always verify this signature before processing a webhook payload to ensure the request originated from SendOps and was not tampered with in transit. ### Verification steps 1. Extract the `X-SendOps-Timestamp` header value. 2. Extract the raw request body as a byte string. Do not parse or transform it before verification. 3. Concatenate the timestamp, a literal `.` character, and the raw body: `timestamp + "." + body`. 4. Compute an HMAC-SHA256 digest of this string using your signing secret as the key. 5. Prefix the hex-encoded digest with `v1=` and compare it to the `X-SendOps-Signature` header using a constant-time comparison function. 6. If the signatures match, the payload is authentic. If they do not match, reject the request with a `401` status code. ### Pseudocode example ``` secret = get_env("SENDOPS_WEBHOOK_SECRET") raw_body = request.get_raw_body() timestamp = request.headers["X-SendOps-Timestamp"] signed_content = timestamp + "." + raw_body expected_signature = "v1=" + hmac_sha256(key: secret, message: signed_content).to_hex() received_signature = request.headers["X-SendOps-Signature"] if not constant_time_equal(expected_signature, received_signature): respond(status: 401, body: "Invalid signature") return event = parse_json(raw_body) # Process the event... respond(status: 200) ``` Always use a constant-time string comparison function when checking signatures. A standard string equality check can leak information through timing differences, potentially allowing an attacker to forge valid signatures. ## Responding to webhooks Your endpoint must return an HTTP `2xx` status code within **10 seconds** to acknowledge receipt. SendOps considers any non-`2xx` response or timeout as a delivery failure and will retry. You do not need to process the event before responding. A recommended pattern is to store the raw payload in a queue and return `200` immediately, then process the event asynchronously. ## Retry policy When a webhook delivery fails, SendOps retries up to 2 additional times: | Attempt | Delay | |---------|-------| | Initial delivery | Immediate | | 1st retry | 5 minutes | | 2nd retry | 15 minutes | After 3 total attempts, the delivery is marked as failed. You can manually resend failed deliveries from the delivery log. ### Auto-disable on consecutive failures If a webhook endpoint fails **10 consecutive deliveries** (across any notification types), SendOps automatically disables the endpoint to prevent further failed requests. The endpoint can be re-enabled from **Connections > Webhooks** after resolving the issue. You can view delivery history and failure details for each endpoint in **Connections > Webhooks** by clicking on an endpoint. SendOps guarantees **at-least-once delivery** for webhooks. In rare cases (e.g., your server responds with a `200` but the response is lost in transit), the same event may be delivered more than once. Use the `id` field in the payload to deduplicate events on your end. ## Managing endpoints From **Connections → Webhooks**, you can: - **Enable / disable** — temporarily stop deliveries to an endpoint without deleting it. - **Edit subscriptions** — change which notification types are routed to the endpoint. - **Rotate secret** — generate a new signing secret. The previous secret remains valid for 24 hours to give you time to update your server. - **View delivery log** — inspect recent deliveries, including response status codes, latency, and payload contents. - **Delete** — permanently remove the endpoint and all its delivery history. ## What's next? - Review the full list of events you can subscribe to in [Notification Types](/notifications/notification-types). - Configure which notifications reach you in [Configuring Notifications](/notifications/configuring-notifications). --- # Template Management Source: https://help.sendops.dev/templates/template-management Section: Templates > Connect a GitHub repository, sync email templates, validate Handlebars syntax, and deploy to SES — all from the SendOps Templates page. SendOps manages email templates entirely through GitHub. There is no in-app template editor — you connect a GitHub repository, and SendOps syncs your template files, validates them, extracts variables, and deploys them to Amazon SES. Templates use **Handlebars** syntax for dynamic content. If you have existing email templates in your SES account, you can export them as Git-ready files and import them into SendOps. See [Importing from SES](/templates/importing-from-ses) for the step-by-step workflow. ## Connecting a GitHub repository The GitHub connection is set up directly from the **Templates** page (not from Settings). Navigate to **Templates** in the sidebar. If no repository is connected, you'll see the connection card. Click **Connect Repository**. You'll be redirected to GitHub to install the SendOps GitHub App on your account or organization. The app requests read access to repository contents and pull request events. Choose the **repository owner** and **repository name** from the dropdowns. Only repositories where the GitHub App is installed are shown. Select the branch SendOps should sync from (typically `main` or `master`). Only templates on this default branch are deployed to SES. Optionally specify a directory path within the repository (e.g. `templates/` or `email/ses/`). SendOps only looks for template files in this directory. Leave blank to scan the entire repository root. ### Connection statuses | Status | Meaning | |--------|---------| | **Active** | The GitHub App is installed and working. Pushes trigger automatic syncs. | | **Disconnected** | The connection was removed from SendOps. Re-connect to resume syncing. | | **Suspended** | The GitHub App installation was suspended on the GitHub side (e.g. by an org admin). Re-activate the app in GitHub to restore the connection. | You can refresh the repository list at any time if you've added new repos to the GitHub App installation. ## Template sync Once connected, SendOps syncs templates automatically whenever you push to the default branch. You can also trigger a manual sync by clicking **Sync Now**. During a sync, SendOps: 1. Reads template files from the configured path 2. Validates each template (syntax and SES limits) 3. Extracts template variables 4. Deploys valid templates to SES (default branch only) ### Sync statuses Each sync shows one of these statuses: | Status | Meaning | |--------|---------| | **Synced** | All templates synced and deployed successfully. | | **Synced with errors** | Some templates failed validation and were not deployed. | | **Failed** | The sync could not complete (e.g. GitHub API error, permission issue). | The sync indicator on the Templates page shows the last sync time and lets you view **sync history** — a list of past syncs with their status, timestamp, commit SHA, and any errors. ### Why a deploy might be skipped A template is synced but not deployed to SES when: - The template has validation errors - The push was to a non-default branch - The template file type is not supported ## Supported file types SendOps recognizes these file extensions as templates: - `.html` - `.hbs` - `.handlebars` The file name (without extension) becomes the SES template name. For example, `welcome-email.hbs` creates an SES template named `welcome-email`. ## Manifest file For advanced configuration, you can add a `sendops.json` manifest file to your repository root. The manifest lets you explicitly list which files to sync, map file names to specific SES template names, and define test data profiles that sync alongside your templates. Without a manifest, SendOps scans the template path for all supported files automatically. See [Manifest File](/templates/manifest-file) for the full format reference. ## Handlebars syntax Templates use [Handlebars](https://handlebarsjs.com/) syntax for dynamic content — not just simple `{{variable}}` substitution. SendOps supports the full set of built-in Handlebars block helpers: ### Variables ```handlebars

Hello, {{name}}!

Your order {{order_id}} has shipped to {{shipping_address}}.

``` ### Conditional blocks ```handlebars {{#if premium_member}}

Thank you for being a premium member!

{{else}}

Upgrade to premium for exclusive benefits.

{{/if}} {{#unless unsubscribed}} {{/unless}} ``` ### Iteration ```handlebars {{#each items}} {{this.name}} {{this.price}} {{/each}} ``` ### Scoped context ```handlebars {{#with shipping_address}}

{{street}}, {{city}}, {{state}} {{zip}}

{{/with}} ``` SendOps manages the template definition in SES, but your application provides the variable values when calling the SES `SendTemplatedEmail` or `SendBulkTemplatedEmail` API. ## Template listing The Templates page shows all synced templates with two view modes: - **Card view** — visual cards showing template name, validation status, and last sync time - **Table view** — compact list with sortable columns You can **search** by template name and **filter** by validation status (valid, invalid, all). A **branch selector** lets you switch between branches to view templates on any branch, though only the default branch deploys to SES. ## Template detail Click any template to open its detail page, which has two tabs: ### Overview tab The Overview tab shows: - **Source and preview** — side-by-side view of the raw Handlebars source code on the left and the rendered HTML preview on the right - **Validation errors** — if the template is invalid, errors are listed with the specific **line and column** number so you can locate issues quickly - **Variables card** — a list of all variables extracted from the template, including their inferred types - **SES deployment info** — the SES template name, region, and deployment status ### Test Data tab The Test Data tab lets you manage test data profiles for previewing and testing the template. See [Previews & Test Data](/templates/template-versioning) for full details. ## Template validation Every synced template is automatically validated against two sets of rules: **Handlebars syntax** — the template must be valid Handlebars. Unclosed blocks, malformed expressions, and syntax errors are caught and reported with line and column numbers. **SES limits** — Amazon SES imposes limits on templates: | Limit | Value | |-------|-------| | Maximum template file size | 1 MB | | Maximum subject line length | 140 characters | | Maximum templates per region | 20,000 | Templates that fail validation are still synced to SendOps (so you can see the errors) but are **not deployed to SES**. ## Variables extraction SendOps automatically extracts all variables from your templates and displays them on the template detail page. The extractor understands Handlebars syntax, so it correctly identifies variables inside `{{#if}}`, `{{#each}}`, and `{{#with}}` blocks, and infers basic types (string, boolean, array, object) from how variables are used. ## What's next? - [Pull Requests & Compare](/templates/github-integration) — review template changes through PRs and compare branches. - [Previews & Test Data](/templates/template-versioning) — preview templates, manage test data profiles, and send test emails. - [Manifest File](/templates/manifest-file) — control template naming and sync git-based test data profiles. - [How Email Flows](/sending-email/email-flow) — understand how templates fit into the sending pipeline. --- # Importing from SES Source: https://help.sendops.dev/templates/importing-from-ses Section: Templates > Export your existing SES email templates as a Git-ready zip, commit them to a repository, and connect it to SendOps for version-controlled template management. If you already have email templates in Amazon SES, you can export them and bring them into SendOps in a few minutes. Once imported, your templates are version-controlled in Git and managed entirely through SendOps — you won't need to create or edit templates directly in the SES console again. This is a one-time export to move your existing SES templates into a Git-based workflow. After the import, all template changes happen through your GitHub repository and are synced to SES automatically by SendOps. ## Prerequisites Before you start, make sure: - Your **AWS account is connected** to SendOps. If you haven't done this yet, see [Connecting AWS](/aws-setup/connecting-aws). - You have at least one email template in your SES account. - You have a GitHub account where you can create a new repository (or use an existing one). - Your SendOps role has the **SES Template Export** permission. Owners, Org Admins, Infra Admins, and Developers can export. All roles can view the template list. See [Team Members & Roles](/team/members-and-roles) for details. ## Overview The import workflow has four steps: 1. **Export** your SES templates from the SendOps dashboard as a zip file 2. **Unzip** and review the exported files 3. **Push** the files to a GitHub repository 4. **Connect** the repository in SendOps From that point on, you edit templates in Git, push changes, and SendOps validates and deploys them to SES automatically — just like any other SendOps-managed template. ## Step 1: Export your SES templates There are two places to start the export, depending on whether you've already connected a GitHub repository. ### If no GitHub repository is connected When you open the **Templates** page and SendOps detects templates in your SES account, you'll see a prompt showing how many templates were found, with a **Download Templates** button. Click **Download Templates** to open the export dialog. ### If a GitHub repository is already connected If you've already connected a repository (perhaps with different templates, or an empty one), click the **Import from SES** button in the page header. ### The export dialog The export dialog lists all email templates currently in your SES account. Review the list to confirm which templates will be included. Click **Export All** to start the export. SendOps fetches each template from SES, converts it to a Handlebars file, generates a `sendops.json` manifest, and packages everything into a zip archive. This may take a moment depending on how many templates you have. When the export completes, the zip file downloads automatically. ## Step 2: Review the exported files Unzip the downloaded file. You'll find a `sendops-templates` folder with this structure: ``` sendops-templates/ ├── sendops.json ├── templates/ │ ├── welcome-email.hbs │ ├── welcome-email.txt │ ├── order-confirmation.hbs │ ├── password-reset.hbs │ └── ... ├── .gitignore └── README.md ``` ### What's in the zip | File | Purpose | |------|---------| | `sendops.json` | Manifest file that maps each template to its file path. SendOps reads this to know which files to sync. See [Manifest File](/templates/manifest-file) for the full format reference. | | `templates/*.hbs` | Your SES template bodies exported as Handlebars files. HTML templates are saved as `.hbs` files. If a template has both HTML and plain-text bodies, the text version is saved as a separate `.txt` file alongside the `.hbs`. | | `.gitignore` | Standard ignore rules for macOS and editor temporary files. | | `README.md` | A quick-start reference with the basic steps. | The `sendops.json` manifest is already formatted for SendOps — no manual conversion is needed. It maps each SES template name to its exported file: ```json { "templates": { "welcome-email": { "path": "templates/welcome-email.hbs" }, "order-confirmation": { "path": "templates/order-confirmation.hbs" } } } ``` The exported manifest is a working starting point. You can customize it later — for example, to add test data profiles or rename templates. See [Manifest File](/templates/manifest-file) for all available options. ## Step 3: Push to GitHub Initialize a Git repository in the exported folder and push it to GitHub. Open a terminal in the `sendops-templates` folder and run: ```bash git init git add . git commit -m "Import SES templates" ``` Create a new repository on GitHub (public or private — SendOps works with both). You can do this from [github.com/new](https://github.com/new) or using the GitHub CLI: ```bash gh repo create my-email-templates --private --source=. --push ``` If you created the repository on the web, add it as a remote and push: ```bash git remote add origin git@github.com:your-org/my-email-templates.git git branch -M main git push -u origin main ``` ## Step 4: Connect the repository in SendOps Now connect your new GitHub repository to SendOps so it can sync and deploy your templates. In the SendOps dashboard, navigate to **Templates**. Click **Connect Repository** (or **Connect GitHub Repository** if this is your first connection). You'll be redirected to GitHub to install the SendOps GitHub App if you haven't already. Choose the repository you just pushed to. Select the branch (typically `main`) and set the template path to match your folder structure — if you pushed the contents of `sendops-templates` directly, leave the path empty. If you pushed the `sendops-templates` folder itself, set the path to `sendops-templates`. SendOps automatically triggers a sync after connecting. It reads your `sendops.json` manifest, validates each template, and deploys them to SES. You'll see the sync status on the Templates page. For full details on the GitHub connection setup, see [Template Management](/templates/template-management#connecting-a-github-repository). ## What happens next Once connected, SendOps takes over template management: - **Pushes to the default branch** trigger automatic syncs — your templates are validated and deployed to SES. - **Pull requests** that touch template files get validation checks and rendered previews directly in GitHub. - **The Templates page** shows all your templates with their validation status, variables, and sync history. - **Test data profiles** can be added to the manifest to preview templates with realistic data. See [Previews & Test Data](/templates/template-versioning). From this point on, treat your GitHub repository as the source of truth for templates. Edit files in Git, open pull requests for review, and let SendOps handle validation and deployment to SES. After connecting, changes made directly in the SES console will be overwritten on the next sync. Always make template changes through your Git repository. ## Permissions The SES template export requires specific permissions: | Action | Required permission | Roles | |--------|-------------------|-------| | View the SES template list and export status | `templates.ses_export.view` | All roles | | Trigger an export and download the zip | `templates.ses_export.execute` | Owner, Org Admin, Infra Admin, Developer | If you don't have the required permission, the **Export All** button will be disabled. Contact your organization's Owner or Admin to request access. ## What's next? - [Template Management](/templates/template-management) — learn about syncing, validation, and the full template lifecycle. - [Pull Requests & Compare](/templates/github-integration) — review template changes through PRs and compare branches. - [Manifest File](/templates/manifest-file) — customize template naming and add test data profiles. - [Previews & Test Data](/templates/template-versioning) — preview templates and send test emails. --- # Pull Requests & Compare Source: https://help.sendops.dev/templates/github-integration Section: Templates > Review template changes through pull requests directly in SendOps. Approve, request changes, comment, and compare templates across branches. When your team uses pull requests to manage template changes, SendOps automatically detects PRs that touch template files and lets you review them without leaving the dashboard. You can approve, request changes, and comment — all posted back to GitHub. A separate Compare tool lets you diff templates between any two branches. ## Pull request detection Any pull request in your connected repository that modifies files in the configured template path automatically appears on the **Pull Requests** page in SendOps. You don't need to configure anything — if a PR touches a template file, SendOps picks it up. ## PR list The Pull Requests page shows all open PRs that affect templates. Each entry displays: - **PR title and number** — links to the PR on GitHub - **Branches** — source and target branch (e.g. `feature/new-welcome → main`) - **Templates changed** — count of added, modified, and removed templates - **Validation status** — whether the changed templates pass validation on the source branch - **Review status** — current review state (pending, approved, changes requested) ## Reviewing a pull request Click a PR to open the detail page, where you can review all template changes and submit a review. ### Changed templates The detail page lists every template affected by the PR, categorized as: - **Modified** — templates that exist on both branches but have different content. Shows a side-by-side diff with additions highlighted in green and removals in red. - **Added** — new template files introduced in the PR. Shows the full template source. - **Removed** — template files deleted in the PR. Each changed template also shows its validation status on the source branch, so you can catch errors before merging. ### Submitting a review Use the review card at the top of the PR detail page to submit your review: Add a comment explaining your review. This is posted as a review comment on the GitHub PR. Select one of three actions: - **Approve** — approve the PR. This posts an approval review to GitHub. - **Request changes** — request changes before the PR can be merged. A comment is required when requesting changes. - **Comment** — post a comment without approving or requesting changes. Click **Submit Review**. Your review is posted to GitHub immediately and reflected on the PR detail page. Reviews submitted in SendOps are posted to GitHub via the GitHub API. They appear on the PR in GitHub just like any other review. Team members can review in either SendOps or GitHub — both are reflected on the PR. ## Compare tool The Compare page lets you diff templates between any two branches, independent of pull requests. This is useful for: - Checking what's changed on a feature branch before opening a PR - Comparing two long-lived branches (e.g. `staging` vs `main`) - Reviewing accumulated changes across multiple commits ### Using Compare Navigate to **Templates → Compare** (or click the **Compare** button on the Templates page). Choose a **base branch** and a **compare branch** from the dropdowns. The base is typically your default branch. SendOps shows all templates that differ between the two branches, categorized as added, modified, or removed. Modified templates show a side-by-side diff. ## Branch-based development You can view templates on any branch using the branch selector on the Templates page. This lets you preview how templates look on a feature branch before merging. Templates on non-default branches are synced and validated in SendOps, but they are **not deployed to SES**. Only merging (or pushing directly) to the default branch triggers deployment. This means you can safely iterate on template changes in a feature branch without affecting production. When viewing templates on a non-default branch, SendOps shows a warning banner indicating that you're looking at a branch that is not deployed. ## What's next? - [Template Management](/templates/template-management) — connecting GitHub, sync, validation, and template structure. - [Previews & Test Data](/templates/template-versioning) — preview templates, manage test data profiles, and send test emails. --- # Previews & Test Data Source: https://help.sendops.dev/templates/template-versioning Section: Templates > Preview templates with test data profiles, send test emails, and manage sample data for your Handlebars email templates in SendOps. SendOps lets you preview templates with real data, manage test data profiles, and send test emails — all from the template detail page. Test data profiles can be synced from your repository or created directly in SendOps. ## Template preview Every template has a side-by-side preview on the **Overview** tab of the template detail page. The left panel shows the raw Handlebars source code, and the right panel shows the rendered HTML output. The preview updates automatically when you select a different test data profile, so you can see how the template looks with different data without leaving the page. ## Test data profiles Test data profiles provide the variable values used to render template previews and send test emails. Each template can have multiple profiles to test different scenarios (e.g. a profile for a premium user, one for a free user, one with a long list of items). ### Profile types There are two types of test data profiles: **Git-synced profiles** — defined in your repository's [`sendops.json` manifest file](/templates/manifest-file). These profiles are read-only in SendOps and update automatically when you push changes to your repository. Use these for canonical test data that the whole team shares. **User-managed profiles** — created directly in SendOps by any team member. These can be edited, duplicated, and deleted freely. Use these for personal testing scenarios or quick experiments. ### Managing profiles From the **Test Data** tab on the template detail page: - **Create** — click **New Profile** to create a user-managed profile. Give it a name and provide JSON data. - **Edit** — open any user-managed profile to modify its data. Git-synced profiles are read-only. - **Duplicate** — copy an existing profile (either type) as a starting point for a new user-managed profile. - **Delete** — remove a user-managed profile. Git-synced profiles cannot be deleted from SendOps. - **Set as default** — mark a profile as the default. The default profile is automatically selected when previewing or sending a test email. ### Test data editor When creating or editing a profile, the editor has two tabs: **JSON tab** — edit the raw JSON directly. This gives you full control over the data structure, including nested objects and arrays. ```json { "name": "Jane Smith", "premium_member": true, "items": [ { "name": "Widget", "price": "$9.99" }, { "name": "Gadget", "price": "$24.99" } ], "shipping_address": { "street": "123 Main St", "city": "Springfield", "state": "IL", "zip": "62701" } } ``` **Form tab** — a form-based editor with type-aware inputs. Strings show text fields, booleans show toggles, and arrays let you add or remove items. This is convenient for quick edits without writing JSON. ### Stub data generation When you create a new profile, SendOps can **auto-generate stub data** from the template's extracted variables. The generated data uses sensible defaults based on inferred types — strings get placeholder text, booleans default to `true`, arrays get one sample item, and objects get their nested fields populated. This gives you a working starting point that you can customize, rather than having to build the JSON from scratch. ### Variable warnings The test data editor shows warnings when your data doesn't match the template's variables: - **Missing variables** — variables used in the template that aren't present in your test data. The preview will show blank values for these. - **Extra variables** — data fields that don't correspond to any template variable. These are ignored during rendering but may indicate a typo or outdated data. - **Type mismatches** — when a variable is used as one type in the template (e.g. in an `{{#each}}` block, implying an array) but the test data provides a different type (e.g. a string). ## Sending test emails From the template detail page, click **Send Test** to deliver a rendered version of the template to a real inbox. Type the email address where the test should be delivered. Choose a profile to use for rendering. The preview updates in real-time as you switch profiles. Verify that the rendered template looks correct with the selected data. Click **Send**. SendOps renders the template with the selected profile data and sends the email. On success, SendOps shows details including the **message ID**, **subject line**, and **sending method** used. If your SES account is still in the sandbox, test emails can only be sent to verified email addresses. See [SES Sandbox & Production](/sending-email/ses-sandbox) for details on moving to production. ## Versioning SendOps tracks template versions through Git — there is no separate version history UI. Each time a sync processes a template, SendOps records the **content hash** and **commit SHA**. This lets SendOps detect whether a template actually changed between syncs. To view the history of a template's changes, use `git log` on the template file in your repository. To revert a template, revert the commit in Git and push to the default branch — SendOps syncs the restored content automatically. You don't need to configure anything for versioning. SendOps compares content hashes on each sync and only re-deploys templates that have actually changed. ## What's next? - [Template Management](/templates/template-management) — connecting GitHub, sync, validation, and template structure. - [Pull Requests & Compare](/templates/github-integration) — review template changes through PRs and compare branches. - [How Email Flows](/sending-email/email-flow) — understand how templates fit into the sending pipeline. --- # Manifest File Source: https://help.sendops.dev/templates/manifest-file Section: Templates > Use a sendops.json manifest file to control which templates SendOps syncs, map file names to SES template names, and define git-synced test data profiles. The `sendops.json` manifest file is an optional configuration file you place in your repository root. It gives you explicit control over which files are synced as templates, what SES template names they map to, and where test data profiles are stored. ## With vs without a manifest **Without `sendops.json`** — SendOps scans the configured template path for all `.html`, `.hbs`, and `.handlebars` files. Each file becomes a template named after the file (without extension). Test data profiles cannot be synced from the repository — you can only create them manually in SendOps. **With `sendops.json`** — SendOps reads the manifest instead of scanning the directory. Only templates listed in the manifest are synced. You control the template name independently of the file name, and you can define test data profiles that sync automatically from your repository. If `sendops.json` exists but has fatal errors (invalid JSON, missing `templates` key, empty template map), SendOps logs a warning and falls back to directory scanning. Check the sync history for warnings if templates aren't appearing as expected. ## File location and limits - **Location**: repository root (not inside the template path) - **Maximum size**: 64 KB ## Format ```json { "templates": { "welcome-email": { "path": "emails/welcome.html", "testdata": "fixtures/welcome/" }, "order-confirmation": { "path": "emails/order-confirm.hbs", "testdata": "fixtures/order-confirm.json" }, "password-reset": { "path": "emails/password-reset.html" } } } ``` ### Fields A map of template entries. Each key is the **template name** that will be used in SES. Must contain at least one entry. The key can be any string — it doesn't need to match the file name. This is how you control the SES template name independently of your file structure. The repository-relative path to the template file. - Must not be empty - Must not be an absolute path (no leading `/`) - Must not contain directory traversal (`../`) If any of these rules are violated, the entry is **skipped** and a warning is logged. The repository-relative path to test data for this template. Can point to either: - **A directory** (path ends with `/`) — all `.json` files in the directory are loaded as test data profiles - **A single file** (path ends with `.json`) — the file is loaded as one or more test data profiles If omitted, the template syncs normally but no git-synced test data profiles are created. If the path has an invalid extension or contains directory traversal, a warning is logged and test data is skipped for that template. ## Template naming The **key** in the `templates` map becomes the SES template name. This lets you decouple the file name from the template name: ```json { "templates": { "transactional-welcome": { "path": "src/emails/welcome-v2.hbs" } } } ``` Here the file is `welcome-v2.hbs` but the SES template is named `transactional-welcome`. Without a manifest, the SES template name is always derived from the file name (minus extension), so `welcome-v2.hbs` would create a template named `welcome-v2`. ## Test data profiles Test data profiles defined in the manifest are synced as **git-synced profiles** — they appear in SendOps as read-only and update automatically on each sync. See [Previews & Test Data](/templates/template-versioning) for how profiles work in the UI. ### Single file format A test data file can be either a JSON **object** or a JSON **array**. **Object** — creates one profile named after the file: ```json title="fixtures/welcome.json" { "name": "Jane Smith", "premium_member": true, "signup_date": "2025-01-15" } ``` This creates a profile named `welcome` (from the file name `welcome.json`). **Array** — creates multiple profiles from a single file. Each element must include a `_name` field: ```json title="fixtures/welcome.json" [ { "_name": "Premium user", "name": "Jane Smith", "premium_member": true }, { "_name": "Free user", "name": "John Doe", "premium_member": false } ] ``` This creates two profiles: "Premium user" and "Free user". The `_name` field is stripped from the stored profile data — it's only used to name the profile. ### Directory format When `testdata` points to a directory, every `.json` file in that directory is loaded using the same rules as single files (object or array). This lets you organize test data into separate files: ``` fixtures/ └── welcome/ ├── premium-user.json → profile "premium-user" ├── free-user.json → profile "free-user" └── edge-cases.json → array with multiple profiles ``` Each file is limited to 64 KB. ### Profile lifecycle On each sync, SendOps: 1. Upserts all profiles from the manifest (creating new ones or updating existing ones based on content hash) 2. Deletes any git-synced profiles that are no longer in the manifest This means: - **Renaming** a test data file creates a new profile and deletes the old one - **Removing** the `testdata` field from a manifest entry deletes all git-synced profiles for that template - **Removing** all files from a test data directory deletes all git-synced profiles for that template - **User-managed profiles** (created in the SendOps UI) are never affected by manifest syncs ## Unknown fields Unknown top-level fields in `sendops.json` are ignored with a warning. This means you can add comments or metadata fields without breaking the sync, but you'll see warnings in the sync history: ```json { "version": 1, "templates": { ... } } ``` This logs: `unknown top-level field "version" ignored`. ## Validation errors ### Fatal errors

Sync falls back to directory scan

| Error | Cause | |-------|-------| | File too large | `sendops.json` exceeds 64 KB | | Invalid JSON | Syntax error in the file | | Missing `templates` | The `templates` key is not present | | Empty template map | `templates` is present but has no entries | ### Warnings

Individual entries skipped or adjusted

| Warning | Cause | Effect | |---------|-------|--------| | Empty template name | A key in `templates` is an empty string | Entry skipped | | Empty path | `path` field is missing or empty | Entry skipped | | Absolute path | `path` starts with `/` | Entry skipped | | Path traversal | `path` contains `../` | Entry skipped | | Invalid testdata extension | `testdata` doesn't end with `/` or `.json` | Template syncs, test data skipped | | Testdata path traversal | `testdata` contains `../` | Template syncs, test data skipped | Warnings appear in the sync history on the Templates page. ## Example: full manifest ```json { "templates": { "welcome": { "path": "templates/welcome.hbs", "testdata": "testdata/welcome/" }, "order-confirmation": { "path": "templates/order-confirmation.hbs", "testdata": "testdata/order-confirmation.json" }, "password-reset": { "path": "templates/password-reset.html", "testdata": "testdata/password-reset/" }, "weekly-digest": { "path": "templates/marketing/weekly-digest.hbs" } } } ``` With a matching directory structure: ``` repo/ ├── sendops.json ├── templates/ │ ├── welcome.hbs │ ├── order-confirmation.hbs │ ├── password-reset.html │ └── marketing/ │ └── weekly-digest.hbs └── testdata/ ├── welcome/ │ ├── new-user.json │ └── returning-user.json ├── order-confirmation.json └── password-reset/ └── standard.json ``` ## What's next? - [Template Management](/templates/template-management) — connecting GitHub, sync, validation, and Handlebars syntax. - [Previews & Test Data](/templates/template-versioning) — using test data profiles for previews and test emails. --- # Team Members & Roles Source: https://help.sendops.dev/team/members-and-roles Section: Team & Access > Understand the eight roles in SendOps — Owner, Org Admin, Infra Admin, Developer, Marketer, Financial, Customer Support, and Viewer — and learn how to invite teammates to your organization. SendOps uses a role-based access model with eight roles. Every person in your organization is assigned exactly one role, and that role determines what they can see and do across the dashboard. The sidebar navigation automatically adapts to show only the sections relevant to each role. ## Roles overview | Capability | Owner | Org Admin | Infra Admin | Developer | Marketer | Financial | Support | Viewer | |---|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:| | **Organization Settings** | ✓ | ✓ | — | — | — | — | — | — | | **Team Management** | ✓ | ✓ | — | — | — | — | — | — | | **Billing — Plan & Usage** | ✓ | Read | — | — | — | ✓ | — | — | | **Billing — Invoices & Payments** | ✓ | — | — | — | — | ✓ | — | — | | **Billing — SES Cost Estimates** | ✓ | — | — | — | — | ✓ | — | — | | **AWS Connections** | ✓ | ✓ | ✓ | — | — | — | — | — | | **Domains & Tracking Domains** | ✓ | ✓ | ✓ | Read | Read | — | Read | Read | | **Channels** | ✓ | ✓ | ✓ | ✓ | Read | — | Read | Read | | **Templates** | ✓ | ✓ | Read | ✓ | Read | — | Read | Read | | **SES Template Export** | ✓ | ✓ | ✓ | ✓ | View | — | — | View | | **GitHub Integration** | ✓ | ✓ | — | ✓ | — | — | — | — | | **Reports & Analytics** | ✓ | ✓ | Read | ✓ | ✓ | Read | ✓ | Read | | **Recipient Search** | ✓ | ✓ | ✓ | ✓ | — | — | ✓ | — | | **Suppression Lists** | ✓ | ✓ | — | Read | ✓ | — | ✓ | Read | | **Notifications** | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | Read | Read | | **Audit Log** | ✓ | ✓ | Read | Read | Read | Read | Read | Read | | **Setup Guide** | ✓ | ✓ | ✓ | Read | Read | — | — | Read | Every organization has exactly one Owner. You can transfer ownership to another team member directly from Workspace Settings — see [Transferring ownership](#transferring-ownership) below. ## Role details ### Owner The Owner has full, unrestricted access to the organization. In addition to everything an Org Admin can do, the Owner can: - **Transfer ownership** — hand the Owner role to another team member directly from [Workspace Settings](/team/account-settings). See [Transferring ownership](#transferring-ownership) below. - **Delete the organization** — permanently remove the organization and all of its data. The person who creates the organization is automatically assigned the Owner role. There can only be one Owner per organization. ### Org Admin Org Admins have full access to the organization except for deletion, ownership transfer, and detailed billing management. They can: - Add, remove, and change roles for team members. - View the current plan and usage limits (read-only — invoices, payment methods, and plan changes are managed by the Owner or Financial role). - Manage all infrastructure settings including AWS connections, domains, channels, and tracking domains. - Create and edit templates. - Configure notification rules and webhooks. - View the [audit log](/team/audit-log). ### Infra Admin Infra Admins focus on infrastructure and connectivity. They can: - Manage AWS connections, domains, and tracking domains. - Manage channels. - Access the setup guide. - View templates, reports, and the [audit log](/team/audit-log) (read-only). - Manage notification preferences. Infra Admins cannot manage team members, billing, template editing, or GitHub connections. They can export existing SES templates — see [Importing from SES](/templates/importing-from-ses). ### Developer Developers focus on templates and day-to-day email operations. They can: - Create and edit templates. - Manage GitHub connections and template syncs. - Manage channels. - Manage notification preferences. - View domains, tracking domains, reports, suppression lists, and the [audit log](/team/audit-log) (read-only). Developers cannot manage team members, billing, AWS connections, or organization settings. ### Marketer Marketers focus on analytics, reporting, and suppression management. They can: - View and export reports and analytics. - Manage suppression lists. - Manage notification preferences. - View templates, channels, domains, and the [audit log](/team/audit-log) (read-only). Marketers cannot modify infrastructure settings, manage templates, or access AWS or GitHub connections. ### Financial The Financial role is designed for finance and accounting teams who need access to billing, invoices, and cost data without seeing operational product features. Financial users can: - View and manage invoices, payment methods, and billing settings. - Download invoice PDFs and export billing history. - View the current plan and usage limits. - View estimated SES costs and export cost data. - View reports and analytics (read-only, for cost context). - View the [audit log](/team/audit-log) (read-only). - Manage their own notification preferences. Financial users cannot access templates, channels, domains, AWS connections, GitHub integrations, suppression lists, or organization settings. Their sidebar shows only the sections relevant to billing and finance. ### Customer Support The Customer Support role is designed for people who troubleshoot email delivery — looking up whether a specific email was delivered, bounced, or complained, searching by recipient, and managing suppression lists. Customer Support users can: - View and export reports and analytics. - Search by recipient email address to trace individual deliveries. - View and manage suppression lists — remove erroneously suppressed addresses so recipients can receive email again. - View templates, channels, and tracking domains (read-only, for delivery context). - View the [audit log](/team/audit-log) (read-only). - Manage their own notification preferences. Customer Support users cannot modify infrastructure settings, edit templates, access AWS or GitHub connections, manage billing, invite team members, or change organization settings. ### Viewer Viewers have read-only access across the dashboard. They can view reports, templates, channels, domains, suppression lists, the [audit log](/team/audit-log), and notification settings — but cannot modify anything. ## Inviting a team member In the SendOps dashboard, navigate to **System → Team**. You will see a list of current members and their roles. Click the **Invite Member** button at the top of the member list. Type the email address of the person you want to invite. They do not need a SendOps account yet — they will be prompted to create one (or sign in with Google or GitHub) when they accept the invitation. Select a role from the dropdown — **Org Admin**, **Infra Admin**, **Developer**, **Marketer**, **Financial**, **Customer Support**, or **Viewer**. You can change their role later from the same Team settings page. Click **Send Invite**. The invitee receives an email with a link to join your organization. Pending invitations appear in the member list with a "Pending" badge until accepted. The Free plan supports a single user. To invite additional team members, upgrade to the **Team** or **Business** plan. Visit [sendops.dev/pricing](https://sendops.dev/pricing) for details. ## Accepting an invitation There are two ways to accept an invitation to join an organization. ### From the email link When someone invites you, you receive an email showing the organization name, your assigned role, and who invited you. Click **Accept invitation** in the email to be taken directly to SendOps. - If you are already signed in, the invitation is accepted automatically and you are redirected to the organization's dashboard. - If you are not signed in, you will be prompted to sign in (or create an account) first. After signing in the invitation is processed. ### From the dashboard If you are already signed in to SendOps, pending invitations also appear as a banner at the top of your dashboard. The banner shows the organization name and role, with **Accept** and **Decline** buttons. If you have multiple pending invitations, the banner links to your **Profile** page where you can review and respond to each one. You can also view all pending invitations at any time from your **Profile** page. Each invitation shows the organization name, the role you have been offered, and when the invitation expires. From there you can accept or decline each invitation individually. You can belong to more than one organization. After accepting an invitation, SendOps switches to the new organization automatically. Use the organization switcher to move between organizations. Each organization has its own role assignment, so you may be an Admin in one organization and a Viewer in another. Invitations expire after 7 days. If an invitation has expired, the acceptance page will let you know — ask the organization Owner or Admin to send a new one from **System → Team**. ## Changing a member's role Owners and Org Admins can change any member's role from **System → Team**. Click the role badge next to a member's name, select the new role, and confirm. Role changes take effect immediately. ## Removing a member To remove a member, click the menu icon next to their name in the Team settings and select **Remove**. The member immediately loses access to the organization. Their past actions remain recorded in the [audit log](/team/audit-log). You cannot remove a member who has a pending ownership transfer. Cancel the transfer first from **Settings → Workspace**, then remove the member. ## Transferring ownership The Owner can transfer their role to any other active member of the organization. After the transfer, the former Owner is demoted to **Org Admin** and retains full access to the organization except for ownership transfer and workspace deletion. ### Initiating a transfer Navigate to **Settings → Workspace** and scroll to the **Danger Zone** section. Click **Transfer Ownership** to open the transfer modal. Choose the member you want to transfer ownership to. The member must have a verified email address. Review the details and confirm. Both you and the target member will receive an email notification. The target has **72 hours** to accept the transfer. While a transfer is pending, a banner appears on the **Team** page showing the target member's name, the expiry time, and a **Cancel Transfer** button. You can cancel the transfer at any time before it is accepted or expires. ### Accepting a transfer There are two ways the target member can accept an ownership transfer: **From the email link** — the invitation email contains an **Accept ownership** button that links directly to SendOps. If the target is not signed in, they will be prompted to sign in first. **From the dashboard** — a banner appears at the top of the target member's dashboard showing who initiated the transfer, with an **Accept** button. If the organization enforces two-factor authentication, the target member must have MFA set up on their account before they can accept the transfer. See [Multi-factor authentication](/team/account-settings#multi-factor-authentication) for setup instructions. ### What happens when a transfer is accepted - The target member becomes the new **Owner**. - The former Owner is changed to the **Org Admin** role. - Both users are signed out of all sessions and must sign in again. - Both users receive a confirmation email. - The new Owner sees a banner prompting them to review their [billing contact details](/billing/plans-and-usage) to ensure invoices and receipts go to the correct address. All ownership transfer actions — initiated, accepted, cancelled, and expired — are recorded in the [audit log](/team/audit-log). ### Cancellation and expiry - The Owner can **cancel** a pending transfer at any time. The target member receives a cancellation email. - If the target does not accept within **72 hours**, the transfer **expires automatically**. Both parties receive an expiry notification. - After a cancellation or expiry, you can immediately initiate a new transfer to the same or a different member. ## What's next? - Review the [Audit Log](/team/audit-log) to see a record of every change made in your organization. - Update your profile and security settings in [Settings & Profile](/team/account-settings). --- # Audit Log Source: https://help.sendops.dev/team/audit-log Section: Team & Access > Track every change made in your SendOps organization with the audit log — searchable, filterable, and designed for compliance. The audit log records every significant action taken within your SendOps organization. It provides a tamper-proof timeline of who did what and when, giving your team the transparency needed for security reviews and compliance requirements. The audit log is available on all plans. Your plan determines how far back you can see — check the [retention period](#retention-period) table below for details. ## What gets logged The audit log captures changes across every area of the dashboard: | Category | Example actions | |---|---| | **Team changes** | Member invited, member removed, role changed | | **Domain changes** | Domain added, domain verified, domain removed, DNS records updated | | **Channel changes** | Channel created, channel updated, channel deleted, identity assigned to channel | | **Template changes** | Template created, template updated, template deleted, template version published | | **Settings changes** | Organization name updated, notification rules changed, webhook configured | | **Billing events** | Plan upgraded, plan downgraded, payment method updated | | **AWS connection** | AWS account connected, AWS account disconnected, CloudFormation stack updated | | **Template export** | SES templates exported, export downloaded | | **Ownership transfers** | Transfer initiated, transfer accepted, transfer cancelled, transfer expired | Each log entry includes the following columns: - **Time** — the exact date and time of the action (displayed in your local timezone). - **Actor** — the team member who performed the action, identified by name and email. - **Resource** — the type and identifier of the affected resource. - **Operation** — the type of operation performed (e.g., created, updated, deleted). - **Summary** — a human-readable description of what was done. ## Searching and filtering Use the controls at the top of the audit log page to narrow results: - **Resource type** — filter by resource type using the dropdown (e.g., team, domain, channel, template, settings, billing). - **Operation type** — filter by the kind of operation using the dropdown (e.g., created, updated, deleted). - **Actor search** — search for a specific team member to see only their actions. Filters combine with AND logic, so selecting a resource type and an actor shows only that actor's actions on that resource type. ## Retention period Audit log entries are retained based on your plan: | Plan | Retention | |---|---| | **Free** | 7 days | | **Team** | 90 days | | **Business** | 1 year | ## Using the audit log for compliance The audit log helps your team meet common compliance requirements: - **Change tracking** — demonstrate that all infrastructure changes (domains, channels, AWS connections) are attributable to a specific person. - **Access reviews** — verify who was added or removed from the organization and when. - **Incident investigation** — trace the sequence of changes that led to a deliverability issue or misconfiguration. - **Billing accountability** — see who changed the plan or updated payment details. Audit log entries cannot be edited or deleted by any user, including the Owner. This ensures the log serves as a reliable record for compliance and investigation purposes. ## What's next? - Learn about the seven roles and their permissions in [Team Members & Roles](/team/members-and-roles). - Manage your personal profile and security settings in [Settings & Profile](/team/account-settings). --- # Settings & Profile Source: https://help.sendops.dev/team/account-settings Section: Team & Access > Manage your SendOps workspace settings, personal profile, security preferences, and billing configuration. SendOps separates settings into three areas: **Workspace Settings** for your shared workspace configuration, **Profile** for your personal account, and **Billing** for plan and payment management. ## Workspace Settings Navigate to **Settings → Workspace** to manage workspace-level configuration. These settings affect all members of the workspace and require the **Owner** or **Org Admin** role. ### General - **Workspace name** — the display name for your workspace, shown in the dashboard header and in team invitations (maximum 64 characters). - **Workspace URL slug** — the URL identifier for your workspace (3–48 characters, lowercase letters, numbers, and hyphens). Changing the slug updates all workspace URLs. SendOps redirects requests to the old slug for 30 days after a change. - **Workspace logo** — upload a custom logo that appears in the dashboard sidebar and in emails sent from SendOps on behalf of your workspace. Supported formats are PNG, JPEG, WebP, and GIF, with a maximum file size of 2 MB. You can replace or remove the logo at any time. ### Security - **Two-factor authentication enforcement** — toggle this on to require all workspace members to set up MFA on their accounts. Members without MFA will have 7 days to configure it before losing access. - **Session timeout** — configure how long inactive sessions remain valid before users are required to re-authenticate. Options are 1 hour, 4 hours, 8 hours (default), 24 hours, 7 days, and 30 days. ### Danger Zone - **Transfer ownership** — transfer the Owner role to another team member. The target has 72 hours to accept. After the transfer, you become an **Org Admin**. See [Transferring ownership](/team/members-and-roles#transferring-ownership) for the full workflow. Deleting a workspace removes all data, including channels, domains, templates, members, and reports. This action cannot be undone. - **Delete workspace** — permanently remove the workspace and all of its data. This action is available to the **Owner** only. You must type the workspace name exactly to confirm. Deleting a workspace also cancels any active subscription and revokes all member sessions. #### Leaving the demo workspace If you still have the demo workspace in your organization list and no longer need it, you can remove it from the Workspace Settings page. Open the demo workspace, navigate to **Settings → Workspace**, and click **Leave Demo Workspace**. This removes it from your organization list without affecting other members. ## Profile Navigate to **Profile** to manage your personal account settings. These settings are specific to you and do not affect other workspace members. ### Account details - **Display name** — the name shown to other team members and in the [audit log](/team/audit-log) (maximum 200 characters). - **Email address** — the address used for sign-in and notifications. Changing your email requires verification of the new address. This option is only available if you signed up with email and password. - **Avatar** — your profile photo, automatically pulled from your connected OAuth provider (Google or GitHub). There is no manual avatar upload. ### Password If you signed up with email and password, you can change your password from the Profile page. You will need to enter your current password and then set a new one. ### Connected accounts You can link your SendOps account to **Google** and **GitHub** for single sign-on. Once linked, you can sign in using those providers instead of (or in addition to) your email and password. - To connect a provider, click **Connect** next to the provider name and complete the OAuth flow. - To disconnect a provider, click **Disconnect**. You must always keep at least one active sign-in method. ### Multi-factor authentication Enable multi-factor authentication (MFA) to add a second layer of security when signing in. SendOps supports two MFA methods — you can use either or both. Go to **Profile** and find the **Multi-Factor Authentication** section. Click **Set up authenticator app**. Open your authenticator app (such as Google Authenticator, Authy, or 1Password) and scan the QR code displayed on screen. If you cannot scan it, click **Enter code manually** to reveal the secret key. Enter the six-digit code from your authenticator app, give the credential a name (e.g. "Work phone"), and click **Verify**. After verification, SendOps displays ten one-time recovery codes. **Save these in a secure location** such as a password manager. Each recovery code can be used once if you lose access to your authenticator app or security key. If you lose both your authentication devices and your recovery codes, you will need to contact [support@sendops.dev](mailto:support@sendops.dev) to regain access to your account. You can register a hardware security key (such as a YubiKey) or a platform authenticator (such as Touch ID or Windows Hello) as your second factor. In the **Multi-Factor Authentication** section, click **Set up security key**. Follow your browser's prompt to tap or activate your security key. Give the credential a name (e.g. "Office YubiKey") to help identify it later. If this is your first MFA method, SendOps displays ten one-time recovery codes. Save these in a secure location. #### Managing MFA credentials Once MFA is enabled, you can manage your credentials from the Profile page: - **Add additional credentials** — register more authenticator apps or security keys for redundancy. - **Rename credentials** — update the display name of any credential to keep them identifiable. - **Remove credentials** — delete a credential you no longer use. If you remove your last credential, MFA is disabled on your account. - **Regenerate recovery codes** — generate a fresh set of ten recovery codes. This immediately invalidates all previous codes. #### Signing in with MFA When MFA is enabled, after entering your email and password you are prompted to verify with one of your registered methods. You can switch between your authenticator app, security key, or a recovery code on the verification screen. ### Date and time preferences - **Timezone** — choose the timezone used to display dates and times throughout the dashboard. - **Follow browser timezone** — toggle this on to automatically use your browser's timezone instead of a manually selected one. - **Week start** — choose whether your week starts on Monday or Sunday, which affects calendar-based views and weekly report date ranges. ### Appearance - **Theme** — choose between **Light**, **Dark**, or **System** (follows your operating system preference). ### Pending invitations If you have been invited to join additional organizations, pending invitations appear on your Profile page. Each invitation shows the organization name, the role offered, and when it expires. You can **Accept** or **Decline** each invitation individually. ## Billing Billing has its own dedicated section in the sidebar with four tabs: - **[Plans & Usage](/billing/plans-and-usage)** — view your current plan, usage metrics, plan comparison, and add-ons. - **[Invoices](/billing/invoices)** — view, filter, and download PDF invoices. - **[Payment History](/billing/payment-history)** — see all payment transactions and export as CSV. - **[SES Cost Estimates](/billing/ses-cost-estimates)** — view estimated AWS SES sending costs by month and channel. Full billing access is available to the **Owner** and **Financial** roles. **Org Admins** can view the current plan and usage limits (read-only). See [Team Members & Roles](/team/members-and-roles) for the complete access matrix. ## Closing your account SendOps does not have a separate "close account" action. To close your account, delete all workspaces you own from **Settings → Workspace**. If you are a member (not Owner) of other workspaces, leave those organizations first. Once all your workspaces are deleted and you have no remaining organizations, your account is effectively closed and you will no longer be able to sign in. If you need your personal data removed entirely, contact [support@sendops.dev](mailto:support@sendops.dev). ## What's next? - Set up roles and invite teammates in [Team Members & Roles](/team/members-and-roles). - Review the [Audit Log](/team/audit-log) to see a history of changes in your workspace. --- # Plans & Usage Source: https://help.sendops.dev/billing/plans-and-usage Section: Billing > Understand the three SendOps plan tiers — Free, Team, and Business — and how to monitor your usage, switch plans, and manage add-ons. Navigate to **Billing** in the sidebar to view your current plan, usage, and available plan tiers. The Billing page is accessible to the **Owner**, **Org Admin** (read-only), and **Financial** roles. See [Team Members & Roles](/team/members-and-roles) for the full access matrix. ## Plans SendOps offers three plan tiers: | | Free | Team | Business | |---|---|---|---| | **Price** | $0 | $29/month | $149/month or $1,639/year | | **Seats** | 1 | 5 | 25 | | **Email templates** | 3 | 50 | 200 | | **Event retention** | 7 days | 90 days | 1 year | | **Notifications** | Email & in-app | All channels | All channels + presets | | **Add-ons** | — | — | Extra seats & templates | Visit [sendops.dev/pricing](https://sendops.dev/pricing) for the full feature comparison. ## Current plan and status The top of the Billing page shows your active plan tier, status badge, price, and renewal date. Possible statuses are: - **Active** — subscription is current and will renew automatically. - **Past Due** — a payment has failed. Update your payment method to avoid service interruption. - **Cancelled** — the subscription will not renew. You keep access until the end of the current billing period. - **Paused** — the subscription is temporarily paused. If a plan change is scheduled (e.g. a downgrade), a banner shows the upcoming change and the date it takes effect. ## Usage The Usage card shows your current consumption against your plan limits: - **Seats** — number of team members in the workspace. - **Email templates** — total templates created. - **Event retention** — how long email event data is kept. Usage bars turn amber when you reach 80% of a limit. ## Upgrading Click **Upgrade to Team** or **Upgrade to Business** on the plan comparison cards. If you don't have an existing subscription, you'll be redirected to a Stripe checkout page. If you already have a subscription, the plan change is applied immediately. For the Business plan, you can toggle between **Monthly** ($149/month) and **Annual** ($1,639/year — save $149) billing before upgrading. ## Downgrading Click **Downgrade to [Plan]** on the plan comparison card. Downgrades are scheduled for the end of your current billing period — you keep your current plan's features until then. A "Scheduled" badge appears on the target plan card, and a **Cancel downgrade** button lets you revert the change before it takes effect. Before downgrading, ensure your usage is within the target plan's limits. For example, if you have 10 team members and downgrade to Team (5 seats), you will need to remove members before the change takes effect. ## Add-ons (Business plan) Business plan subscribers can purchase additional capacity: - **Extra seats** — $10/seat/month, prorated to your billing cycle. - **Extra template packs** — 10 templates per pack at $5/pack/month, prorated. Adjust quantities using the controls in the Add-ons section and click **Save**. Changes are reflected on your next invoice. ## Managing payment methods Click **Manage Billing** to open the Stripe customer portal, where you can update your payment method and view your payment details. ## What's next? - View your [Invoices](/billing/invoices) and download PDFs. - Check your [Payment History](/billing/payment-history) for a record of all charges. - See [SES Cost Estimates](/billing/ses-cost-estimates) for estimated AWS sending costs. --- # Invoices Source: https://help.sendops.dev/billing/invoices Section: Billing > View, filter, and download PDF invoices for your SendOps subscription directly from the dashboard. Navigate to **Billing → Invoices** to view all invoices for your workspace. This tab is visible to the **Owner** and **Financial** roles. ## Invoice list The invoice table shows: | Column | Description | |---|---| | **Invoice** | The invoice number (e.g. `INV-0001`). | | **Date** | When the invoice was created. | | **Period** | The billing period the invoice covers. | | **Amount** | The total amount due, displayed in your subscription currency. | | **Status** | Current status badge — see below. | ### Invoice statuses - **Paid** — payment has been collected successfully. - **Open** — invoice has been issued and payment is pending. - **Draft** — invoice is being prepared and has not been finalized. - **Void** — invoice has been cancelled and is no longer payable. ### Filtering by status Use the status tabs at the top of the page to filter invoices: **All**, **Paid**, **Open**, or **Void**. ## Line item details Click any invoice row to expand it and see the individual line items — each showing a description, unit amount, and quantity. ## Downloading PDFs Click the download icon on any invoice row to save a PDF copy. PDF download requires the `billing.invoices.download` permission, available to the **Owner** and **Financial** roles. If you are on the Free plan or have just subscribed, the invoice list will be empty until your first charge is processed. ## What's next? - Review your [Payment History](/billing/payment-history) for a record of all charges and refunds. - Check your [Plans & Usage](/billing/plans-and-usage) to monitor limits and switch plans. --- # Payment History Source: https://help.sendops.dev/billing/payment-history Section: Billing > View a chronological record of all payment transactions and export billing history as CSV. Navigate to **Billing → History** to see a chronological list of all payment transactions for your workspace. This tab is visible to the **Owner** and **Financial** roles. ## Transaction list Each transaction shows: | Column | Description | |---|---| | **Date** | When the transaction was processed. | | **Description** | A summary of the charge (e.g. "Team plan — monthly"). | | **Amount** | The charge amount in your subscription currency. | | **Status** | Transaction outcome — **succeeded**, **pending**, or **failed**. | | **Payment Method** | The card brand and last four digits used for the charge. | The list is paginated. Use the **Newer** and **Older** buttons to navigate between pages. ## Exporting to CSV Click **Export CSV** to download a CSV file containing your full billing history. This is useful for accounting reconciliation or expense reporting. CSV export requires the `billing.history.export` permission, available to the **Owner** and **Financial** roles. If you don't have permission, the export button is disabled with a tooltip explaining why. ## What's next? - View individual [Invoices](/billing/invoices) and download PDFs. - Monitor your [SES Cost Estimates](/billing/ses-cost-estimates) for AWS sending costs. --- # SES Cost Estimates Source: https://help.sendops.dev/billing/ses-cost-estimates Section: Billing > View estimated AWS SES sending costs derived from your email volume, broken down by month and channel. Navigate to **Billing → SES Costs** to see estimated AWS SES spending based on your actual email volume. This tab is visible to the **Owner** and **Financial** roles. Cost figures are calculated from your email sending volume and the published SES rate card. They do not include data transfer fees, dedicated IP costs, or other AWS charges. Your actual AWS bill may differ. The rate card version used is shown as a badge in the toolbar. ## Current month projection The projection card at the top of the page shows four metrics for the current month: - **Month to Date** — total estimated cost so far this month, with the number of emails sent. - **Projected Total** — estimated cost for the full month based on your current daily run rate. A percentage indicator compares this to last month's total (green for lower, red for higher). - **Daily Run Rate** — average estimated cost per day so far this month. - **Month Progress** — a progress bar showing how far through the billing month you are. ## Monthly cost trend The bar chart shows your estimated SES sending cost for each month in the selected time range. Hover over any bar to see the exact cost. Use the range selector to view **3 months**, **6 months**, or **12 months** of history. ## Channel breakdown The channel breakdown table shows how your sending cost is distributed across your channels for the selected period: | Column | Description | |---|---| | **Channel** | The channel name. | | **Send Count** | Number of emails sent through this channel. | | **Estimated Cost** | Proportional share of the total estimated cost. | | **% of Total** | Visual bar showing the channel's share of total cost. | A footer row shows the combined totals. ## EC2 free tier toggle If you send email from an EC2-hosted application, AWS includes the first 62,000 emails per month at no charge. The **EC2 free tier** toggle (enabled by default) applies this discount to all cost calculations on the page. Turn it off to see the raw cost without the free tier discount — useful if you send from outside EC2 or want to see the full rate. ## Exporting cost data Click **Export CSV** to download cost data. You can choose to export: - **By Month** — one row per month with year, month, send count, and estimated cost. - **By Channel** — one row per channel with channel name, send count, and estimated cost. CSV export requires the `billing.ses_costs.export` permission, available to the **Owner** and **Financial** roles. ## How costs are calculated SendOps counts `Send` events in your email analytics data and applies the AWS SES rate card: - **Rate**: $0.10 per 1,000 emails sent. - **EC2 free tier**: First 62,000 emails/month are free (when the toggle is enabled). - **Channel allocation**: Each channel's cost is proportional to its share of total sends. The rate card version (currently `2025-01`) is displayed as a badge in the page toolbar. ## What's next? - Review your [Invoices](/billing/invoices) for your SendOps subscription charges. - Check [Plans & Usage](/billing/plans-and-usage) to see your current plan limits. --- # Adding a Domain Source: https://help.sendops.dev/domains/adding-a-domain Section: Identities & Domains > Add and verify a sending domain in SendOps. SendOps uses AWS SES to generate DKIM tokens and verify domain ownership through DNS records. Adding a domain is the third step of onboarding (after [creating your account](/getting-started/creating-your-account) and [connecting your AWS account](/aws-setup/connecting-aws)). Once a domain is verified, you can send email from any address on that domain. ## How domain verification works When you add a domain in SendOps, the following happens behind the scenes: 1. SendOps calls AWS SES to initiate domain verification for your domain. 2. SES generates three CNAME records for DKIM signing. 3. SendOps displays these records in the dashboard, along with recommended SPF and DMARC records, so you can add them to your DNS provider. 4. You can check verification status at any time using the **Refresh** button on each domain row. ## Adding your domain In the SendOps dashboard at [app.sendops.dev](https://app.sendops.dev), navigate to **Identities** in the sidebar (under the Infrastructure section). The page shows both Domain Identities and Email Identities in separate tables. In the Domain Identities section, click **Add Domain**. Type the domain you want to verify (e.g., `mail.example.com`), then click **Add**. The region is determined by your organization's AWS connection. SendOps calls SES to initiate verification. After a moment, the dashboard displays the DNS records you need to configure. Copy the DNS records from the SendOps dashboard and add them at your DNS provider (Route 53, Cloudflare, Namecheap, etc.). You will need to add the following records: | Type | Name | Value | Purpose | |------|------|-------|---------| | CNAME | `{token1}._domainkey.example.com` | `{token1}.dkim.amazonses.com` | DKIM signing (record 1 of 3) | | CNAME | `{token2}._domainkey.example.com` | `{token2}.dkim.amazonses.com` | DKIM signing (record 2 of 3) | | CNAME | `{token3}._domainkey.example.com` | `{token3}.dkim.amazonses.com` | DKIM signing (record 3 of 3) | The token values are unique to your domain and are generated by AWS SES. Use the copy buttons in the dashboard to avoid transcription errors. SendOps also displays recommended **SPF** and **DMARC** records for your domain. While not required for verification, these records improve deliverability and are strongly recommended. For detailed guidance on each record type, see [DNS Configuration](/domains/dns-configuration). Once the DNS records are in place, click the **Refresh** button on the domain row to check verification status. The domain status will change from **Pending** to **Verified** once SES confirms the records. DNS changes typically propagate within a few minutes, but in some cases can take **up to 72 hours** depending on your DNS provider and TTL settings. If your domain still shows as pending after a few hours, double-check that the records were entered exactly as shown in the dashboard. ## Importing existing domains from AWS SES If you already have domains configured in your AWS SES account, SendOps can import them automatically. There are two ways this happens: ### Automatic sync SendOps regularly syncs with your AWS SES account in the background. Any domains found in SES that aren't already in SendOps will appear on the Identities page with a **"New"** badge. You don't need to do anything — discovered domains show up automatically. For more details on how sync works, see [AWS Account Sync](/aws-setup/account-sync). ### During onboarding During the **Domain Verification** step of onboarding, you can click **Sync from AWS** to immediately scan your SES account. After syncing, SendOps shows: - **Managed identities** — domains already connected to SendOps. - **Unmanaged identities** — domains discovered from SES that are not yet connected. For each unmanaged domain, click **Connect** to bring it under SendOps management. Connected domains will appear on the Identities page and can be assigned to channels, tracked for health, and used for sending. You can also **Disconnect** a managed domain to stop managing it in SendOps without deleting it from SES. The **Delete** action permanently removes the domain from your AWS SES account, not just from SendOps. This cannot be undone. If you only want to hide a domain from your dashboard without affecting SES, use **Archive** instead. See [AWS Account Sync](/aws-setup/account-sync) for details on archiving. Sync discovers all identity types — both domains and individual email addresses. Any email identities found will also appear for you to connect or will be discovered automatically during background sync. ## Domain Identities table The Domain Identities table on the Identities page displays the following columns for each domain: | Column | Description | |--------|-------------| | **Domain** | Your sending domain name. May show a **"New"** badge if recently discovered from AWS, or a **"Detached"** / **"Archived"** badge. | | **Status** | Current verification status (Pending, Verified, or Failed). | | **DKIM** | Whether DKIM signing is configured and verified. | | **SPF** | Whether SPF records are detected. | | **DMARC** | Whether a DMARC policy is in place. | | **Channel** | A dropdown to assign the domain to a [channel](/channels/understanding-channels). | | **Health** | Health indicator derived from DKIM status: healthy (DKIM verified), critical (DKIM failed), or pending (in progress). | | **Actions** | **Refresh** to check verification status with SES. **Archive** (for detached domains) to hide from the list. **Restore** (for archived domains) to bring back. **Delete** to permanently remove from SES. | Archived domains are hidden by default. Enable the **Show archived** toggle above the table to see them. For more on entity lifecycle states and what "Detached" and "Archived" mean, see [AWS Account Sync](/aws-setup/account-sync). ## What's next? - Set up additional DNS records for SPF and DMARC in [DNS Configuration](/domains/dns-configuration). - If you need to verify individual email addresses instead of a full domain, see [Email Identities](/domains/email-identities). - Configure a [Tracking Domain](/domains/tracking-domains) for branded open and click tracking. --- # Email Identities Source: https://help.sendops.dev/domains/email-identities Section: Identities & Domains > Verify individual email addresses as sending identities in SendOps. Learn when to use email identities versus domain verification. In addition to verifying entire domains, SendOps lets you verify individual email addresses as sending identities. This is useful in specific scenarios where full domain verification is not practical or necessary. ## Email identity vs. domain verification | | Email Identity | Domain Verification | |---|---|---| | **Scope** | A single email address (e.g., `alerts@example.com`) | All addresses on a domain (e.g., `*@example.com`) | | **DNS required** | No | Yes | | **Verification method** | SES registers the identity and verification status is checked via the API | DNS records (TXT + CNAME) | | **Status** | Pending, Verified, or Failed | Pending, Verified, or Failed | | **Best for** | Quick testing, domains you don't own, sandbox mode | Production sending at any scale | | **Actions** | Refresh status, Delete | Refresh status, Delete | ### When to use email identities - **Sandbox testing** — When your SES account is still in the [SES sandbox](/sending-email/ses-sandbox), you can quickly verify a few email addresses to start testing without configuring DNS. - **Sending from domains you don't control** — If you need to send from an address on a domain where you cannot add DNS records (e.g., a partner's domain), you can verify just that address. - **Quick prototyping** — Verifying an email address takes seconds rather than waiting for DNS propagation. For production use, [domain verification](/domains/adding-a-domain) is strongly recommended. It covers every address on the domain, enables DKIM signing, and avoids the need to verify each address individually. ## Verifying an email address In the SendOps dashboard, go to **Identities** in the sidebar (under the Infrastructure section). Scroll down to the **Email Identities** section and click **Add Email Identity**. In the **Add Email Identity** dialog, type the full email address you want to verify (e.g., `noreply@example.com`) and click **Add Email Identity**. SendOps calls the SES `CreateEmailIdentity` API to register the address. After a moment, the identity appears in the Email Identities table with its current status. The identity will appear in the Email Identities table with a status of **Pending**, **Verified**, or **Failed**. Click the **Refresh** button (refresh icon) on the identity row to re-check the verification status from SES at any time. If your SES account is still in **sandbox mode**, you must verify both the sender and every recipient address. This means you can only send to email addresses that have also been verified. See [SES Sandbox & Production](/sending-email/ses-sandbox) for details on requesting production access. ## Email Identities table The Email Identities table on the **Identities** page displays the following columns: | Column | Description | |--------|-------------| | **Email Address** | The verified email address. May show a **"New"** badge if recently discovered from AWS, or a **"Detached"** / **"Archived"** badge. | | **Domain** | The parent domain of the email address. | | **Status** | Current verification status (Pending, Verified, or Failed). | | **Channel** | A dropdown to assign the identity to a [channel](/channels/understanding-channels). | | **Actions** | **Refresh** to re-check verification status. **Archive** (for detached identities) to hide from the list. **Restore** (for archived identities) to bring back. **Delete** to permanently remove from SES. | Archived email identities are hidden by default. Enable the **Show archived** toggle above the table to see them. ## Managing email identities Verified email identities appear in the Email Identities section of the **Identities** page. You can: - **View status** — See whether an identity is Pending, Verified, or Failed. - **Refresh** — Click the **Refresh** button (refresh icon) on the identity row to re-check the current verification status from SES. - **Archive** — Hide a detached identity from your list. You can restore it later. Archiving does not affect your SES account. - **Restore** — Bring an archived identity back to your list. - **Delete** — Remove an identity you no longer need. This permanently deletes the identity from your AWS SES account and from the SendOps database. - **Assign to a channel** — Use the channel dropdown to associate the identity with a [channel](/channels/understanding-channels) for organized sending and reporting. The **Delete** action permanently removes the email identity from your AWS SES account, not just from SendOps. This cannot be undone. If you want to hide an identity without deleting it from SES, use **Archive** instead. SendOps also discovers email identities automatically through [AWS Account Sync](/aws-setup/account-sync). Identities found in your SES account appear with a "New" badge and can be assigned to channels like any other identity. ## Limitations - Email identities do not support DKIM signing unless the parent domain is also verified with DKIM records. Messages sent from an email-only identity will use the default SES signing domain. - If you later verify the full domain, individual email identity verifications on that domain are still valid but become redundant. The domain-level verification takes precedence. ## What's next? - Verify your full domain for production sending with [Adding a Domain](/domains/adding-a-domain). - Learn about DNS records for DKIM, SPF, and DMARC in [DNS Configuration](/domains/dns-configuration). - Understand SES sandbox limitations in [SES Sandbox & Production](/sending-email/ses-sandbox). --- # DNS Configuration Source: https://help.sendops.dev/domains/dns-configuration Section: Identities & Domains > Set up DKIM, SPF, and DMARC DNS records for your sending domain to maximize email deliverability. Properly configured DNS records are essential for email deliverability. They prove to receiving mail servers that your messages are legitimate and haven't been tampered with. This guide covers every DNS record you should configure for a domain verified in SendOps. ## DKIM (DomainKeys Identified Mail) DKIM adds a cryptographic signature to every email you send. Receiving servers use the public key published in your DNS to verify that the message was actually sent by your domain and wasn't altered in transit. When you [add a domain](/domains/adding-a-domain) in SendOps, SES generates three DKIM signing key pairs and provides three CNAME records to publish. ### DKIM records | Type | Name | Value | |------|------|-------| | CNAME | `{token1}._domainkey.example.com` | `{token1}.dkim.amazonses.com` | | CNAME | `{token2}._domainkey.example.com` | `{token2}.dkim.amazonses.com` | | CNAME | `{token3}._domainkey.example.com` | `{token3}.dkim.amazonses.com` | The token values are unique to your domain and are generated by AWS SES. Copy them from the SendOps dashboard to avoid errors. **Why three records?** SES uses key rotation. Having three selectors allows SES to rotate signing keys automatically without any downtime or manual intervention on your part. All three CNAME records must be added for DKIM to be fully enabled. Once they propagate, SendOps will show DKIM status as **Verified**. ## SPF (Sender Policy Framework) SPF tells receiving servers which mail servers are authorized to send email on behalf of your domain. For SES, you need to include Amazon's SPF record. When you add a domain, SendOps generates a recommended SPF record and displays it in the dashboard. The recommended value is: ### SPF record | Type | Name | Value | |------|------|-------| | TXT | `example.com` | `v=spf1 include:amazonses.com ~all` | A domain can only have **one** SPF TXT record. If you already have an SPF record (e.g., for Google Workspace or another provider), **merge** the includes into a single record rather than creating a second one. For example: ``` v=spf1 include:_spf.google.com include:amazonses.com ~all ``` Multiple SPF records on the same domain will cause validation failures. ### SPF mechanism reference | Mechanism | Meaning | |-----------|---------| | `include:amazonses.com` | Authorize Amazon SES servers to send on your behalf | | `~all` | Soft-fail messages from unauthorized servers (recommended starting point) | | `-all` | Hard-fail messages from unauthorized servers (stricter, use after confirming everything works) | ## DMARC (Domain-based Message Authentication, Reporting & Conformance) DMARC ties DKIM and SPF together and tells receiving servers what to do when a message fails authentication. It also enables reporting so you can monitor authentication results. When you add a domain, SendOps generates a recommended DMARC record and displays it in the dashboard. The default record uses `p=quarantine` with reports sent to `dmarc@yourdomain.com`: ### DMARC record | Type | Name | Value | |------|------|-------| | TXT | `_dmarc.example.com` | `v=DMARC1; p=quarantine; rua=mailto:dmarc@example.com` | ### Policy options | Policy (`p=`) | Behavior | When to use | |---------------|----------|-------------| | `quarantine` | Mark failing messages as spam | Default policy generated by SendOps | | `reject` | Reject failing messages outright | Full enforcement after confidence in your authentication setup | | `none` | Take no action, just collect reports | Monitoring only, while reviewing reports | The DMARC record generated by SendOps uses `p=quarantine` by default. This provides immediate protection by directing receiving servers to treat unauthenticated messages as suspicious. You can adjust the policy value in your DNS if needed -- for example, moving to `reject` once you have confirmed all legitimate sending sources pass DKIM and SPF. ### DMARC tag reference | Tag | Required | Example | Purpose | |-----|----------|---------|---------| | `v` | Yes | `DMARC1` | Protocol version | | `p` | Yes | `quarantine`, `reject`, `none` | Policy for messages that fail authentication | | `rua` | No | `mailto:dmarc@example.com` | Address to receive aggregate reports | | `ruf` | No | `mailto:dmarc-forensic@example.com` | Address to receive forensic (failure) reports | | `pct` | No | `100` | Percentage of messages to apply the policy to (default 100) | | `sp` | No | `none` | Policy for subdomains (defaults to `p` value) | ## Complete DNS checklist Once all records are in place, your domain should have: | Record | Status | Required? | |--------|--------|-----------| | 3x CNAME (DKIM) | Verified | Yes | | TXT (SPF) | Configured | Strongly recommended | | TXT (DMARC) | Configured | Strongly recommended | | CNAME (Tracking domain) | Configured | Optional | ## What's next? - Set up a custom [Tracking Domain](/domains/tracking-domains) for branded open and click tracking links. - If your domain is not verifying, see [Domain Verification Troubleshooting](/troubleshooting/domain-verification). - Learn about organizing verified domains into [Channels](/channels/understanding-channels). --- # Tracking Domains Source: https://help.sendops.dev/domains/tracking-domains Section: Identities & Domains > Set up a custom tracking domain for branded open and click tracking URLs in your emails, improving deliverability and link trust. When open and click tracking is enabled, SES rewrites links in your emails to route them through a tracking endpoint. By default, these tracking URLs use a generic SES domain. A custom tracking domain replaces that with a subdomain you control, so tracking links match your brand. ## Why use a custom tracking domain? - **Better deliverability** — Spam filters look at link domains in your email. When tracking links use your own domain instead of a shared SES domain, your messages appear more consistent and trustworthy. - **Branded URLs** — Recipients see links like `track.yourdomain.com/...` instead of a generic AWS tracking URL. This builds trust and reduces the chance of recipients flagging your emails as suspicious. - **Domain reputation isolation** — Your tracking domain carries only your reputation, not the reputation of other SES customers sharing the default tracking domain. You can send email and track opens and clicks without a custom tracking domain. SES will use its default tracking endpoint. However, a custom tracking domain is recommended for production sending to maximize deliverability and maintain a professional appearance. Each organization can have up to **10 tracking domains**. If you need more, consider consolidating subdomains or removing unused ones. ## Setting up a tracking domain In the SendOps dashboard at [app.sendops.dev](https://app.sendops.dev), navigate to **Tracking Domains** in the sidebar (under the Infrastructure section). Click **Add Tracking Domain** to open the creation wizard. Pick a subdomain dedicated to tracking, such as `track.yourdomain.com` or `links.yourdomain.com`. This must be a subdomain (containing at least one dot), not a bare domain. It should not be used for anything else (e.g., not your website or email). Enter the subdomain and click **Continue**. Select whether tracking links should use **HTTP** or **HTTPS**. - **HTTP** — Simple setup. Your CNAME record points directly to the SES tracking endpoint (`r..awstrack.me`). No additional infrastructure is needed. - **HTTPS** — Requires a CDN distribution (e.g., CloudFront) with your own SSL certificate that terminates TLS and proxies requests to SES. When you select HTTPS, you must provide the **CDN Domain** (e.g., `d1234567890.cloudfront.net`). Click **Create Domain** to proceed. After creation, the wizard displays all the DNS records you need to add at your DNS provider. There are two sets of records: **Tracking CNAME record** — Routes tracking link requests to the correct endpoint. | Type | Name | Value | |------|------|-------| | CNAME | `track.yourdomain.com` | `r..awstrack.me` (HTTP) or your CDN domain (HTTPS) | The exact CNAME target is displayed in the dashboard. For HTTP, it points to the SES regional tracking endpoint. For HTTPS, it points to the CDN domain you provided. **DKIM records** — Creating a tracking domain also creates an SES email identity for your subdomain. SES generates three CNAME records for DKIM signing that must be added to your DNS. | Type | Name | Value | |------|------|-------| | CNAME | `{token1}._domainkey.track.yourdomain.com` | `{token1}.dkim.amazonses.com` | | CNAME | `{token2}._domainkey.track.yourdomain.com` | `{token2}.dkim.amazonses.com` | | CNAME | `{token3}._domainkey.track.yourdomain.com` | `{token3}.dkim.amazonses.com` | The token values are unique to your domain and are generated by AWS SES. Use the copy buttons in the dashboard to avoid transcription errors. Click **Continue to Verification** to go to the detail page and monitor verification progress. DNS changes typically propagate within a few minutes, but in some cases can take longer depending on your DNS provider and TTL settings. If your domain still shows as pending after a few hours, double-check that the records were entered exactly as shown in the dashboard. ## Verification Verification is tracked separately for two components: - **DNS CNAME** — Confirms that your subdomain's CNAME record resolves to the expected target. - **SES Identity (DKIM)** — Confirms that the DKIM records are in place and SES has verified the email identity. Each component has its own status: | Status | Meaning | |--------|---------| | **pending** | Waiting for DNS records to be detected. Verification is polling automatically. | | **verified** | The records have been confirmed. | | **failed** | Verification timed out after 7 days or SES reported a failure. | | **invalid** | DNS CNAME was previously verified but now points to a different target (drift detected). | The tracking domain is fully active only when both DNS CNAME and SES Identity show **verified**. ### Automatic background verification After you create a tracking domain, SendOps automatically polls for verification on a decay schedule: - Every **2 minutes** during the first hour. - Every **10 minutes** during the first day. - Every **hour** after the first day, up to **7 days**. If both checks are not verified within 7 days, the status is set to **failed**. ### Manual verification On the tracking domain detail page, click **Verify Now** to trigger an immediate check. There is a **30-second cooldown** between manual verification attempts. ## Tracking Domains list page The list page at **Tracking Domains** displays a table with the following columns: | Column | Description | |--------|-------------| | **Subdomain** | The tracking subdomain (e.g., `track.yourdomain.com`). | | **Protocol** | Badge showing HTTP or HTTPS. | | **Status** | Combined verification status (verified, pending, failed, or invalid). | | **Created** | When the tracking domain was created (relative time). | Click any row to open the detail page for that tracking domain. ## Tracking domain detail page The detail page shows full configuration and status for a tracking domain. ### Verification Status card Displays DNS CNAME and SES Identity (DKIM) verification statuses side by side, each with their last-checked or last-verified timestamp. If verification encountered an error, it is displayed below the statuses. A **Verify Now** button triggers an immediate re-check when verification is not yet complete. ### Configuration card Shows the domain's configuration: - **Protocol** — HTTP or HTTPS. - **Region** — The AWS region used for this tracking domain. - **CNAME Target** — The value your CNAME record should point to. - **SES Tracking Endpoint** — The subdomain registered with SES. For HTTPS tracking domains, a **CDN Domain** field is also shown. You can edit the CDN domain at any time by clicking **Edit**, entering the new CDN domain, and clicking **Save**. Changing the CDN domain also updates the CNAME target, so you will need to update your DNS records accordingly. ### DNS Records accordion An expandable accordion showing the tracking CNAME record and DKIM records for reference. Use the copy buttons to copy record values. ### Danger Zone Contains the **Delete Tracking Domain** button, which permanently removes the tracking domain. ## How tracking works with a custom domain Once your tracking domain is fully verified: 1. When you send an email with open or click tracking enabled, SES rewrites links to route through your tracking subdomain (e.g., `http://track.yourdomain.com/click/...`). 2. When a recipient clicks a link, the request hits your tracking CNAME, which resolves to the SES tracking endpoint (or your CDN for HTTPS). 3. SES logs the click event and redirects the recipient to the original destination URL. 4. Open tracking works similarly — a small transparent pixel is loaded from your tracking domain, and the event is logged. ## Deleting a tracking domain You can delete a tracking domain from the detail page under the **Danger Zone** section. Deletion requires confirmation. Deleting a tracking domain removes the SES email identity and the database record. Any tracking links that use this domain will **stop working** immediately. You will need to **manually remove** the CNAME and DKIM DNS records from your DNS provider — SendOps cannot do this for you. This action cannot be undone. ## What's next? - Review the full DNS setup for your domain in [DNS Configuration](/domains/dns-configuration). - Organize your domains and identities into [Channels](/channels/understanding-channels) for grouped reporting. - Monitor click and open rates in [Engagement Metrics](/reports/engagement-metrics). --- # Understanding Channels Source: https://help.sendops.dev/channels/understanding-channels Section: Channels > Learn what channels are in SendOps, how they map to AWS SES configuration sets, and how they control event tracking and sending behavior. Channels in SendOps map directly to **AWS SES configuration sets**. They control how SES processes and tracks emails sent through them, including event tracking, engagement tracking settings, and sending behavior. Identities are assigned to channels, and multiple identities can share a single channel. ## What is a channel? A channel represents an AWS SES configuration set within SendOps. Each channel determines how emails sent through it are tracked and processed by SES. Rather than managing configuration sets directly in the AWS console, SendOps lets you create and manage them from the dashboard with a clear visual interface. Each channel has: - A **Purpose** badge — one of **Default**, **Transactional**, **Marketing**, **Onboarding**, or **Custom** — indicating the type of email traffic it handles - A **Status** — **Enabled** or **Disabled** — derived from the channel's `sending_enabled` setting, controlling whether the channel is available for sending - A **Sync status** — **Synced**, **Syncing**, or **Error** — reflecting whether the channel is in sync with its corresponding SES configuration set in AWS ## How channels relate to identities Identities (domains and email addresses) are **assigned to** channels. This assignment determines which SES configuration set is used when email is sent from that identity. Key points about the relationship: - **Multiple identities can share a single channel.** For example, `notifications@example.com` and `receipts@example.com` can both be assigned to a "Transactional" channel. - Identities are assigned to channels from the **Identities** page — both domain identities and email identities have their own **Channel** dropdown column in their respective tables. - Reassigning an identity to a different channel changes the SES configuration set used for that identity's outgoing email. ## Why use channels? Channels let you separate your email traffic by purpose and apply different tracking and sending configurations to each stream: ### By email type | Channel | Purpose | Example identities | |---|---|---| | **Transactional** | Transactional | `notifications@example.com`, `receipts@example.com` | | **Marketing** | Marketing | `campaigns@example.com`, `newsletter@example.com` | | **Onboarding** | Onboarding | `welcome@example.com` | ### By product or service If your company runs multiple products, each product can have its own channel with appropriate tracking settings and event destinations. ### By environment Separate staging and production traffic into different channels to keep test data out of your production metrics. After your first domain is verified during onboarding, SendOps automatically provisions four channels: **Default**, **Transactional**, **Marketing**, and **Onboarding**. Any identities that are not explicitly assigned to a channel will use the Default channel. You can change which channel is the default via **Set as Default** on the channel detail page. The default channel cannot be deleted — there is always at least one channel in your workspace. ## What channels control Each channel's detail page exposes the following settings, which map to the underlying SES configuration set: - **Sending enabled/disabled** — controls whether sending is allowed through this channel. - **TLS policy** — **Require** or **Optional** — determines whether SES enforces TLS when delivering email. - **Max delivery duration** — from 5 minutes to 14 hours — how long SES will attempt delivery before giving up. - **Auto validation threshold** — **SES Managed**, **High**, **Medium**, or **Off** — controls automatic email address validation. - **Suppression override** — bounce and complaint suppression rules that override the account-level suppression list. - **Engagement tracking** — whether open and click tracking is enabled for emails sent through the channel. - **Event destinations** — auto-configured EventBridge destinations where SES event data is routed so SendOps can power your reports and notifications. ## Specifying a channel when sending Because channels map to SES configuration sets, specifying a channel is an AWS SES concept — you tell SES which configuration set to use, and SendOps resolves it to the corresponding channel. There are two ways to do this: When you assign a channel to a domain or email identity on the **Identities** page, SendOps configures SES so that every email sent from that identity automatically uses the channel's configuration set. No code changes are required — SES applies the configuration set for you based on the sending identity. This is the simplest approach and works regardless of how you send (SES API, SMTP interface, or any SDK). If you call the SES API directly, you can explicitly pass the configuration set name in the `X-SES-CONFIGURATION-SET` header. The channel detail page displays the exact header value to use, for example: ``` X-SES-CONFIGURATION-SET: sesmail-transactional ``` This overrides any identity-level assignment and is useful when you want the same identity to send through different channels depending on the type of message. If an identity has no channel assigned and no `X-SES-CONFIGURATION-SET` header is provided, the email is sent without a configuration set. To ensure events are captured in SendOps, always assign a channel to your identities or include the header in your API calls. ## Channels and analytics Reports in SendOps can be filtered by channel. This means you can: - Compare bounce rates between your transactional and marketing channels - Track open and click rates for a specific channel's email traffic - Monitor complaint rates per channel to catch issues early - Set up [notifications](/notifications/notification-types) scoped to a specific channel ## What's next? Learn how to create, configure, and manage your channels in [Managing Channels](/channels/managing-channels), or read about [How Email Flows](/sending-email/email-flow) to understand how SendOps captures events from your SES sending. --- # Managing Channels Source: https://help.sendops.dev/channels/managing-channels Section: Channels > How to create, configure, sync, and manage channels in SendOps, including integration headers and channel settings. This guide covers the day-to-day tasks for working with channels in the SendOps dashboard — creating new channels, syncing with AWS SES, configuring settings, and managing channel defaults. ## Creating a channel Channel creation uses a three-step wizard: Details, Settings, and Review. Navigate to **Channels** in the left sidebar of the SendOps dashboard. Click the **New Channel** button in the top-right corner of the page. Give the channel a descriptive name and select its **Purpose** using the radio group. Available purposes are: - **Default** — general-purpose channel for all email types - **Transactional** — password resets, receipts, and system notifications - **Marketing** — newsletters, promotions, and campaigns - **Onboarding** — welcome emails and activation flows - **Custom** — custom channel with manual configuration Click **Next** to proceed. Settings are pre-filled based on the selected purpose. You can adjust any of them before creating the channel. See [Channel settings](#channel-settings) below for details on each setting. Click **Next** to proceed to the review step. Review the channel name, purpose, SES config set name, and all settings. Click **Create Channel** to save. The new channel appears in the list and a corresponding SES configuration set is created in your AWS account. ## Configuration set mapping Each channel in SendOps maps to an **AWS SES configuration set**. When you create a channel, SendOps creates a configuration set named `sesmail-` in your connected AWS account. This configuration set controls how SES processes emails sent through this channel. You can view the mapped configuration set name on the channel detail page, displayed beneath the channel name. ## Integration header The channel detail page includes an **Integration** section that shows the `X-SES-CONFIGURATION-SET` header value for this channel. Use this header when sending emails through the SES API to route them through the channel. A copy button is provided to copy the full header to your clipboard. If the channel is set as the default, emails sent without a configuration set header automatically use this channel. ## Syncing with AWS Channels have two aspects of sync with AWS: ### Settings sync When you change a channel's settings in SendOps, those changes are pushed to the corresponding SES configuration set in AWS. The channel shows a **sync status** icon: | Sync status | Meaning | |---|---| | **Synced** (check icon) | The channel and its SES configuration set are in sync. | | **Syncing** (spinner) | Changes are being applied to AWS. | | **Error** (warning icon) | The sync failed. A "Sync Error" badge appears. Check the error details and retry. | If a sync fails, an error banner appears on the channel detail page showing the error message, along with a **Retry Sync** button. ### Account sync SendOps regularly syncs with your AWS SES account to detect changes. If a channel's underlying SES configuration set is removed from AWS, the channel is marked as **Detached**. If a new configuration set is found in SES that SendOps wasn't tracking, it appears as a **Discovered** channel with a "New" badge. For full details on how account sync works, see [AWS Account Sync](/aws-setup/account-sync). ## Archiving and restoring channels Channels that become **Detached** (their SES configuration set no longer exists in AWS) can be **Archived** to hide them from your channel list. Archiving does not delete anything from SES. To view archived channels, enable the **Show archived** toggle at the top of the Channels page. From there, you can **Restore** any archived channel to make it visible again. **Archive** hides a channel from your list without affecting SES — you can restore it later. **Delete** permanently removes the channel and its SES configuration set, and reassigns all linked identities to the default channel. ## Channel settings From the channel detail page, you can configure the following settings. Each setting is saved individually and synced to AWS SES immediately. ### Sending enabled Toggle whether emails can be sent through this channel. When disabled, SES will reject any emails sent through the channel's configuration set. Disabling sending requires confirmation through a dialog. The channel list page shows the status as **Enabled** or **Disabled** based on this setting. ### Auto validation Control automatic email address validation before sending. Options are: - **SES Managed** — AWS determines when to validate (default) - **High** — validate more aggressively - **Medium** — moderate validation - **Off** — no automatic validation Auto validation incurs an AWS charge of approximately $0.01 per 1,000 emails validated. ### TLS policy Toggle between **Require** and **Optional** TLS encryption for outbound connections: - **Require** (default) — require TLS encryption for all outbound connections - **Optional** — attempt TLS but fall back to unencrypted if unavailable ### Max delivery duration Set the maximum time SES will attempt to deliver a message before giving up. Available options: - 5 minutes - 30 minutes - 1 hour - 14 hours (default) ### Suppression override When enabled, this overrides the account-level suppression list with channel-specific settings. You can select which suppression reasons to apply: - **Bounces** — suppress addresses that have previously bounced - **Complaints** — suppress addresses that have previously filed complaints ### Tracking A single toggle that enables or disables open and click tracking for emails sent through this channel. Tracking settings are applied at the SES configuration set level and affect all identities assigned to the channel. Tracking incurs an AWS charge of approximately $0.0001 per event. ### Event destinations SendOps automatically configures an **EventBridge** event destination on each channel's configuration set. This routes SES events (bounces, complaints, deliveries, etc.) to your AWS EventBridge default event bus, which SendOps uses for reports and notifications. No manual configuration is needed. ## Default channel The channel detail page includes a **Default Channel** section. If the channel is already the default, a badge indicates this. Otherwise, a **Set as Default** button lets you make the channel the default. Emails sent without specifying a configuration set header will use the default channel. ## Renaming a channel You can rename a channel from the **Danger Zone** section on the channel detail page (available for non-default channels). If the new name produces a different slug, SendOps creates a new SES configuration set, reassigns all linked identities, and deletes the old configuration set. Any integrations referencing the old config set name will break. ## Deleting a channel When you delete a channel, all identities currently assigned to that channel are reassigned to the **default** channel's SES configuration set. The channel and its SES configuration set are permanently deleted. This action cannot be undone. To delete a channel: 1. Go to **Channels** and select the channel. 2. Scroll to the **Danger Zone** section. 3. Click **Delete Channel** and confirm. You cannot delete the default channel. If you need to remove the current default, first set a different channel as default. ## Auto-provisioned channels After your first domain is verified during onboarding, SendOps automatically provisions four channels: - **Default** — general-purpose, set as the default channel - **Transactional** — optimized for transactional emails (tracking off, bounce suppression) - **Marketing** — optimized for marketing emails (tracking on, bounce and complaint suppression, high auto validation) - **Onboarding** — for welcome and activation emails ## Best practices - **Match channels to your email streams.** Create channels that reflect how your organization segments its email traffic — transactional, marketing, onboarding, and so on. - **Use purpose badges consistently.** The purpose badge helps your team quickly identify what kind of traffic flows through each channel. - **Review sync status regularly.** If a channel shows an error sync status, investigate and resolve it promptly to ensure your SES configuration stays accurate. - **Use channels for alerting.** Set up [notifications](/notifications/configuring-notifications) per channel so the right team gets alerted about the right metrics. ## What's next? Now that your channels are set up, learn about [How Email Flows](/sending-email/email-flow) through SES and into SendOps, or explore [Deliverability Reports](/reports/deliverability-reports) to see channel-level analytics in action. --- # How Email Flows Source: https://help.sendops.dev/sending-email/email-flow Section: Sending Email > A developer-focused guide to how email moves from your application through AWS SES and into SendOps for analytics, tracing, and alerting. This page explains the full lifecycle of an email — from the moment your application sends it through AWS SES to the point where SendOps captures delivery and engagement events. Understanding this flow is essential for developers integrating with SES and using SendOps for observability. ## The key principle **Your sending code does not change.** SendOps is purely observational on the sending path. Your application sends email exactly the way it does today — through the AWS SES API or SMTP interface using your existing AWS SDK. SendOps never sits in the sending path and never touches the email content. Whether you send email with the AWS SDK for Node.js, Python (boto3), Go, Java, .NET, or any other language — nothing changes. You do not need to install a SendOps SDK, modify your sending code, or route email through a different endpoint. ## The email flow Here is the complete path an email takes, from send to analytics: ### 1. Your application sends the email Your application calls the SES `SendEmail`, `SendRawEmail`, or `SendTemplatedEmail` API (or uses the SMTP interface) to send an email. This happens entirely within your infrastructure and the AWS SES service — SendOps is not involved at this stage. Each email should include a configuration set name so that SES knows where to publish events. In SendOps, every channel maps to its own SES configuration set. You can find the exact header value on the channel detail page in SendOps — it looks like `X-SES-CONFIGURATION-SET: sesmail-transactional`. If you do not specify a configuration set, SES will still deliver the email, but SendOps will not be able to associate the event with a specific channel. For details on sending email with the AWS SDK, see the [AWS SES Developer Guide — Sending email](https://docs.aws.amazon.com/ses/latest/dg/send-email.html). ### 2. SES processes and delivers the email SES validates the request, checks your sending quotas and suppression list, and delivers the email to the recipient's mail server. This is standard SES behavior — no changes here. ### 3. SES generates events As the email progresses, SES generates events for each stage of the lifecycle: | Event | When it fires | |---|---| | **Send** | SES accepted the API call and will attempt delivery | | **Delivery** | The recipient's mail server accepted the email | | **Bounce** | The email could not be delivered (hard or soft bounce) | | **Complaint** | The recipient marked the email as spam | | **Reject** | SES rejected the email before attempting delivery | | **Open** | The recipient opened the email (requires open tracking) | | **Click** | The recipient clicked a link (requires click tracking) | | **Delivery Delay** | Delivery is being temporarily delayed (e.g., recipient server throttling) | | **Rendering Failure** | A template rendering error occurred | | **Subscription** | A recipient's subscription preferences changed | ### 4. Events route through the Configuration Set to EventBridge Each channel's SES configuration set has an EventBridge event destination that publishes events to the default event bus in your AWS account. This is configured automatically when SendOps creates the channel's configuration set. The CloudFormation stack also creates a base configuration set called `sendops-events` with its own EventBridge destination. This serves as a fallback for identities that have not been assigned to a specific channel. ### 5. EventBridge forwards events to SendOps The CloudFormation stack sets up an **EventBridge rule** that matches all SES events (source: `aws.ses`) and forwards them to an **API Destination** — a managed HTTPS endpoint pointing at the SendOps ingest webhook. The connection authenticates with an API key passed in the `x-api-key` header on every request. This is a secure, server-to-server integration running entirely within your AWS account. EventBridge handles retries automatically if the endpoint is temporarily unreachable. ### 6. SendOps processes the events Once SendOps receives an event, the ingest pipeline: 1. **Authenticates the request** using the API key to identify your organization and AWS account 2. **Parses the EventBridge envelope** and extracts the SES event detail 3. **Filters by managed identities** — events from domains or email addresses that are not managed in SendOps are dropped 4. **Resolves the channel** by matching the configuration set name in the event to a channel in your organization 5. **Deduplicates** to prevent the same event from being processed twice 6. **Buffers and batch-writes** events into the analytics engine (ClickHouse) for querying From there, SendOps: - **Aggregates metrics** into deliverability and engagement dashboards - **Evaluates notification rules** and triggers alerts when thresholds are crossed - **Builds message traces** so you can follow a single email through every stage of its lifecycle ## Visualizing the flow ## What SendOps does NOT do To avoid any confusion, here is what SendOps is **not** involved in: - **Sending email** — your application sends directly through SES. SendOps never touches outbound email. - **Modifying email content** — SendOps does not alter headers, body, or attachments. - **Replacing SES** — SES is and remains your email sending infrastructure. SendOps is a layer on top. - **Providing a sending API** — there is no SendOps API for sending email. You use the AWS SES SDK. ## Latency and reliability Events typically arrive in SendOps within **a few seconds** of SES generating them. The EventBridge-to-API Destination pipeline is designed for near-real-time delivery. Open and click events depend on recipient behavior and may arrive minutes, hours, or days after the original send. If the SendOps ingest endpoint is temporarily unreachable, EventBridge retries delivery automatically with exponential backoff, so no events are lost during brief outages. ## What's next? - If you are on a new AWS account, read [SES Sandbox & Production](/sending-email/ses-sandbox) to understand sending restrictions and how to request production access. - To learn how identities are organized, see [Understanding Channels](/channels/understanding-channels). - For details on the AWS infrastructure setup, see [AWS Integration](/aws-setup/connecting-aws). --- # SES Sandbox & Production Source: https://help.sendops.dev/sending-email/ses-sandbox Section: Sending Email > Understand the difference between SES sandbox and production mode, sandbox limitations, and how to request production access for your AWS account. Every new AWS account starts with Amazon SES in **sandbox mode**. This is an AWS-imposed restriction designed to prevent abuse — it limits who you can send to and how much you can send. This page explains what sandbox mode means, how it affects your sending, and how to move to production. The sandbox is an Amazon SES limitation that applies at the AWS account level. SendOps has no control over these restrictions. SendOps works fully in both sandbox and production mode — the only difference is the volume and recipients you can reach through SES. ## What is SES sandbox mode? When your AWS account is new (or when SES is first enabled in a region), SES operates in sandbox mode. In sandbox mode, SES restricts your sending to protect its shared infrastructure and your sender reputation. ## Sandbox limitations | Restriction | Sandbox limit | |---|---| | **Recipients** | You can only send to **verified email addresses and domains** | | **Daily sending quota** | **200 emails** per 24-hour period | | **Sending rate** | **1 email per second** | | **From address** | Must be a **verified identity** (this applies in production too) | In sandbox mode, every recipient address must be individually verified in SES before you can send to it. This makes sandbox mode suitable for development and testing, but not for real-world sending. ## Does SendOps work in sandbox mode? **Yes.** SendOps captures and processes events regardless of whether your SES account is in sandbox or production mode. You can: - Complete the full onboarding flow (AWS integration, connection validation, domain verification, and test send) - Send test emails to verified addresses - See delivery, bounce, and engagement events appear in your SendOps dashboard - Verify that notifications and alerting are working After your first domain is verified during onboarding, SendOps provisions four default channels — **Default**, **Transactional**, **Marketing**, and **Onboarding** — each backed by its own SES configuration set. This happens regardless of whether your account is in sandbox or production mode. ## How SendOps detects sandbox status SendOps automatically checks your SES account status during the **Connection Validation** phase of onboarding. The validation results include: - Whether SES sending is enabled in your region - Whether your account is in sandbox or production mode - Your current sending limits (daily quota and per-second rate) - The number of verified identities detected in your SES account If your account is in sandbox mode, the validation shows a warning: **"Sandbox mode active"** with a note that you can only send to verified email addresses. After onboarding, the **AWS Status Badge** in the sidebar header reflects your sandbox status. When your account is in sandbox mode, the badge displays **"Sandbox"** with an amber indicator instead of the green **"AWS Connected"** indicator shown for production accounts. Clicking the badge shows your region, account ID, and mode. ## Sandbox warnings during test send During the **First Test Send** phase of onboarding, if your account is in sandbox mode, SendOps shows a warning listing your verified identities. If you enter a recipient address that does not match a verified email address or a verified domain, SendOps warns that the send will likely fail. ## Checking your SES account status You can check your sandbox status in two places: **In SendOps:** Look at the AWS Status Badge in the sidebar header. If it shows "Sandbox" with an amber dot, your account is in sandbox mode. If it shows "AWS Connected" with a green dot, you have production access. **In the AWS console:** 1. Open the [AWS SES console](https://console.aws.amazon.com/ses/). 2. In the left sidebar, go to **Account dashboard**. 3. Look for the **Account status** section. It will show either "Sandbox" or "Production". ## Requesting production access When you are ready to send to any email address without recipient verification limits, you need to request production access from AWS. Go to the [AWS SES console](https://console.aws.amazon.com/ses/) and navigate to **Account dashboard**. In the account status section, click the **Request production access** button. AWS asks for details about your sending use case. Be specific and thorough: - **Mail type** — choose Transactional, Marketing, or both - **Website URL** — the website or application associated with your sending - **Use case description** — explain what emails you send, who your recipients are, and how you handle bounces and complaints - **Additional contacts** — an email address where AWS can reach you about your account AWS reviews production access requests manually by opening a **support ticket** on your behalf. Approval typically takes **24 hours** but can take longer. You will receive an email notification when your request is approved or if AWS needs more information. ### What AWS looks for AWS doesn't approve production access automatically — a human reviews your request through a support ticket. In most cases, they will reply asking for evidence that you follow good sending practices. Specifically, they want to see: - **Bounce handling** — how you detect and stop sending to addresses that bounce - **Complaint management** — how you process and act on spam complaints - **List hygiene** — how you maintain a clean recipient list and avoid sending to invalid addresses - **Deliverability monitoring** — what tools you use to track your sender reputation ### Using SendOps to support your request SendOps gives you exactly the kind of infrastructure AWS wants to see. When responding to the support ticket, mention that you use SendOps for: - **Real-time bounce and complaint monitoring** with automated notifications when rates exceed thresholds - **Suppression management** to prevent re-sending to addresses that have bounced or complained - **Engagement tracking** (opens and clicks) to measure recipient interaction and identify disengaged addresses - **Channel-based traffic separation** so transactional, marketing, and onboarding emails are tracked independently - **Deliverability reports** filtered by channel, domain, and time period Referencing specific tooling like this demonstrates to AWS that you take deliverability seriously and significantly improves your chances of approval. AWS may deny requests that are vague or incomplete. To improve your chances of approval: - Explain your use case clearly — what kind of emails, to whom, and how often - Describe your bounce and complaint handling process, and mention that you use SendOps for monitoring and alerting - Specify realistic sending volumes - Make sure you have a working website at the URL you provide - If AWS replies asking for more detail, respond promptly and thoroughly — the support ticket stays open until resolved For the full details on the request process, see the [AWS documentation on requesting production access](https://docs.aws.amazon.com/ses/latest/dg/request-production-access.html). ## After production access is granted Once your account moves to production mode: - You can send to **any email address** — recipients no longer need to be verified - Your daily sending quota increases (typically to **50,000 emails/day** initially) - Your sending rate increases (typically to **14 emails/second** initially) - AWS may increase these limits further over time based on your sending patterns and reputation SendOps detects the change automatically. The AWS Status Badge updates from "Sandbox" to "AWS Connected", and SendOps sends a notification informing you that your account has moved out of sandbox mode. No configuration changes are needed on the SendOps side. ## Sandbox status notifications SendOps periodically monitors your SES account status. If your sandbox status changes — either from sandbox to production, or from production back to sandbox (which can happen if AWS places your account under review) — SendOps sends a notification to your organization. These notifications appear in your SendOps notification feed and are dispatched through any configured notification channels (email, Slack, webhooks). ## Sandbox mode per region SES sandbox status is **per region**. If you enable SES in a new AWS region, that region starts in sandbox mode even if your primary region is in production. You need to request production access separately for each region you plan to send from. If you use SendOps with [multiple regions](/aws-setup/supported-regions), make sure each region has production access before sending real email from it. ## What's next? - Learn about [How Email Flows](/sending-email/email-flow) from your application through SES and into SendOps - Set up your sending identities in [Adding a Domain](/domains/adding-a-domain) or [Email Identities](/domains/email-identities) - Organize your identities into [Channels](/channels/understanding-channels) for focused reporting --- # Moving to SES Production Source: https://help.sendops.dev/sending-email/ses-production-access Section: Sending Email > A complete guide to requesting AWS SES production access, including what AWS looks for, how to write a strong request, and how SendOps supports every requirement. Every AWS account starts with SES in sandbox mode. To send email to real recipients at scale, you need to request **production access** from AWS. This is a manual review process — AWS evaluates your sending practices, infrastructure, and compliance before lifting sandbox restrictions. The good news: when you're sending through SendOps, most of what AWS looks for is already in place. ## Before you request Make sure you've completed these steps before submitting your production access request: Your sending domain should have DKIM, SPF, and DMARC records fully configured and passing verification in SendOps. See [DNS Configuration](/domains/dns-configuration) for details. AWS reviewers will visit your domain. Make sure there's a real website at the domain you'll be sending from — it should clearly explain what your business does. Your AWS account should be connected to SendOps and receiving delivery events (bounces, complaints, deliveries). You can verify this on the Messages Dashboard. Send a test email through SES to a verified address to confirm your integration is working end-to-end. Set up separate channels for transactional and marketing email. This demonstrates to AWS that you manage different traffic types independently. See [Understanding Channels](/channels/understanding-channels). Configure bounce and complaint notifications so your team is alerted before rates become problematic. See [Configuring Notifications](/notifications/configuring-notifications). ## What AWS evaluates AWS reviewers assess your request against several criteria. Understanding what they're looking for helps you write a stronger application. ### Sending use case clarity AWS wants to know exactly what kinds of email you'll send, who receives them, and why. Be specific — "transactional emails" is vague; "order confirmations, password resets, and shipping notifications sent to customers who made a purchase" is clear. ### Bounce handling Reviewers look for evidence that you detect and act on bounces. They want to know that hard-bounced addresses are suppressed and won't receive future sends. AWS takes this seriously because high bounce rates degrade SES's shared infrastructure. ### Complaint processing When a recipient marks your email as spam, that feedback needs to flow back to you and result in action. AWS wants to see that you process complaints and stop sending to recipients who complain. ### List quality and consent AWS wants assurance that your recipients opted in to receive your emails. They look for clear consent mechanisms — double opt-in, sign-up forms, purchase relationships — and evidence that you don't send to purchased or scraped lists. ### Unsubscribe mechanism Every marketing email must include a working unsubscribe link. AWS also looks for support of the `List-Unsubscribe` header, which enables one-click unsubscribe in email clients. ### Sending volume expectations Provide realistic volume projections. AWS is wary of new accounts requesting millions of sends immediately. They prefer to see gradual ramp-up plans that start small and grow over weeks. ### Deliverability monitoring AWS wants to know you're watching your metrics. They look for dashboards, alerting, and processes that let you catch problems before they escalate. ## How SendOps addresses each requirement If you're using SendOps, you already have infrastructure in place for every concern AWS evaluates: | AWS requirement | How SendOps handles it | |---|---| | **Bounce monitoring** | Real-time bounce detection and notifications. Bounces are tracked per-channel with configurable alert thresholds. | | **Complaint handling** | Complaint events are captured automatically via SES feedback notifications. Alerts fire when complaint rates exceed your thresholds. | | **Reputation tracking** | Deliverability reports show bounce rates, complaint rates, and engagement metrics across all channels and domains. | | **Traffic separation** | Channels let you isolate transactional, marketing, and other traffic types with independent monitoring and thresholds. | | **Audit trail** | The audit log records all configuration changes. Message-level event history tracks every delivery, bounce, and complaint. | | **DNS and authentication** | Domain onboarding verifies DKIM, SPF, and DMARC configuration. SendOps alerts you if DNS records are missing or misconfigured. | | **Deliverability monitoring** | The Messages Dashboard and engagement metrics give you real-time visibility into sending health. | You can include a link to this page (or to your SendOps dashboard) in your production access request to demonstrate the infrastructure backing your sends. ## Writing your production access request The production access request is submitted through the AWS Console under **SES → Account dashboard → Request production access**. Here's how to fill it out effectively. ### Mail type Choose the type that best describes your primary sending: - **Transactional** — triggered by user actions (receipts, password resets, notifications) - **Marketing** — promotional campaigns, newsletters, product updates If you send both, select **Transactional** as the primary type and explain the full picture in your use case description. ### Website URL Enter the URL of the website associated with your sending domain. AWS reviewers will visit this site, so make sure it's live, professional, and clearly explains your business. ### Use case description This is the most important field. Be thorough and specific. Cover: - **What** you send (email types) - **Who** receives it (how they opted in) - **How** you handle bounces and complaints - **What infrastructure** you use to monitor deliverability Don't be vague. The more detail you provide about your sending practices, the faster your request gets approved. ### Responding to follow-ups AWS sometimes asks follow-up questions. Common ones include: - "How do you handle hard bounces?" — Explain that SendOps detects bounces in real time and your team is notified via configurable alerts. - "How do recipients opt in?" — Describe your specific sign-up or consent process. - "What's your expected sending volume?" — Give a realistic monthly number and mention you plan to ramp up gradually. ## Sample request text Here's a template you can customize for your production access request: Replace the bracketed sections with your specific details. The structure is designed to address every concern AWS reviewers typically look for. > We are requesting production access for SES to send [transactional / marketing / both transactional and marketing] email from [your domain]. > > **Use case:** We send [describe your email types — e.g., order confirmations, shipping notifications, account alerts, and a weekly product newsletter] to [describe your recipients — e.g., customers who created an account on our platform and opted in to receive communications]. > > **Consent and list management:** All recipients have explicitly opted in through [describe your opt-in mechanism — e.g., account registration, checkbox during checkout, double opt-in for newsletter]. We do not send to purchased or third-party lists. > > **Bounce and complaint handling:** We use SendOps (https://sendops.dev) as our email operations platform on top of SES. SendOps monitors all delivery events in real time, including bounces and complaints. Our team receives automated alerts when bounce or complaint rates exceed configured thresholds. Hard bounces are tracked and surfaced immediately so we can take action. > > **Infrastructure and monitoring:** SendOps provides deliverability reports, engagement metrics, and per-channel monitoring. We use separate channels for transactional and marketing email, each with independent bounce and complaint tracking. All configuration changes are recorded in an audit log. > > **DNS and authentication:** Our sending domain is fully configured with DKIM, SPF, and DMARC, verified through SendOps's domain onboarding process. > > **Volume:** We expect to send approximately [number] emails per month initially, ramping up gradually over [timeframe] as our user base grows. Our initial daily volume will be approximately [number] emails per day. > > **Unsubscribe:** All marketing emails include a visible unsubscribe link. We support the List-Unsubscribe header for one-click unsubscribe in supported email clients. ## After approval Once AWS grants production access: - **Sandbox restrictions are lifted** — you can send to any recipient without pre-verifying their address. - **Your initial quota** is typically 50,000 emails per day with a sending rate of 14 emails per second. These limits increase automatically as AWS observes good sending practices. - **SendOps detects the change** — your account status updates in SendOps, and any sandbox-related warnings are cleared. Don't jump to high volumes immediately after approval. Start with a fraction of your quota and increase over days or weeks. Sudden spikes from a new account can trigger SES reputation alarms, even with production access. A good rule of thumb is to double your daily volume every 2–3 days until you reach your target. ### Initial sending limits | Metric | Typical initial limit | |---|---| | **Daily sending quota** | 50,000 emails per 24-hour period | | **Maximum send rate** | 14 emails per second | These limits grow automatically. AWS evaluates your sending patterns continuously and raises your quota as you demonstrate consistent, low-bounce, low-complaint sending. ## If your request is denied Denials happen, usually because the request lacked detail. AWS rarely explains exactly what was missing, but common reasons include: - **Vague use case** — "We send emails to customers" isn't enough. Specify email types, recipient relationships, and consent mechanisms. - **No bounce/complaint handling described** — AWS needs to know you have systems in place. Reference your SendOps setup and monitoring. - **No website or non-functional domain** — AWS checks your sending domain. Make sure there's a live, professional website. - **Unrealistic volume** — Requesting millions of sends for a brand-new account raises flags. Start with modest numbers and a ramp-up plan. ### How to resubmit You can submit a new request immediately. When resubmitting: 1. Add significantly more detail than your first attempt 2. Address the specific area you think was weak 3. Reference your monitoring infrastructure explicitly 4. Include the link to your SendOps dashboard or this documentation page 5. Keep volume projections modest with a clear ramp-up plan ## What's next? - [SES Sandbox & Production](/sending-email/ses-sandbox) — understand sandbox limitations in detail - [Configuring Notifications](/notifications/configuring-notifications) — set up bounce and complaint alerts - [Deliverability Reports](/reports/deliverability-reports) — monitor your sending health - [Understanding Channels](/channels/understanding-channels) — separate transactional and marketing traffic --- # AWS Integration Source: https://help.sendops.dev/aws-setup/connecting-aws Section: AWS Reference > Connect your AWS account to SendOps using CloudFormation or manual IAM role setup — no API keys required. SendOps connects to your AWS account through **cross-account IAM role assumption** using AWS Security Token Service (STS). Instead of handing over long-lived access keys, you create a role in your account that trusts the SendOps AWS account. SendOps then calls `sts:AssumeRole` to obtain short-lived, scoped credentials every time it needs to interact with your SES resources. This approach follows AWS best practices — credentials rotate automatically, you control exactly which permissions are granted, and you can revoke access instantly by deleting the role. SendOps will **never** ask you for an AWS Access Key ID or Secret Access Key. If you are prompted for static credentials, stop and contact [support@sendops.dev](mailto:support@sendops.dev). Cross-account role assumption is the only supported authentication method. ## Before you begin Connecting your AWS account is **Phase 1** of the SendOps [Setup Guide](/getting-started/creating-your-account#the-setup-guide). Before selecting a method: - **Review the permission breakdown** — the Setup Guide displays expandable permission categories (Account & Identities, Configuration Sets, Email Templates, Suppression List, Resource Tagging, EventBridge) showing exactly which IAM permissions are requested. - **Understand the security scope** — SendOps requests SES and EventBridge permissions only. It never accesses EC2, S3, RDS, Lambda, Billing, IAM (write), or CloudWatch Logs.
Recommended

Method 1: Automated (CloudFormation)

One click deploys a CloudFormation stack that creates everything SendOps needs — the IAM role, SES configuration set with event destination, EventBridge rule, webhook connection, and API destination — in a single stack. ### Deploying the stack In the Setup Guide's AWS Integration phase, select **Automated Setup**. Then use the **AWS Region** dropdown to pick the region where your SES sending is configured. EventBridge rules are regional, so the integration must be deployed in the same region as your SES. See [Supported AWS Regions](/aws-setup/supported-regions) for the full list. Click **Launch CloudFormation Stack**. This opens the AWS CloudFormation console with the template URL and all parameters pre-filled for your account. Confirm the parameters are correct and that the region in the top-right corner of the AWS console matches your SES region. The **stack name** defaults to `sendops` — you can change it, but keeping the default makes support easier. Check the IAM capabilities acknowledgment box, then click **Create stack**. Deployment typically completes in one to two minutes. Once the stack reaches **CREATE_COMPLETE**, the Setup Guide shows a link to the **IAM Role** in the AWS console where you can copy the ARN. You can also check the stack status directly from the link in the Setup Guide. Back in the Setup Guide, paste the Role ARN and click **Validate**. SendOps will perform an `AssumeRole` call to verify the connection. If the connection is already established, the button reads **Re-validate**. Click **"Don't have AWS access? Delegate this step"** to open a dialog with two options: **Email a colleague** (a pre-written message with the deploy link and instructions) or **Use an AI assistant** (a ready-to-copy CLI prompt with the full `aws cloudformation create-stack` command). Both include all parameters specific to your workspace. ### What the stack creates The CloudFormation template provisions seven resources: | Resource | Type | Purpose | |---|---|---| | **IAM Role** (`SendOpsRole`) | `AWS::IAM::Role` | Cross-account role that SendOps assumes. Includes an inline policy (`SendOpsSESAccess`) granting least-privilege SES and EventBridge read permissions. | | **SES Configuration Set** (`sendops-events`) | `AWS::SES::ConfigurationSet` | SES publishes events from identities associated with this configuration set. | | **SES Event Destination** (`sendops-eventbridge`) | `AWS::SES::ConfigurationSetEventDestination` | Routes all SES event types (send, reject, bounce, complaint, delivery, open, click, renderingFailure, deliveryDelay, subscription) to the default EventBridge event bus. | | **EventBridge Connection** (`sendops-webhook`) | `AWS::Events::Connection` | Stores the API key (`x-api-key` header) used to authenticate requests to the SendOps webhook. | | **EventBridge API Destination** (`sendops-ingest`) | `AWS::Events::ApiDestination` | Defines the SendOps webhook endpoint as an HTTP POST target with a rate limit of 300 invocations per second. | | **EventBridge IAM Role** (`SendOpsEventBridgeRole`) | `AWS::IAM::Role` | Allows the EventBridge service to invoke the API destination. Has an inline policy granting `events:InvokeApiDestination`. | | **EventBridge Rule** (`sendops-ses-events`) | `AWS::Events::Rule` | Matches all events from the `aws.ses` source on the default event bus and routes them to the API destination via the EventBridge IAM role. | ### Stack parameters When you deploy the stack you will provide four parameters. The Setup Guide pre-fills these values in the launch URL. | Parameter | Description | |---|---| | `SendOpsAccountId` | The SendOps AWS account ID. Used in the IAM trust policy so SendOps can assume the cross-account role. | | `ExternalId` | A unique identifier for your SendOps organization. Prevents [confused deputy](https://docs.aws.amazon.com/IAM/latest/UserGuide/confused-deputy.html) scenarios. | | `WebhookUrl` | The SendOps event ingestion endpoint URL (HTTPS). | | `WebhookApiKey` | An API key that authenticates webhook requests. Generated per-organization in SendOps. | ### Stack outputs Once the stack is created, the **Outputs** tab shows: | Output | Value | |---|---| | `RoleARN` | The ARN of the cross-account IAM role. Paste this into the Setup Guide to validate the connection. | | `ConfigurationSetName` | The name of the SES configuration set (`sendops-events`). | | `EventRuleName` | The name of the EventBridge rule (`sendops-ses-events`). | ### Updating the stack If SendOps releases a new version of the template (e.g., to add permissions for new features), you can update the stack in place. During Connection Validation, SendOps will show an update banner if the deployed template version is behind and any permission probes fail. In the [CloudFormation console](https://console.aws.amazon.com/cloudformation/), select your `sendops` stack and click **Update**. Choose **Replace current template**, enter the new template URL, and click through to **Update stack**. ### Deleting the stack Deleting the stack removes all resources it created — the IAM roles, configuration set, event destination, EventBridge rule, API destination, and connection. SendOps will stop receiving events from this region. In the [CloudFormation console](https://console.aws.amazon.com/cloudformation/), select your `sendops` stack and click **Delete**. Confirm the deletion when prompted. The stack and all its resources will be removed. Deleting the stack does not affect your SES sending capability or any other AWS resources outside the stack. It only removes the event pipeline to SendOps. ## Method 2: Manual If your organization requires manual resource provisioning, you can create the IAM role step by step. The Setup Guide provides the full IAM policy in both **CloudFormation** and **Terraform** formats with a copy button. The manual method creates the cross-account IAM role with SES and EventBridge permissions. It does **not** set up the SES configuration set, event destination, EventBridge rule, or webhook pipeline. Those resources are only provisioned by the automated CloudFormation method. ### How STS AssumeRole works When SendOps needs to interact with your SES resources, it makes an `sts:AssumeRole` API call to AWS. AWS verifies: 1. **Trust policy** — the role in your account explicitly trusts the SendOps AWS account as a principal. 2. **External ID** — the request includes a unique External ID that matches the condition on your role's trust policy, preventing [confused deputy attacks](https://docs.aws.amazon.com/IAM/latest/UserGuide/confused-deputy.html). 3. **Permissions policy** — the attached policy defines exactly what actions SendOps can perform. If all checks pass, AWS returns temporary credentials (valid for one hour) that are scoped to the permissions you granted. ### Creating the IAM role manually In the Setup Guide's AWS Integration phase, select **Manual Setup**, then pick your **AWS Region** from the dropdown. You will see the IAM policy displayed below in CloudFormation and Terraform tabs. Copy the CloudFormation or Terraform snippet from the Setup Guide. The snippet includes placeholders for `` and `` — replace them with the values shown in the Setup Guide (or use the CloudFormation template directly, which fills them automatically). The role should be named `SendOpsRole` with a trust policy that allows `sts:AssumeRole` from the SendOps AWS account, conditioned on the External ID. The inline permissions policy (`SendOpsSESAccess`) grants six groups of permissions: - **Account & Identities** — `ses:GetAccount`, `ses:GetSendQuota`, `ses:GetSendStatistics`, `ses:ListEmailIdentities`, `ses:GetEmailIdentity`, `ses:CreateEmailIdentity`, `ses:DeleteEmailIdentity`, `ses:SendEmail` - **Configuration Sets** — `ses:ListConfigurationSets`, `ses:GetConfigurationSet`, `ses:CreateConfigurationSet`, `ses:DeleteConfigurationSet`, `ses:PutConfigurationSetTrackingOptions`, `ses:PutConfigurationSetReputationOptions`, `ses:PutConfigurationSetSendingOptions`, `ses:PutConfigurationSetDeliveryOptions`, `ses:PutConfigurationSetSuppressionOptions`, `ses:PutEmailIdentityConfigurationSetAttributes`, `ses:CreateConfigurationSetEventDestination`, `ses:UpdateConfigurationSetEventDestination`, `ses:DeleteConfigurationSetEventDestination` - **Email Templates** — `ses:CreateEmailTemplate`, `ses:UpdateEmailTemplate`, `ses:DeleteEmailTemplate`, `ses:GetEmailTemplate`, `ses:ListEmailTemplates` - **Suppression List** — `ses:ListSuppressedDestinations`, `ses:DeleteSuppressedDestination` - **Resource Tagging** — `ses:TagResource`, `ses:UntagResource` - **EventBridge** — `events:DescribeRule`, `events:ListTargetsByRule` See [IAM Permissions Explained](/aws-setup/iam-permissions) for a full breakdown of every permission and why it is required. After the role is created, open it in the IAM console and copy its **ARN** (e.g., `arn:aws:iam::123456789012:role/SendOpsRole`). Back in the Setup Guide, paste the Role ARN and click **Validate**. SendOps will perform an `AssumeRole` call to verify the connection. ## Verifying the connection Once the AWS Integration is complete (via either method), SendOps attempts to assume the role. If the connection succeeds you will see a green **"AWS account connected"** confirmation showing the Role ARN and region. If it fails, double-check: - The **SendOps Account ID** in the trust policy matches exactly. - The **External ID** in the trust policy condition matches the value shown in SendOps. - The role's permissions policy is attached (not just the trust policy). - The selected **AWS Region** matches the region where the role and SES are configured. ## What's next? After completing the AWS Integration phase, click **Continue** to proceed to **Connection Validation**. In this phase, you click **Run Validation** to trigger an asynchronous check that verifies your CloudFormation stack resources (SES configuration set, EventBridge rule, webhook target) and probes your SES account status (sending enabled, sandbox mode, sending limits, verified identities). For a detailed breakdown of every IAM permission SendOps requests, see [IAM Permissions Explained](/aws-setup/iam-permissions). --- # IAM Permissions Explained Source: https://help.sendops.dev/aws-setup/iam-permissions Section: AWS Reference > A complete breakdown of every AWS IAM permission SendOps requires, why it is needed, and how least-privilege is enforced. When you [connect your AWS account](/aws-setup/connecting-aws), SendOps deploys a CloudFormation stack that creates an IAM role, an SES configuration set, EventBridge rules, and a webhook integration. The IAM role defines exactly what actions SendOps can perform in your AWS account. This page documents every permission in that role so your security and infrastructure teams can review them before granting access. SendOps only requests permissions it actively uses. The IAM role policy is scoped to SES v2 and EventBridge read-only actions — SendOps has no access to S3, EC2, Lambda, or any other AWS service. EventBridge rules, API destinations, and connections are created by the CloudFormation stack itself, not by the IAM role. ## SES permissions — read-only These permissions allow SendOps to read your SES account status, identities, configuration sets, and templates. They do not modify any resources. | Permission | Description | Why SendOps needs it | |---|---|---| | `ses:GetAccount` | Read account-level sending limits, enforcement status, and DKIM settings | Displays your account health and sending limits on the dashboard | | `ses:GetSendQuota` | Read the current sending quota and how much has been used in the last 24 hours | Powers the sending quota widget and quota-based alerts | | `ses:GetSendStatistics` | Read aggregate sending statistics (sends, bounces, complaints, rejects) | Feeds the deliverability overview charts | | `ses:ListEmailIdentities` | List all verified domains and email addresses (SES v2) | Populates the Identities page in SendOps | | `ses:GetEmailIdentity` | Read verification status, DKIM attributes, MAIL FROM configuration, and notification settings for a domain or email address (SES v2) | Shows verification status, DKIM configuration, and MAIL FROM status on identity detail pages | | `ses:ListConfigurationSets` | List all SES configuration sets | Verifies the `sendops-events` configuration set exists | | `ses:GetConfigurationSet` | Read configuration set details including tracking, reputation, sending, delivery, and suppression options (SES v2) | Confirms the configuration set is correctly configured | | `ses:GetEmailTemplate` | Read template subject, HTML body, and text body (SES v2) | Displays template content for preview and version comparison | | `ses:ListEmailTemplates` | List all SES email templates (SES v2) | Populates the Templates section in SendOps | | `ses:ListSuppressedDestinations` | List email addresses on the account-level suppression list | Displays suppressed addresses so you can review and manage them | ## SES permissions — write and management These permissions allow SendOps to create, update, and delete SES resources on your behalf. They power domain onboarding, configuration set management, suppression list management, and template management. | Permission | Description | Why SendOps needs it | |---|---|---| | `ses:CreateEmailIdentity` | Create a new email identity (domain or email address), initiating verification and DKIM setup (SES v2) | Starts the domain or email verification workflow from the SendOps dashboard | | `ses:DeleteEmailIdentity` | Remove a verified domain or email address (SES v2) | Lets you clean up identities you no longer use | | `ses:PutEmailIdentityConfigurationSetAttributes` | Associate or disassociate a configuration set with an email identity | Links identities to the `sendops-events` configuration set for event tracking | | `ses:CreateConfigurationSet` | Create a new SES configuration set | Creates the `sendops-events` configuration set if it does not exist | | `ses:DeleteConfigurationSet` | Delete a configuration set | Removes configuration sets during cleanup or reconfiguration | | `ses:PutConfigurationSetTrackingOptions` | Set custom redirect domain for open and click tracking on a configuration set | Configures tracking domains for open and click tracking | | `ses:PutConfigurationSetReputationOptions` | Enable or disable reputation metrics on a configuration set | Enables reputation monitoring for SendOps-managed configuration sets | | `ses:PutConfigurationSetSendingOptions` | Enable or disable sending on a configuration set | Controls whether a configuration set is active for sending | | `ses:PutConfigurationSetDeliveryOptions` | Set TLS policy and sending pool on a configuration set | Configures delivery options like TLS enforcement | | `ses:PutConfigurationSetSuppressionOptions` | Configure suppression list behavior on a configuration set | Controls how bounces and complaints are handled at the configuration set level | | `ses:CreateConfigurationSetEventDestination` | Add an event destination to a configuration set | Routes SES events to EventBridge for the SendOps pipeline | | `ses:UpdateConfigurationSetEventDestination` | Update an existing event destination | Modifies event routing when configuration changes | | `ses:DeleteConfigurationSetEventDestination` | Remove an event destination from a configuration set | Cleans up event destinations during reconfiguration | | `ses:CreateEmailTemplate` | Create a new email template (SES v2) | Powers template creation from the SendOps dashboard or GitHub sync | | `ses:UpdateEmailTemplate` | Update an existing template's subject, HTML, or text body (SES v2) | Powers template editing and version promotion | | `ses:DeleteEmailTemplate` | Delete an email template (SES v2) | Lets you remove templates you no longer need | | `ses:TagResource` | Add tags to SES resources | Tags resources created by SendOps for identification and organization | | `ses:UntagResource` | Remove tags from SES resources | Manages tags on SendOps-created resources | | `ses:DeleteSuppressedDestination` | Remove an email address from the account-level suppression list | Lets you unsuppress addresses that were previously bounced or complained | ## SES permissions — sending | Permission | Description | Why SendOps needs it | |---|---|---| | `ses:SendEmail` | Send an email via the SES v2 API (supports simple, raw, and templated formats) | Used exclusively for the onboarding test send | SendOps uses `ses:SendEmail` **only** to send the initial test email during onboarding, which verifies that the integration is working end-to-end. After that, SendOps never sends email from your account again. All production sending is done by your own application using your own SES credentials — SendOps only monitors and reports on those sends. ## EventBridge permissions — read-only These permissions allow SendOps to verify that the EventBridge rule and targets created by the CloudFormation stack are correctly configured. SendOps does **not** have permission to create or modify EventBridge rules — those resources are managed by the CloudFormation stack. | Permission | Description | Why SendOps needs it | |---|---|---| | `events:DescribeRule` | Read the details of an EventBridge rule | Verifies the `sendops-ses-events` rule exists and is enabled | | `events:ListTargetsByRule` | List targets attached to an EventBridge rule | Confirms the rule is routing events to the SendOps API destination | ## Removing unused permissions If you do not plan to use certain SendOps features, you can remove the corresponding permissions from the policy: - **Not managing templates through SendOps?** Remove `ses:CreateEmailTemplate`, `ses:UpdateEmailTemplate`, `ses:DeleteEmailTemplate`. - **Already completed onboarding?** You can remove `ses:SendEmail` — it is only used for the initial test send. - **Not managing identities through SendOps?** Remove `ses:CreateEmailIdentity` and `ses:DeleteEmailIdentity`. - **Not managing the suppression list through SendOps?** Remove `ses:DeleteSuppressedDestination`. The read-only SES permissions and EventBridge permissions are required for core SendOps functionality (dashboards, alerts, and event pipeline verification). Removing them will break the connection. For more on securing cross-account access, see the [AWS IAM Best Practices](https://docs.aws.amazon.com/IAM/latest/UserGuide/best-practices.html) documentation. SendOps aligns with these recommendations by using external IDs, temporary credentials via STS, and scoped policies. ## Related pages - [AWS Integration](/aws-setup/connecting-aws) — create the IAM role and deploy the CloudFormation stack - [Supported AWS Regions](/aws-setup/supported-regions) — regions where these permissions apply --- # Supported AWS Regions Source: https://help.sendops.dev/aws-setup/supported-regions Section: AWS Reference > All AWS regions where Amazon SES is available and supported by SendOps, with guidance on choosing the right region. SendOps supports a specific set of AWS regions where Amazon SES is available. You select a region during the [AWS Integration](/aws-setup/connecting-aws) step of onboarding, and your CloudFormation stack, IAM role, and SES resources all operate in that region. ## Available regions | Region Name | Region Code | |---|---| | US East (N. Virginia) | `us-east-1` | | US East (Ohio) | `us-east-2` | | US West (N. California) | `us-west-1` | | US West (Oregon) | `us-west-2` | | Canada (Central) | `ca-central-1` | | Canada West (Calgary) | `ca-west-1` | | Europe (Ireland) | `eu-west-1` | | Europe (London) | `eu-west-2` | | Europe (Paris) | `eu-west-3` | | Europe (Frankfurt) | `eu-central-1` | | Europe (Zurich) | `eu-central-2` | | Europe (Stockholm) | `eu-north-1` | | Europe (Milan) | `eu-south-1` | | Asia Pacific (Mumbai) | `ap-south-1` | | Asia Pacific (Hyderabad) | `ap-south-2` | | Asia Pacific (Tokyo) | `ap-northeast-1` | | Asia Pacific (Seoul) | `ap-northeast-2` | | Asia Pacific (Osaka) | `ap-northeast-3` | | Asia Pacific (Singapore) | `ap-southeast-1` | | Asia Pacific (Sydney) | `ap-southeast-2` | | Asia Pacific (Jakarta) | `ap-southeast-3` | | Asia Pacific (Malaysia) | `ap-southeast-5` | | Middle East (Bahrain) | `me-south-1` | | Middle East (UAE) | `me-central-1` | | Israel (Tel Aviv) | `il-central-1` | | Africa (Cape Town) | `af-south-1` | | South America (São Paulo) | `sa-east-1` | ## Region selection during onboarding During the [AWS Integration](/aws-setup/connecting-aws) phase of onboarding, you choose a region from a searchable dropdown. The CloudFormation stack is deployed into this region, and it provisions the IAM role, EventBridge rule, and SES configuration set there. Your account is connected to a single region. ## Choosing a region If you are starting fresh and unsure which region to use, consider: - **Latency** — pick the region closest to your application servers to minimize API call latency when sending. - **Data residency** — some organizations require email data to remain in a specific geography. SES processes and stores data in the region you select. - **SES sandbox status** — each region has its own sandbox. Moving to production access in one region does not affect others. See [SES Sandbox & Production](/sending-email/ses-sandbox) for details. AWS may add SES support to new regions over time. Check the [AWS SES Regional Availability](https://docs.aws.amazon.com/general/latest/gr/ses.html) documentation for the most current list. If a new region is available in AWS but not listed here, contact [support@sendops.dev](mailto:support@sendops.dev) — adding support is typically fast. ## Related pages - [AWS Integration](/aws-setup/connecting-aws) — deploy the event pipeline and set up the cross-account IAM role - [IAM Permissions Explained](/aws-setup/iam-permissions) — full permission breakdown --- # Connection Issues Source: https://help.sendops.dev/troubleshooting/connection-issues Section: Troubleshooting > Diagnose and fix common AWS connection problems in SendOps, including IAM role failures, stack validation errors, and EventBridge delivery issues. When connecting your AWS account to SendOps, most issues fall into a few well-known categories. This page walks through each one with the likely cause and how to fix it. ## AWS Status Badge The **AWS Status Badge** in the sidebar header shows the current state of your AWS connection at a glance. Clicking it opens a popover with details such as region, account ID, and last validated time. | Badge State | Meaning | |---|---| | **Not Connected** | No AWS account has been linked yet. Click to start onboarding. | | **Connecting...** | The connection has been saved but not yet validated. | | **Validating...** | A validation job is currently running (AWS connection or stack validation). | | **Connection Error** | The most recent validation failed. Click to open onboarding and fix the issue. | | **Update Available** | Your CloudFormation stack is running an older template version. Click to update. | | **Sandbox** | Connected successfully, but your SES account is in sandbox mode. | | **AWS Connected** | Connected and validated with production SES access. | ## IAM role errors **Symptom:** The AWS connection step in onboarding fails with one of these messages: - "Access denied — ensure the IAM role ARN is correct and the Sesmail account is included in the trust policy with the correct External ID" - "IAM role not found — verify the role ARN and that it exists in your AWS account" - "Role trust policy must include the external ID condition" **Cause:** SendOps calls `sts:AssumeRole` to verify your IAM role during onboarding. The most common mistakes are: - The **IAM Role ARN** you entered does not match an existing role in your AWS account. - The **trust policy** on the role does not include the SendOps AWS Account ID in the `Principal` field. - The **External ID** in the trust policy's `Condition` block does not match the value shown in your onboarding page. - The trust policy is missing the `sts:ExternalId` condition entirely. **Fix:** If you used the **Launch Stack** button during onboarding, the CloudFormation template creates the `SendOpsRole` automatically with the correct trust policy. If the role already exists from a previous attempt, delete the old CloudFormation stack first and re-launch. If you set up the role manually: 1. Open your IAM role in the [AWS IAM Console](https://console.aws.amazon.com/iam/). 2. Click the **Trust relationships** tab and then **Edit trust policy**. 3. Verify the `Principal.AWS` value matches the SendOps Account ID shown on your onboarding page. 4. Verify the `sts:ExternalId` condition value matches the External ID shown on the same page. Copy and paste it directly to avoid typos. 5. Save and retry the connection in SendOps. Every SendOps organization gets a unique External ID, generated when onboarding is initialized. If you are setting up multiple organizations, each one has its own External ID and needs its own IAM role and trust policy. See [AWS Integration](/aws-setup/connecting-aws) for the full setup walkthrough. ## CloudFormation stack failed **Symptom:** The CloudFormation stack created during onboarding shows a status of `CREATE_FAILED` or `ROLLBACK_COMPLETE` in the AWS Console. **Cause:** Common reasons include: - **Insufficient permissions** — the IAM user or role you used to launch the stack does not have permission to create the required resources (IAM roles, SES configuration sets, EventBridge rules, EventBridge connections, API destinations). - **Resource name conflict** — a resource with the same name already exists in that region (e.g., an IAM role named `SendOpsRole`, a configuration set named `sendops-events`, or an EventBridge rule named `sendops-ses-events`). - **Service limit reached** — you have hit an AWS account limit for EventBridge rules, API destinations, or SES configuration sets. **Fix:** 1. Open the [CloudFormation Console](https://console.aws.amazon.com/cloudformation/) in the region where you launched the stack. 2. Click the failed stack and go to the **Events** tab. 3. Look for the first event with a `CREATE_FAILED` status — this is usually the root cause. The **Status reason** column explains the error. 4. Fix the underlying issue (grant permissions, delete the conflicting resource, or request a limit increase). 5. Delete the failed stack and re-launch it from the SendOps onboarding page. The stack creates these resources: `SendOpsRole` (IAM role), `SendOpsConfigSet` (SES configuration set named `sendops-events`), `SendOpsEventDestination` (EventBridge event destination on the config set), `SendOpsConnection` (EventBridge connection with API key auth), `SendOpsApiDestination` (API destination at 300 invocations/sec), `SendOpsEventBridgeRole` (IAM role for EventBridge), and `SendOpsEventRule` (EventBridge rule named `sendops-ses-events`). See [AWS Integration](/aws-setup/connecting-aws) for a detailed breakdown. ## Stack validation errors **Symptom:** You click **Run Validation** in the Connection Validation phase of onboarding, and one or more checks fail. **Cause:** SendOps runs a background validation job that verifies your CloudFormation stack resources are correctly deployed. The job checks six items: 1. **SES Configuration Set** — confirms the `sendops-events` configuration set exists in your selected region. 2. **EventBridge Rule** — confirms the `sendops-ses-events` rule exists and is enabled. 3. **Webhook Target** — confirms the EventBridge rule has a target pointing to the SendOps API destination (webhook endpoint). 4. **Channel Management** — probes that your IAM role has `ses:ListConfigurationSets` permission for creating and managing channels. 5. **Channel Settings** — probes that your IAM role has `ses:PutConfigurationSetDeliveryOptions` permission for syncing channel settings. 6. **Email Templates** — probes that your IAM role has `ses:ListEmailTemplates` permission for template management. The validation also reads your SES account status and reports whether sending is enabled, whether you are in sandbox mode, your sending limits, and how many verified identities exist. **Fix:** - If the configuration set or EventBridge rule is missing, the CloudFormation stack likely did not deploy successfully. Check the [CloudFormation Console](https://console.aws.amazon.com/cloudformation/) for stack status, delete any failed stacks, and re-launch from onboarding. - If the webhook target is missing, the stack may have partially deployed. Delete and re-create the stack. - If permission probes fail (Channel Management, Channel Settings, or Email Templates), your CloudFormation stack may be running an older template version. The validation page will show a **stack update card** with a link to update your stack to the latest version. After updating, click **Re-validate**. - If SES sending is disabled or your account is in sandbox mode, these are AWS-side settings. Visit the [AWS SES Console](https://console.aws.amazon.com/ses/) to request production access. Stack validation does not run automatically. You must click **Run Validation** (or **Re-validate** after making changes) to trigger the check. The validation job runs in the background and the page polls for results every 2 seconds. ## EventBridge not delivering events **Symptom:** Your AWS account is connected and stack validation passes, but SendOps is not receiving email events (the Messages Dashboard stays empty). **Cause:** SES events flow through EventBridge to SendOps via an API Destination (webhook). Check these in order: 1. **EventBridge rule is disabled** — open the [EventBridge Console](https://console.aws.amazon.com/events/) and verify the `sendops-ses-events` rule is in an **ENABLED** state. 2. **API destination target is missing** — click the rule and check that it has a target pointing to the `sendops-ingest` API destination. If the target is missing, the webhook endpoint is not connected. 3. **SES configuration set not assigned** — emails must be sent using a configuration set that has an EventBridge event destination for events to fire. The bootstrap configuration set created by the stack is `sendops-events`. Channels create their own configuration sets (named `sesmail-`) with EventBridge destinations auto-configured. 4. **Wrong region** — the EventBridge rule only captures events from the region it is deployed in. If you send email from a different region than where the stack is deployed, events will not be captured. **Fix:** - Re-enable the rule if it was disabled. - If the API destination target is missing, delete and re-create the CloudFormation stack from onboarding. - Verify your sending setup uses a configuration set with an EventBridge event destination. See [Understanding Channels](/channels/understanding-channels) for how channels map to configuration sets. - Deploy the CloudFormation stack in every region from which you send email. See [Supported AWS Regions](/aws-setup/supported-regions). EventBridge only captures events generated after the rule is active. If the rule was disabled or misconfigured for a period, events from that window are lost and cannot be backfilled. ## Template version mismatch **Symptom:** The AWS Status Badge shows **Update Available**, or the Connection Validation page displays a stack update card after re-validating. **Cause:** SendOps periodically adds new features that require additional IAM permissions or resources in the CloudFormation stack. When the deployed stack version is older than the current template version, permission probes may fail and new features (such as channel management or template sync) will not work. **Fix:** 1. Open the stack update link shown on the validation page or in the AWS Status Badge popover. This opens the CloudFormation Console with the latest template pre-filled. 2. Follow the CloudFormation update wizard to apply the changes. 3. Return to SendOps and click **Re-validate** to confirm the update was successful. ## Still stuck? If none of the above resolves your issue: - Check the [AWS SES Console](https://console.aws.amazon.com/ses/) directly to confirm your SES account is active and not under review or on probation. - Review [IAM Permissions Explained](/aws-setup/iam-permissions) to verify your role has all required permissions. - Contact [support@sendops.dev](mailto:support@sendops.dev) with your organization name and the error message you see in the dashboard. --- # Domain Verification Source: https://help.sendops.dev/troubleshooting/domain-verification Section: Troubleshooting > Troubleshoot common domain verification issues in SendOps, including DNS propagation delays, DKIM failures, and SPF/DMARC misconfigurations. Domain verification confirms that you own the domain you want to send from. SendOps manages this process through AWS SES, which requires DKIM CNAME records in your DNS. Verification succeeds when SES confirms that all three DKIM records are in place and resolve correctly. Most verification problems come down to DNS records that are missing, malformed, or have not propagated yet. ## DNS records not propagating **Symptom:** You added the DNS records but SendOps still shows the domain as "Pending" after several minutes. **Cause:** DNS changes take time to propagate across the internet. Propagation depends on the TTL (time to live) of your existing records and your DNS provider's infrastructure. **Fix:** 1. Wait at least 15 to 30 minutes. Some providers can take up to 72 hours, though most complete within an hour. 2. Verify the records are published correctly by querying DNS directly from your terminal: 3. If the records do not appear in DNS query results, return to your DNS provider and confirm they were saved correctly. If your domain previously had a high TTL (e.g., 86400 seconds / 24 hours), old cached records may take that long to expire before new records become visible. Consider lowering TTL before making DNS changes, then restoring it afterward. ## DKIM not verifying **Symptom:** The domain DKIM status shows "Pending" or "Failed" in the Identities table even though you added DNS records. **Cause:** The DKIM CNAME records are incorrect. Common mistakes include: - **Trailing dot issue** — some DNS providers automatically append a dot to the record value; others require you to include it. Adding a dot when the provider already appends one creates a double dot (`example.com..`), which fails. - **Wrong record type** — DKIM records must be CNAME records, not TXT records. - **Truncated value** — the CNAME target value is long. Some DNS provider interfaces truncate it if you do not paste carefully. - **Wrong token** — SES generates three unique DKIM tokens for your domain. All three CNAME records must be added using the exact token values shown in the SendOps dashboard, not generic selectors like `selector1`, `selector2`, `selector3`. **Fix:** 1. Check that all three CNAME records exist. SES generates unique tokens for each domain (not numbered selectors). The record names follow the pattern `{token}._domainkey.example.com` pointing to `{token}.dkim.amazonses.com`. 2. Verify each record with `dig`, using the actual token values from your SendOps dashboard: ```bash dig CNAME {token1}._domainkey.example.com +short dig CNAME {token2}._domainkey.example.com +short dig CNAME {token3}._domainkey.example.com +short ``` 3. Compare the output to the values shown in the SendOps Identities page. They must match exactly. 4. If your DNS provider adds a trailing dot automatically, do not include one yourself. ## Wrong DNS provider settings **Symptom:** Records appear correct in your DNS provider's interface but do not resolve when queried. Different DNS providers have quirks that can cause issues: - **Cloudflare** — disable the orange cloud (proxy) for CNAME records used by SES. DKIM CNAMEs must be DNS-only (grey cloud). Cloudflare's proxy rewrites the record and breaks DKIM verification. - **GoDaddy** — omit the domain name from the "Host" field. For `{token}._domainkey.example.com`, enter only `{token}._domainkey`. GoDaddy appends the domain automatically. - **Route 53** — include the trailing dot in CNAME target values (e.g., `{token}.dkim.amazonses.com.`). Route 53 treats records as fully qualified domain names. - **Namecheap** — similar to GoDaddy, enter only the subdomain portion in the "Host" field. When in doubt, query the actual DNS to see what resolves rather than relying on your provider's UI. ## Domain stuck in "Pending" **Symptom:** The domain has been in "Pending" for more than 72 hours despite correct DNS records. **Fix:** 1. Verify the records resolve correctly using `dig` or `nslookup` as shown above. 2. Click the **Refresh** button (the circular arrow icon) on the domain row in the Identities page. This calls SES to re-check DKIM status and updates the domain record. 3. Check the domain status directly in the [AWS SES Console](https://console.aws.amazon.com/sesv2/) under **Identities**. If SES shows it as verified but SendOps does not, try clicking Refresh again — the status should sync. 4. If SES also shows "Pending," try removing the domain from SendOps and re-adding it. This calls SES `CreateEmailIdentity` again, which forces SES to re-check the DNS records. Note that removing a domain from SendOps enqueues a deletion of the identity from SES, so you will need to re-add the DNS records after adding the domain again. 5. Confirm you are checking the correct AWS region — domain verification is region-specific, and your organization's region is set during AWS connection setup. SendOps tracks several DKIM status values from SES: **Pending** (waiting for DNS verification), **Success** (DKIM verified), **Failed** (verification failed), **Temporary Failure** (transient issue, may resolve on its own), and **Not Started** (DKIM has not been initiated). The domain is marked as verified only when DKIM status reaches Success. ## SPF and DMARC issues **Symptom:** Emails are delivered but fail SPF or DMARC checks, visible in recipient mail headers or deliverability reports from your email provider. ### SPF SPF tells receiving servers which mail servers are authorized to send on behalf of your domain. SendOps generates a recommended SPF record when you add a domain: `v=spf1 include:amazonses.com ~all`. Common misconfigurations: - **Missing SES include** — your SPF record must include `include:amazonses.com`. A typical SPF record looks like: `v=spf1 include:amazonses.com ~all` - **Multiple SPF records** — a domain must have only one SPF TXT record. If you have multiple, merge them into a single record. - **Too many lookups** — SPF allows a maximum of 10 DNS lookups. If you have many `include` statements, you may exceed this limit. Use an SPF flattening tool if needed. ### DMARC DMARC builds on SPF and DKIM to tell receivers what to do when authentication fails. SendOps generates a recommended DMARC record using `p=quarantine` (e.g., `v=DMARC1; p=quarantine; rua=mailto:dmarc@example.com`). Common issues: - **No DMARC record** — add a TXT record at `_dmarc.example.com`. The SendOps-recommended policy is: `v=DMARC1; p=quarantine; rua=mailto:dmarc@example.com` - **Strict SPF alignment** — if your DMARC policy uses `aspf=s` (strict SPF alignment), the envelope sender domain must exactly match the From header domain. Consider using `aspf=r` (relaxed) if you encounter alignment failures. - **Policy too aggressive too soon** — if you are not yet confident all legitimate mail passes authentication, start with `p=none` to monitor, then move to `p=quarantine` (the SendOps default), and finally `p=reject` once everything is verified. ## What's next? - If your domain is verified but emails are not arriving, see [Deliverability Problems](/troubleshooting/deliverability-problems). - For a full guide on DNS records, see [DNS Configuration](/domains/dns-configuration). - If your AWS connection itself is failing, see [Connection Issues](/troubleshooting/connection-issues). --- # Deliverability Problems Source: https://help.sendops.dev/troubleshooting/deliverability-problems Section: Troubleshooting > Troubleshoot high bounce rates, complaint spikes, SES account reviews, and authentication failures that affect your email deliverability. Deliverability problems occur when your emails fail to reach recipients' inboxes. SendOps surfaces bounce rates, complaint rates, and reputation data through the [Deliverability Reports](/reports/deliverability-reports) page so you can identify and address issues quickly. You can filter all deliverability data by channel to isolate problems to a specific sending stream. AWS SES enforces strict thresholds. A bounce rate above **5%** triggers a warning, and above **10%** is critical. A complaint rate above **0.08%** triggers a warning, and above **0.1%** is critical and can lead to an account review, sending pause, or permanent suspension. Monitor these metrics closely in your SendOps dashboard and take action before you reach these limits. ## Understanding bounce types Not all bounces are equal. SES classifies bounces into two categories: ### Hard bounces (Permanent) The recipient address is permanently invalid. Common causes: - The email address does not exist (typo or deactivated account). - The domain does not exist or has no MX records. - The recipient server permanently rejected the message. **Hard bounces count toward your SES bounce rate.** You should immediately stop sending to addresses that hard bounce. SES adds them to the account-level suppression list automatically when suppression is enabled for your channel. ### Soft bounces (Transient) The delivery failed temporarily. Common causes: - The recipient's mailbox is full. - The recipient's server is temporarily unavailable. - The message is too large. - A connection error or timeout occurred. SES automatically retries soft bounces for a period before giving up. Repeated soft bounces to the same address may indicate the address is effectively dead. SendOps breaks down bounce rates into hard bounce, soft bounce, and total bounce KPIs on the [Deliverability Reports](/reports/deliverability-reports) page, along with a bounce reasons chart that shows counts by sub-type (address not found, mailbox full, content rejected, message too large, connection timeout, etc.). ## Reducing complaint rates A complaint occurs when a recipient marks your email as spam using the "Report Spam" button in their email client. ISPs report these back to SES via feedback loops. To keep complaint rates below the 0.1% critical threshold: - **Include a clear unsubscribe link** in every marketing or bulk email. Make it easy to find and one-click when possible. - **Only send to people who opted in** -- never purchase email lists or scrape addresses. - **Set expectations at sign-up** -- tell subscribers what kind of email they will receive and how often. - **Honor unsubscribes immediately** -- do not continue sending to people who have opted out. - **Segment your audience** -- send relevant content to the right people rather than blasting your entire list. - **Monitor engagement** -- if recipients are not opening your emails over several months, consider removing them from your list. SendOps shows complaint reasons broken down by feedback type on the [Deliverability Reports](/reports/deliverability-reports) page, so you can see whether complaints are primarily "abuse", "not-spam", or other categories. SES maintains an account-level suppression list that automatically prevents sending to addresses that have previously hard bounced or complained. SendOps shows and manages this suppression list directly on the [Deliverability Reports](/reports/deliverability-reports) page -- you can search, sort, and delete entries without going to the AWS Console. Each channel's suppression behavior is controlled by its **Suppression Reasons** setting, which can be set to suppress on bounces only, bounces and complaints, or disabled entirely. ## Configuring deliverability notifications SendOps can alert you when deliverability metrics cross thresholds. Under [Configuring Notifications](/notifications/configuring-notifications), the **Deliverability** category includes: - **Bounce Rate Threshold** -- triggered when bounce rate exceeds a configured percentage. Default threshold is 5%. - **Complaint Rate Threshold** -- triggered when complaint rate exceeds a configured percentage. Default threshold is 0.1%. - **Bounce Rate Trend** -- alerts on a sustained upward trend in bounce rates. - **Complaint Rate Trend** -- alerts on a sustained upward trend in complaint rates. - **Blocklist Detection** -- triggered when a sending IP or domain is detected on a blocklist. - **Suppression List Growth Spike** -- alerts when the suppression list grows unusually fast. For bounce and complaint rate thresholds, account admins can set **per-channel threshold overrides** so that channels with different risk profiles (for example, marketing vs. transactional) can have different alert thresholds. Notifications are delivered via in-app alerts and email by default. ## Responding to an SES account review If your bounce or complaint rates exceed thresholds, AWS may place your SES account under review. During a review: 1. **You can still send email**, but AWS is watching your metrics closely. 2. You receive an email from AWS with the subject "Amazon SES Sending Review" explaining the issue. 3. You typically have **a limited window** (often 24-48 hours) to improve your metrics before AWS may pause your sending. **Steps to respond:** 1. Immediately identify the source of bounces or complaints in your SendOps [Deliverability Reports](/reports/deliverability-reports). Filter by channel and date range to find the spike. 2. Check the ISP breakdown table to see if the problem is concentrated at a specific mailbox provider. 3. Stop any campaign or sending flow that is generating the problem. 4. Clean your recipient lists -- remove invalid addresses, unengaged recipients, and anyone who has complained. 5. Reply to the AWS review notification with a summary of what caused the issue and what you have done to fix it. 6. Monitor your rates in SendOps over the following days to confirm improvement. The reputation score on the Deliverability Reports page shows your current standing. If AWS pauses your sending, you will need to submit a case through AWS Support to have it reinstated. ## IP reputation and shared vs. dedicated IPs By default, SES sends email from a pool of **shared IP addresses** managed by AWS. Your reputation is tied to the overall health of that shared pool plus your own sending behavior (domain reputation). If you send high volumes (over 100,000 emails per day), consider **dedicated IPs** in SES: - **Pros** -- your reputation is entirely under your control; no other senders can affect it. - **Cons** -- you must warm up new IPs gradually, and low-volume senders may see worse deliverability than the shared pool. Dedicated IPs are configured in the AWS SES Console. SendOps monitors deliverability regardless of whether you use shared or dedicated IPs. ## Authentication issues (DKIM, SPF, DMARC) Failed authentication is a leading cause of emails landing in spam or being rejected outright. Check these in order: 1. **DKIM** -- ensure all three DKIM CNAME records are published and resolving correctly. See [Domain Verification](/troubleshooting/domain-verification) for detailed checks. 2. **SPF** -- your SPF record must include `include:amazonses.com`. If you use a custom MAIL FROM domain, the SPF record goes on that subdomain. 3. **DMARC** -- verify your DMARC record at `_dmarc.yourdomain.com`. Start with `p=none` to monitor before enforcing. You can verify domain authentication status on the [Email Identities](/domains/email-identities) page in SendOps, which shows DKIM verification state for each domain. ## Common deliverability checklist Use this checklist when investigating deliverability drops: - [ ] Bounce rate is below 5% (warning) / 10% (critical) in [Deliverability Reports](/reports/deliverability-reports) - [ ] Complaint rate is below 0.08% (warning) / 0.1% (critical) - [ ] DKIM is passing (all three CNAME records resolving) - [ ] SPF is passing (`include:amazonses.com` in SPF record) - [ ] DMARC record exists and alignment is passing - [ ] SES account is not in sandbox mode (see [SES Sandbox & Production](/sending-email/ses-sandbox)) - [ ] Sending to opted-in recipients only - [ ] Unsubscribe link is present and functional - [ ] Channel suppression reasons are configured appropriately (bounces only, or bounces and complaints) - [ ] No recent changes to DNS records or sending infrastructure - [ ] Deliverability notifications are enabled so you are alerted to threshold breaches ## What's next? - Review your current metrics in [Deliverability Reports](/reports/deliverability-reports). - Check [Engagement Metrics](/reports/engagement-metrics) to understand open and click rates. - Set up [deliverability notifications](/notifications/configuring-notifications) to get alerted before thresholds are breached. - For DNS and authentication troubleshooting, see [Domain Verification](/troubleshooting/domain-verification). --- # Glossary Source: https://help.sendops.dev/reference/glossary Section: Reference > Definitions of key terms and concepts used throughout SendOps and AWS SES. A reference of terms you will encounter in the SendOps dashboard, this documentation, and the AWS SES ecosystem. --- **AWS Status Badge** — a header indicator in the SendOps dashboard that shows the state of your AWS connection. Possible states: Not Connected, Connecting, Validating, Connection Error, Update Available, Sandbox, and AWS Connected. See [AWS Integration](/aws-setup/connecting-aws). **Bounce (Hard)** — a permanent delivery failure. The recipient address does not exist, the domain is invalid, or the receiving server has permanently rejected the message. Hard bounces count toward your SES bounce rate and the address should not be retried. SES warning threshold is 5%, critical threshold is 10%. See [Deliverability Problems](/troubleshooting/deliverability-problems). **Bounce (Soft)** — a temporary delivery failure. The recipient's mailbox may be full, the server may be temporarily unavailable, or the message may be too large. SES retries soft bounces automatically before giving up. **Channel** — a named configuration in SendOps that maps to an AWS SES configuration set (named `sesmail-` in your AWS account). Channels have a Purpose (Default, Transactional, Marketing, Onboarding, or Custom) and control how emails are tracked and processed. Each channel has settings for sending enabled/disabled, TLS policy, max delivery duration, auto validation threshold, suppression override, and engagement tracking. Four default channels are provisioned during onboarding: Default, Transactional, Marketing, and Onboarding. See [Understanding Channels](/channels/understanding-channels). **Channel Sync Status** — indicates whether a channel's configuration in SendOps matches its corresponding SES configuration set. Possible values: synced, syncing, or error. See [Managing Channels](/channels/managing-channels). **Click Tracking** — a feature that rewrites links in HTML emails to pass through a tracking domain, recording when a recipient clicks a link. Click events appear in the SendOps [Messages Dashboard](/reports/messages-dashboard). Requires a tracking domain to be configured. See [Tracking Domains](/domains/tracking-domains). **CloudFormation Stack** — the AWS CloudFormation template that provisions the resources SendOps needs in your AWS account. The stack creates: SendOpsRole (IAM role), SendOpsConfigSet (SES configuration set), SendOpsEventDestination, SendOpsConnection, SendOpsApiDestination, SendOpsEventBridgeRole, SendOpsEventRule. See [AWS Integration](/aws-setup/connecting-aws). **Complaint** — a report generated when a recipient marks your email as spam using their email client's "Report Spam" button. ISPs send complaints back to SES through feedback loops. SES warning threshold is 0.08%, critical threshold is 0.1%. See [Deliverability Problems](/troubleshooting/deliverability-problems). **Config Set / Configuration Set** — an SES resource that defines event publishing rules. When you send email through a configuration set, SES publishes delivery events to destinations via EventBridge. SES supports 10 event types: Send, Delivery, Bounce, Complaint, Reject, Open, Click, DeliveryDelay, RenderingFailure, and Subscription. In SendOps, each channel maps to a configuration set named `sesmail-`. See [AWS Integration](/aws-setup/connecting-aws). **Connections** — a section in the SendOps dashboard for managing external integrations. It has two tabs: **Webhooks** for configuring HTTP callback endpoints that receive event notifications, and **Integrations** for connecting third-party services like Slack. **DKIM (DomainKeys Identified Mail)** — an email authentication method that attaches a cryptographic signature to outgoing messages. The receiving server verifies the signature against public keys published as DNS CNAME records, confirming the message was not altered in transit and was sent by an authorized server. In SendOps, successful DKIM verification is what marks a domain as verified. See [DNS Configuration](/domains/dns-configuration). **DMARC (Domain-based Message Authentication, Reporting, and Conformance)** — a DNS-based policy that tells receiving servers what to do when SPF or DKIM checks fail. DMARC also enables aggregate reporting so you can monitor authentication results. Published as a TXT record at `_dmarc.yourdomain.com`. **Domain Identity** — a domain that has been verified in AWS SES via DKIM, proving you own it. Once verified (DKIM status is "Success"), you can send email from any address at that domain. Domains can be assigned to a channel and have associated email identities. See [Adding a Domain](/domains/adding-a-domain). **Email Identity** — a specific sender email address registered in SendOps and associated with a verified domain. Email identities are assigned to a channel and marked as managed, meaning SendOps tracks their usage. See [Email Identities](/domains/email-identities). **Engagement** — a measure of how recipients interact with your emails. In SendOps, engagement is tracked through open and click events, with performance broken down by template. See [Engagement Metrics](/reports/engagement-metrics). **Event Destination** — the mechanism that delivers SES email events from your AWS account to SendOps. SendOps uses an auto-configured EventBridge rule with an API Destination (authenticated via x-api-key, rate-limited to 300 invocations per second). See [AWS Integration](/aws-setup/connecting-aws). **EventBridge** — an AWS service that routes events between AWS services and external targets. SendOps uses EventBridge to receive real-time SES email events (all 10 event types: Send, Delivery, Bounce, Complaint, Reject, Open, Click, DeliveryDelay, RenderingFailure, Subscription) from your AWS account via an API Destination. **External ID** — a unique identifier included in STS AssumeRole requests to prevent confused deputy attacks. Each SendOps organization has its own External ID, which must be set as a condition in your IAM role's trust policy. See [AWS Integration](/aws-setup/connecting-aws). **IAM Role** — an AWS Identity and Access Management entity with specific permissions that can be assumed by trusted parties. SendOps uses a cross-account IAM role to access your SES resources without requiring static API keys. The role is created by the CloudFormation stack during onboarding. **Identity** — a general term for a verified sender in SES, which can be either a domain identity or an email identity. **Managed Identity** — a domain or email identity that is tracked by SendOps. When events are ingested, SendOps filters by managed identities to associate events with the correct organization. Only events from managed identities are processed and displayed in the dashboard. **Open Tracking** — a feature that inserts a tiny invisible image (tracking pixel) into HTML emails. When the recipient's email client loads the image, an open event is recorded. Open tracking requires HTML email and is not 100% reliable due to image blocking by some clients. **Ownership Transfer** — the process of transferring the Owner role from one team member to another. The current Owner initiates the transfer from Workspace Settings, and the target member has 72 hours to accept via email link or dashboard banner. On acceptance, the former Owner becomes an Org Admin. All transfer actions are recorded in the [audit log](/team/audit-log). See [Transferring ownership](/team/members-and-roles#transferring-ownership). **Roles** — SendOps supports seven organization roles that control access and permissions: **Owner** (full control including billing, ownership transfer, and account deletion), **Org Admin** (manages members, billing, and org settings but cannot transfer ownership or delete the org), **Infra Admin** (manages AWS connections, domains, tracking domains, and infrastructure configuration), **Developer** (manages templates, GitHub connections, channels, and day-to-day email operations), **Marketer** (views analytics and reports, manages suppression lists, and views templates in read-only mode), **Financial** (invoices, payment methods, billing settings, and SES cost estimates), and **Viewer** (read-only access to dashboards, templates, and analytics). See [Team Members & Roles](/team/members-and-roles). **SES Sandbox** — the default state for new SES accounts. In sandbox mode, you can only send to verified email addresses and have reduced sending limits. You must request production access from AWS to send to arbitrary recipients. The AWS Status Badge shows "Sandbox" when your account is in sandbox mode. See [SES Sandbox & Production](/sending-email/ses-sandbox). **Setup Guide** — the four-phase onboarding process in SendOps that walks you through connecting your AWS account. The phases are: AWS Integration, Connection Validation, Domain Verification, and First Test Send. See [Creating Your Account](/getting-started/creating-your-account). **SPF (Sender Policy Framework)** — a DNS-based authentication method that specifies which mail servers are authorized to send email on behalf of a domain. Published as a TXT record. For SES, the record should include `include:amazonses.com`. **STS (Security Token Service)** — an AWS service that issues temporary, limited-privilege credentials. SendOps uses STS AssumeRole to obtain short-lived credentials for accessing your AWS resources. **Suppression List** — an account-level list maintained by SES that prevents sending to email addresses that have previously hard bounced or complained. The suppression list reduces future bounces and protects your sender reputation. Channels can override suppression behavior via the suppression override setting. **Template** — a reusable email layout with placeholders for dynamic content. In SendOps, templates are managed through GitHub integration — connect a repository and SendOps syncs your templates automatically. Templates are deployed to SES as SES templates. See [Template Management](/templates/template-management). **Tracking Domain** — a custom subdomain (e.g., `track.example.com`) used for open and click tracking instead of the default SES tracking domain. Tracking domains require DNS verification (CNAME) and SES identity verification (DKIM). Using your own tracking domain improves deliverability by keeping all URLs on-brand. See [Tracking Domains](/domains/tracking-domains). **Webhook** — an HTTP callback endpoint that sends real-time notification data to an external URL when specific events occur. Webhooks are configured under the **Webhooks** tab in the **Connections** section of the SendOps dashboard. Each webhook has a signing secret for payload verification. See [Webhooks](/notifications/webhooks). --- # AWS SES Resources Source: https://help.sendops.dev/reference/aws-ses-resources Section: Reference > Curated links to AWS documentation, SDKs, and tools for working with Amazon Simple Email Service. This page collects the most useful AWS documentation and resources for working with Amazon SES. Bookmark it as a quick-reference when you need to look up SES configuration details, SDK methods, or service limits. SendOps documentation covers how SES integrates with the SendOps dashboard. For SES-specific configuration, API details, and service limits, AWS documentation is the definitive reference. The links below point to the latest versions. ## Amazon SES documentation - [SES Developer Guide](https://docs.aws.amazon.com/ses/latest/dg/) — the comprehensive guide to SES concepts, setup, sending, and monitoring. - [SES v2 API Reference](https://docs.aws.amazon.com/ses/latest/APIReference-V2/) — full reference for the SES v2 API actions, data types, and error codes. SendOps uses the SES v2 API exclusively. - [SES Best Practices](https://docs.aws.amazon.com/ses/latest/dg/best-practices.html) — AWS recommendations for list management, authentication, compliance, and deliverability. - [SES Sending Limits](https://docs.aws.amazon.com/ses/latest/dg/manage-sending-quotas.html) — how sending quotas and rate limits work, and how to request increases. In sandbox mode, accounts are limited to 200 emails per 24-hour period at 1 email per second, and can only send to verified recipients. - [Request SES Production Access](https://docs.aws.amazon.com/ses/latest/dg/request-production-access.html) — instructions for moving your SES account out of sandbox mode by submitting an AWS support ticket. See [SES Sandbox & Production](/sending-email/ses-sandbox) for how this affects SendOps. ## AWS SDKs for SES - [AWS SDK for JavaScript (SESv2 Client)](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/client/sesv2/) — the official Node.js/TypeScript SDK for interacting with the SES v2 API. - [AWS SDK for Python / Boto3 (SESv2)](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/sesv2.html) — the official Python SDK for SES v2 operations. - [AWS SDK for Go (SESv2)](https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/service/sesv2) — the official Go SDK for SES v2 operations. The SendOps backend is built with Go and uses this SDK. ## Related AWS services These AWS services work alongside SES and are part of the SendOps integration: - [AWS IAM Documentation](https://docs.aws.amazon.com/IAM/latest/UserGuide/) — identity and access management. Covers the cross-account IAM role (`SendOpsRole`) that SendOps assumes via STS to access your SES resources. See also [IAM Permissions Explained](/aws-setup/iam-permissions). - [AWS CloudFormation Documentation](https://docs.aws.amazon.com/cloudformation/) — infrastructure as code. SendOps uses a CloudFormation stack to provision the full integration: the IAM role, SES configuration set with event destination, EventBridge rule, webhook connection, API destination, and EventBridge IAM role — seven resources in total. See [AWS Integration](/aws-setup/connecting-aws). - [AWS EventBridge Documentation](https://docs.aws.amazon.com/eventbridge/) — event routing service. SES publishes all 10 event types (send, reject, bounce, complaint, delivery, open, click, renderingFailure, deliveryDelay, subscription) to EventBridge. An EventBridge rule routes these events to an API Destination that delivers them as webhook POST requests to SendOps. - [AWS STS Documentation](https://docs.aws.amazon.com/STS/latest/APIReference/) — Security Token Service. SendOps uses `sts:AssumeRole` with an external ID to obtain short-lived credentials scoped to your account. ## AWS Console links Direct links to relevant sections of the AWS Management Console: - [SES Console](https://console.aws.amazon.com/ses/) — manage identities, configuration sets, suppression lists, and sending statistics. - [IAM Console](https://console.aws.amazon.com/iam/) — manage the cross-account role that SendOps uses. - [CloudFormation Console](https://console.aws.amazon.com/cloudformation/) — view and manage the SendOps stack. - [EventBridge Console](https://console.aws.amazon.com/events/) — check event rules and targets. SES, EventBridge, and CloudFormation are regional services. Make sure you are viewing the correct region in the AWS Console. The region selector is in the top-right corner of the console. See [Supported AWS Regions](/aws-setup/supported-regions) for which regions SendOps supports. ## What's next? - Review the [Glossary](/reference/glossary) for definitions of SES and SendOps terms. - Follow the [AWS Integration](/aws-setup/connecting-aws) guide to set up the integration. - If you run into problems, check [Connection Issues](/troubleshooting/connection-issues) for common fixes. ---