# EventBridge Events

Source: https://wraps.dev/docs/infrastructure/events

Every email event flows through your AWS account's EventBridge bus. Create rules to build alerts, analytics, workflows, and more.

## How It Works

When you send an email through Wraps, SES publishes event notifications to the default EventBridge bus in your AWS account. Wraps creates a rule on that bus to capture events into its processing pipeline — but because it's _your_ bus, you can add your own rules alongside Wraps' rule with zero configuration.

architecture.txt

```
# Email Event Flow## Your App ─→ SES ─→ Configuration Set ─→ EventBridge (default bus)#                                              │#                                    ┌─────────┴─────────┐#                                    │                    │#                              Wraps Rule           Your Rules#                                    │                    │#                                   SQS              [targets]#                                    │#                                 Lambda#                                    │#                                DynamoDB
```

Your events, your bus, your rules

EventBridge evaluates every rule on the bus independently. Adding your own rules has no impact on Wraps' processing pipeline, and you get native fan-out, content-based filtering, and IAM security — no webhook endpoints to manage.

## What Wraps Handles Automatically

Wraps' built-in pipeline captures all configured events and processes them into DynamoDB. You get this for free — custom rules are for anything extra you want to build.

| Field | Source | Description |
| --- | --- | --- |
| `messageId` | mail.messageId | SES message ID (partition key) |
| `sentAt` | mail.timestamp | When the email was sent (sort key) |
| `accountId` | envelope.account | Your AWS account ID |
| `from` | mail.source | Sender email address |
| `to` | mail.destination | Recipient email addresses |
| `subject` | mail.commonHeaders | Email subject line |
| `eventType` | detail.eventType | Event type (Send, Delivery, Bounce, etc.) |
| `eventData` | Full event JSON | Complete event payload for detailed analysis |
| `expiresAt` | Computed | TTL (90 days Production, 365 days Enterprise) |

Suppression normalization

When SES reports a bounce with sub-type `Suppressed` or `OnAccountSuppressionList`, Wraps normalizes these into a distinct `Suppressed` event type in DynamoDB. This makes it easy to distinguish real bounces from suppression-list entries.

## Event Types Reference

SES uses three different naming conventions depending on where you look. This mapping table is the key to writing EventBridge rules correctly.

| Event | EventBridge `detail-type` | `detail.eventType` | When It Fires |
| --- | --- | --- | --- |
| Send | `Email Sent` | `Send` | Email accepted by SES for delivery |
| Delivery | `Email Delivered` | `Delivery` | Recipient mail server accepted the email |
| Open | `Email Opened` | `Open` | Recipient opened the email (tracking pixel loaded) |
| Click | `Email Clicked` | `Click` | Recipient clicked a tracked link |
| Bounce | `Email Bounced` | `Bounce` | Email could not be delivered |
| Complaint | `Email Complaint Received` | `Complaint` | Recipient marked email as spam |
| Reject | `Email Rejected` | `Reject` | SES rejected the email (virus, policy) |
| Delivery Delay | `Email Delivery Delayed` | `DeliveryDelay` | Temporary delivery issue, SES will retry |
| Rendering Failure | `Email Rendering Failed` | `Rendering Failure` | SES template rendering failed |
| Subscription | `Email Subscribed` | `Subscription` | Recipient changed subscription preferences |
| Inbound Email | `email.received` | — | Inbound email received (source: `wraps.inbound`) |

### Delivery Events

Send — Email accepted by SES

Delivery — Accepted by recipient mail server

Bounce — Email could not be delivered

Reject — SES refused to send

Delivery Delay — Temporary delivery issue

### Engagement Events

Open and click events carry the recipient's IP address and user agent. Wraps stores the user agent to filter bot opens out of your metrics and discards the IP address without storing it — the full event, IP included, still lands in your own AWS account. User agents are personal data in the EU and UK, so drop `OPEN` and `CLICK` from your event types if you don't want engagement tracked at all — SES stops emitting those events entirely, so they never reach your own EventBridge bus or DynamoDB history either. See [Section 1.5 of our privacy policy](https://wraps.dev/privacy).

Event types aren't set in a config file — there isn't one for infrastructure. They're chosen at the CLI: on a new deployment, run `wraps email init` and pick the **Custom** configuration path (Starter doesn't enable event tracking at all; Production and Enterprise both track all ten event types and don't prompt). On an existing deployment, run `wraps email upgrade` and choose the event types action. Either way, you're asked which event types should reach your EventBridge bus and DynamoDB history — Send, Delivery, Open, Click, Reject, Rendering Failure, Delivery Delay, and Subscription are all offered, and all are selected by default. Deselect `Open` and `Click` there to stop engagement tracking. `Bounce` and `Complaint` aren't offered as choices — they're always included, since dropping either would leave your pipeline blind to bounces and complaints and your domain reputation would degrade.

Open — Recipient opened the email

Click — Recipient clicked a link

Subscription — Preference change

### System Events

Complaint — Marked as spam

Rendering Failure — Template error

### Inbound Events

Inbound Email — email.received (source: wraps.inbound)

## Bounce & Complaint Reference

Bounces and complaints are the most important events to handle correctly. AWS monitors your bounce and complaint rates and will suspend your account if they exceed thresholds.

SES suspension thresholds

AWS will place your SES account under review if your bounce rate exceeds **5%** or your complaint rate exceeds **0.1%**. Persistent violations lead to sending suspension.

Bounce Type Matrix

| bounceType | bounceSubType | Recommended Action |
| --- | --- | --- |
| Permanent | `General` | Suppress immediately — address is invalid |
| Permanent | `NoEmail` | Suppress — mailbox does not exist |
| Permanent | `Suppressed` | Already on SES suppression list (Wraps normalizes to Suppressed) |
| Permanent | `OnAccountSuppressionList` | Account-level suppression (Wraps normalizes to Suppressed) |
| Permanent | `UnsubscribedRecipient` | Recipient unsubscribed — remove from mailing list |
| Transient | `General` | Retry later — temporary issue |
| Transient | `MailboxFull` | Retry, suppress after repeated failures |
| Transient | `MessageTooLarge` | Reduce message size and resend |
| Transient | `ContentRejected` | Review email content for policy violations |
| Transient | `AttachmentRejected` | Review attachment type and size |
| Undetermined | `Undetermined` | Investigate manually — check diagnostic code |

Wraps suppression normalization

Wraps automatically detects bounces with sub-type `Suppressed` or `OnAccountSuppressionList` and stores them with event type `Suppressed` in DynamoDB instead of `Bounce`. This keeps your bounce metrics accurate by excluding addresses that were already suppressed.

## Creating Custom Rules

The default EventBridge bus evaluates all rules independently. Your rules run alongside Wraps' rule with no interference. Create rules in the AWS Console, CLI, CDK, or Terraform using these event patterns.

Match all SES events

Capture every email event on the bus. Use this to forward everything to a single target.

pattern-all-events.json

```
{  "source": ["aws.ses"],  "detail-type": [    "Email Sent",    "Email Delivered",    "Email Bounced",    "Email Complaint Received",    "Email Opened",    "Email Clicked",    "Email Rejected",    "Email Rendering Failed",    "Email Delivery Delayed",    "Email Subscribed"  ]}
```

Filter bounces and complaints only

Alert on the events that matter most for sender reputation.

pattern-bounces-complaints.json

```
{  "source": ["aws.ses"],  "detail-type": [    "Email Bounced",    "Email Complaint Received"  ]}
```

Filter permanent bounces only

Use content-based filtering to match only permanent hard bounces. Transient bounces are excluded.

pattern-permanent-bounces.json

```
{  "source": ["aws.ses"],  "detail-type": ["Email Bounced"],  "detail": {    "bounce": {      "bounceType": ["Permanent"]    }  }}
```

Match inbound email events

Inbound events use a different source. Match them separately from outbound SES events.

pattern-inbound.json

```
{  "source": ["wraps.inbound"],  "detail-type": ["email.received"]}
```

### Available Targets

| Target | Use Case |
| --- | --- |
| Lambda | Custom processing, database writes, API calls |
| SNS | Fan-out to email, SMS, Slack, PagerDuty |
| SQS | Buffered processing, batch operations |
| Step Functions | Multi-step workflows, orchestration |
| API Destination | Forward to external webhooks (Slack, Zapier, etc.) |
| CloudWatch Logs | Logging, debugging, audit trail |

## Common Use Cases

Practical examples of what you can build with custom EventBridge rules.

Alert on Bounces & Complaints

Route bounce and complaint events to SNS, then subscribe a Slack channel or PagerDuty endpoint. Get notified immediately when your sender reputation is at risk.

EventBridge → SNS → Slack / PagerDuty

Build Engagement Analytics

Capture open and click events in a Lambda function that writes to your own database. Build custom dashboards with open rates, click-through rates, and per-link analytics.

EventBridge → Lambda → Your Database

Trigger Workflows

Use Step Functions to orchestrate multi-step processes. For example: when a welcome email is delivered, wait 3 days, check if the recipient opened it, then send a follow-up.

EventBridge → Step Functions → SES

Forward to Your Webhook

Use an API Destination to forward events to any HTTP endpoint. Connect to Zapier, Make, n8n, or your own API without managing infrastructure.

EventBridge → API Destination → Your Endpoint

## Quotas & Limits

Key EventBridge limits to keep in mind when creating custom rules.

| Resource | Default Limit | Notes |
| --- | --- | --- |
| Rules per event bus | 300 | Adjustable up to 2,000 via AWS support |
| Targets per rule | 5 | Hard limit (use fan-out via SNS for more) |
| PutEvents rate | 10,000/sec | Varies by region; SES manages publishing |
| Event pattern size | 2,048 chars | Adjustable via AWS support |

## Next Steps

What Gets Deployed: Email

See every AWS resource Wraps creates, organized by preset.

[View Resources](https://wraps.dev/docs/infrastructure/email)

Email SDK Reference

Send emails with the TypeScript SDK after deploying.

[View SDK Docs](https://wraps.dev/docs/sdk-reference)

Configuration Presets

Compare presets and customize your configuration.

[View Guide](https://wraps.dev/docs/guides/configuration-presets)

### Need Help?

If you run into any issues, check our GitHub discussions or open an issue.

[Get Help](https://github.com/wraps-team/wraps/discussions)
