Exporting email reporting events to S3 gives your team a durable, queryable record of what happened after each message was submitted. Whether you send through an SMTP relay or a REST API, an S3 export pipeline turns provider-side delivery telemetry into data your engineering, support, security, and analytics teams can use.

What it means to export email reporting events to S3

An email provider can report far more than whether it accepted a send request. Depending on the provider and the recipient mailbox, the event stream may include message accepted, queued, sent, delivered, deferred, bounced, complained, opened, clicked, unsubscribed, suppressed, and failed events.

To export email reporting events to S3 is to copy those records from an email platform into an Amazon S3 bucket under your control. The export may be batch-based, such as an hourly or daily file, or near-real-time through webhooks that your infrastructure writes to S3. Either approach can work well; the right choice depends on how quickly the data must become available and how much operational complexity you can support.

S3 is a useful destination because it is inexpensive object storage, integrates with analytics services, and lets you retain raw source data independently of a provider’s dashboard retention window. You can query files with Amazon Athena, transform them with AWS Glue, load them into Snowflake or BigQuery, or use the exported records as evidence during a delivery investigation.

A mature design normally has three layers:

  1. Raw event storage: immutable files exactly as received, or as close to the provider payload as practical.
  2. Normalized analytics data: parsed events with a stable schema and standardized event names.
  3. Business-facing aggregates: tables and dashboards for delivery rate, bounce categories, complaint rate, engagement, and campaign or product performance.

Do not treat raw event exports as merely a reporting convenience. They are an operational log. A provider dashboard can tell you that a metric changed; your retained event data can help explain which recipients, providers, domains, applications, templates, or sending streams caused the change.

Decide which email events belong in the export

Before creating a bucket or webhook endpoint, define the event contract. Different sending providers use different event names and may expose different lifecycle points, so your downstream model should distinguish events that are transport facts from events that are estimates or user interactions.

Core delivery lifecycle events

For transactional email, capture the events that answer these questions:

  • Did your application submit the message to the provider?
  • Did the provider accept it for processing?
  • Did the provider attempt SMTP delivery?
  • Did the recipient mail server accept it?
  • Was it deferred for later retry?
  • Did it permanently fail, and why?
  • Did the recipient mark it as spam?
  • Was it blocked locally because the address was previously suppressed?

A practical canonical set looks like this:

Canonical eventMeaningImportant fields
acceptedProvider accepted an API or SMTP submission.message ID, submission time, sender, recipient
processedProvider validated or prepared the message.provider event ID, template/version, stream
deliveredReceiving server accepted the SMTP transaction.recipient domain, SMTP response, event time
deferredDelivery is temporarily delayed and may be retried.SMTP code, retry time, reason
bouncedDelivery failed permanently or was classified as final.bounce type, enhanced status code, response
complainedA recipient reported the message as spam.feedback-loop source, recipient where available
suppressedSending was blocked before delivery because of policy or prior history.suppression reason, source
openedTracking pixel was requested.user agent, IP-derived data if collected
clickedTracked link was followed.URL identifier, user agent, event time
unsubscribedRecipient opted out.list or preference scope, source

The labels used by a particular provider may differ. Keep both the original provider event type and your normalized event type. That preserves fidelity while allowing a consistent cross-provider query later.

Do not equate delivered with inbox placement

A delivered event usually means the recipient mail server accepted the SMTP transaction. It does not prove that a message landed in the primary inbox, avoided a spam folder, rendered correctly, or was read by a human.

That distinction matters when stakeholders ask why a delivered email did not produce an expected action. Store the delivery evidence separately from engagement data and from inbox-placement test results. Tools such as mail-tester.com can help assess a test message’s authentication and content signals, while MXToolbox can help inspect DNS records and mail routing, but neither turns a server-accepted event into proof of inbox placement.

Opens and clicks need special handling

Open events are inherently incomplete and can be distorted by image blocking, privacy protections, security scanners, and proxy fetches. Click events are usually more concrete, but security products may visit links automatically. Treat engagement exports as useful behavioral signals, not an infallible count of unique humans.

For privacy and data-minimization reasons, decide whether to retain raw IP address, user-agent, full URL, and recipient address. Often the better warehouse design stores a hashed recipient key, a normalized link identifier, and only the event attributes genuinely needed for reporting.

Choose an export architecture: batch, webhook, or polling

There are three common patterns for exporting events. Some teams use more than one: webhooks for fast operational response and scheduled exports for reconciliation.

Scheduled batch exports

A batch export writes events accumulated during a time window to S3, commonly as JSON Lines, CSV, or Parquet. An hourly schedule is usually enough for routine dashboards; daily files may be sufficient for archival workflows.

Batch exports are simple to operate because the provider owns collection, buffering, and file creation. They are especially attractive when you are exporting campaign metrics, contact attributes, or a reporting data set that does not need sub-minute freshness.

The trade-off is latency. If a sudden complaint spike begins at 10:05, a daily file will not help an on-call engineer react at 10:10. Also verify whether the export window is based on event occurrence time, message send time, provider processing time, or the time the export job runs.

Webhook ingestion to S3

With webhooks, the provider POSTs each event or small batch to an HTTPS endpoint you operate. Your endpoint validates the request, writes the unmodified body to durable storage or a queue, returns a successful response quickly, and processes the data asynchronously.

A resilient AWS implementation is:

Email provider webhooks
        |
        v
API Gateway or Application Load Balancer
        |
        v
Lambda / container receiver
        |
        +--> SQS queue or EventBridge
        |
        +--> raw S3 object storage
        |
        v
Glue, Lambda, Spark, or warehouse loader

The receiver should acknowledge only after the event is safely persisted. For many webhook providers, a 2xx response means “accepted,” while a 4xx response means a request problem and a 5xx response often triggers retry behavior. HTTP 202 Accepted is appropriate when the receiver has durably accepted an event for asynchronous processing but has not completed transformation. HTTP 429 Too Many Requests communicates rate limiting, though providers differ in whether and how they retry it.

Do not perform slow database lookups, complex analytics transformations, or synchronous warehouse inserts before responding. Those choices turn ordinary bursts into timeouts and retry storms.

API polling

Some providers expose an event or message-log API but do not support direct S3 export or webhooks for every event type. In that case, create a polling worker that fetches events using a cursor, updated_at watermark, or provider-issued page token.

Polling requires careful state management. Save the last confirmed cursor only after the retrieved page has been durably written. Overlapping retrieval windows are safer than gaps, provided your pipeline deduplicates events by a stable provider event ID or a deterministic composite key.

If your sending platform supports both REST API submission and SMTP relay, do not assume the reporting endpoint has the same identifiers as the send endpoint. Include your own application-generated message identifier or metadata at send time whenever the provider supports it. That creates a reliable join between your application record, the provider’s event record, and the eventual S3 data set. Review the provider’s email API setup guides before choosing metadata fields and event retrieval behavior.

Design a durable event schema

The biggest long-term export mistake is allowing each provider payload to become the permanent analytics schema. Provider payloads change, new event categories appear, optional fields disappear, and a future migration may introduce a second vocabulary.

Keep a raw copy, then normalize into a versioned envelope.

Recommended normalized event record

This JSON Lines example is intentionally provider-neutral:

{
  "schema_version": 1,
  "event_id": "evt_01JABC123XYZ",
  "event_type": "delivered",
  "event_time": "2026-10-18T14:32:16.483Z",
  "received_at": "2026-10-18T14:32:17.029Z",
  "provider": "example-provider",
  "provider_event_type": "message.delivered",
  "provider_message_id": "msg_8f5c31",
  "application_message_id": "order-receipt-98421",
  "tenant_id": "acct_42",
  "stream": "transactional",
  "from_domain": "mail.example.com",
  "recipient_domain": "gmail.com",
  "recipient_hash": "sha256:...",
  "smtp_code": 250,
  "enhanced_status_code": "2.0.0",
  "smtp_response": "250 2.0.0 OK",
  "metadata": {
    "template": "order-receipt",
    "order_id": "98421"
  },
  "raw_object_key": "raw/provider=example-provider/date=2026-10-18/hour=14/part-0001.jsonl.gz"
}

Separate event_time from received_at. The first is when the provider says the event occurred; the second is when your pipeline received it. Their difference is useful for detecting delayed webhooks, backfills, and provider outages.

Keep full recipient addresses out of broad analytics tables unless you have a defined operational use and access controls. A salted, consistently generated hash can support joins and duplicate detection without exposing the address to every analyst. Store the salt securely; if it changes without a migration plan, historical joins will break.

Preserve raw payloads

Normalized records are not a substitute for raw evidence. Store raw webhook bodies or batch files in a write-once-like prefix, such as:

s3://email-events-prod/raw/provider=provider-a/date=2026-10-18/hour=14/

Then write normalized records to a separate prefix:

s3://email-events-prod/normalized/schema_version=1/event_date=2026-10-18/event_type=delivered/

This separation lets you reprocess history when a mapping bug is discovered. For example, if a provider’s dropped event was incorrectly mapped to bounced, you can rebuild your normalized data without asking the provider to replay months of events.

Plan for schema evolution

Never silently repurpose a field. If bounce_classification changes from a string to an object, increment the schema version or write a backward-compatible new field. Consumers should tolerate unknown fields and nullable optional fields.

A good event contract includes a data dictionary defining every field, its type, whether it is nullable, its source, and its privacy classification. Treat it like an API contract. Analysts, data engineers, and deliverability staff should agree on definitions for terms such as delivered, hard bounce, soft bounce, complaint, and unique open.

Set up S3 securely with least privilege

Email event files can contain personal data, message metadata, behavioral data, IP-derived information, and potentially custom metadata your application attached to a send. Design the bucket as sensitive production data from the start.

Bucket layout and ownership

Use a dedicated bucket or a clearly isolated prefix for email event data. A dedicated bucket simplifies access review, lifecycle rules, encryption policy, logging, and incident response. Avoid a shared catch-all bucket where unrelated workloads can read email telemetry by accident.

Enable S3 Block Public Access. Event exports should not be public, and a bucket policy should not grant anonymous reads. Prefer an IAM role assumed by the export integration, rather than long-lived IAM user access keys whenever the provider supports role-based access or an external identity mechanism.

When static credentials are unavoidable, scope them to a single prefix, rotate them, store them in a secret manager, and use an integration-specific principal. Do not put administrator credentials in a provider configuration form.

Minimal write policy example

The exact permissions vary by integration. Some exporters only need s3:PutObject; others validate the bucket and need s3:GetBucketLocation or list access. This example grants a dedicated IAM role permission to write objects only under exports/provider-a/ and read the bucket location:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AllowBucketLocation",
      "Effect": "Allow",
      "Action": "s3:GetBucketLocation",
      "Resource": "arn:aws:s3:::email-events-prod"
    },
    {
      "Sid": "AllowWriteOnlyToExportPrefix",
      "Effect": "Allow",
      "Action": [
        "s3:PutObject"
      ],
      "Resource": "arn:aws:s3:::email-events-prod/exports/provider-a/*"
    }
  ]
}

Notice the difference between the bucket ARN and object ARN. Bucket-level permissions use arn:aws:s3:::email-events-prod; object actions such as s3:PutObject apply to arn:aws:s3:::email-events-prod/prefix/*. If an exporter needs s3:ListBucket, grant it only on the bucket ARN and, where possible, constrain it to the intended prefix.

Do not add s3:DeleteObject to a write-only exporter unless the vendor documentation explicitly requires it. An export integration should not be able to erase historical records.

Encryption and transport controls

Amazon S3 encrypts new uploads at rest by default, but many organizations require a specific encryption control. SSE-S3 uses S3-managed keys; SSE-KMS uses an AWS KMS key and can support more detailed key-policy controls and audit requirements.

If you enforce SSE-KMS in a bucket policy, confirm that your provider can send the required encryption headers and has permission to use the KMS key. Otherwise, a correctly authenticated upload may fail with AccessDenied. Test with a non-production prefix before enforcing a deny policy across the whole bucket.

Also enforce TLS for bucket access. A common bucket-policy pattern denies requests when aws:SecureTransport is false. Review any explicit deny carefully: explicit denies override allows and can lock out legitimate integrations when conditions are wrong.

Partition files for inexpensive and fast queries

S3 has prefixes rather than traditional directories, but partition-like prefixes make a major difference to query cost and data management. Athena, Glue, Spark, and other tools can avoid reading unrelated data when your data is arranged by fields used in filters.

A sensible starting layout is:

s3://email-events-prod/normalized/
  schema_version=1/
  provider=provider-a/
  event_date=2026-10-18/
  hour=14/
  event_type=delivered/
  part-00001.jsonl.gz

Use partitions that match frequent queries. Date is almost always worthwhile. Provider and event type can also be useful, but do not over-partition into millions of tiny directories. Tenant ID, individual sender, or recipient domain may be too high-cardinality for a primary storage layout; keep them as columns unless query patterns clearly justify a partition.

JSON Lines first, Parquet for analytics

JSON Lines (.jsonl) is excellent for raw ingestion because each line is an independent record, failures are easier to isolate, and appending batches is straightforward. Compress the files with gzip if your tooling supports it.

For recurring analytical queries, convert normalized data into Apache Parquet. Parquet is columnar, so engines can read only the columns needed for a query. That typically reduces scanning compared with JSON or CSV, especially when your event object contains a large metadata field that a dashboard does not need.

A practical pattern is:

  • Raw: compressed JSON Lines, retained as received.
  • Bronze/normalized: validated JSON Lines or Parquet with minimal transformation.
  • Curated: partitioned Parquet tables with explicit column types and deduplication.

Avoid extremely small files. Thousands of tiny objects increase metadata overhead and can slow distributed query engines. Aim to compact data periodically; the ideal file size depends on your query engine and volume, but files in the tens to hundreds of megabytes are generally more efficient than kilobyte-sized event objects.

Make completion explicit

A batch job may produce multiple data files. Downstream jobs should not assume a partition is complete merely because one object exists. Write a manifest or completion marker only after all objects for a batch have landed and passed validation.

For example:

.../event_date=2026-10-18/hour=14/part-00001.parquet
.../event_date=2026-10-18/hour=14/part-00002.parquet
.../event_date=2026-10-18/hour=14/_SUCCESS

The marker is a convention, not an S3 feature. Its value is operational: transformation jobs can process only completed partitions and avoid partial hourly results.

Handle duplicates, retries, ordering, and backfills

Email event delivery is an at-least-once problem. Webhook providers may retry after a timeout. Polling jobs may repeat an overlap window. A batch export may be rerun. Event ordering can differ from real-world order, particularly when delivery attempts, deferrals, complaints, and engagement records flow through separate systems.

Deduplicate by stable event identity

The preferred key is a provider-supplied immutable event ID. If none exists, construct a deterministic key from stable fields such as provider message ID, normalized event type, event timestamp, recipient hash, and SMTP response identifier. Be cautious: two legitimate delivery attempts can share many attributes, so a synthetic key can accidentally collapse real data.

Maintain two related concepts:

  • Raw event count: every event instance received, useful for pipeline auditing.
  • Canonical event count: deduplicated events, useful for business metrics.

Do not discard duplicates before recording that they occurred. A sudden duplicate rate may indicate a webhook timeout, an unhealthy receiver, or a provider replay.

Retry correctly

For outbound API sends, classify failures before retrying. HTTP 400 Bad Request usually indicates a malformed request and should be fixed, not blindly retried. HTTP 401 Unauthorized or 403 Forbidden usually signals credential or authorization problems. HTTP 429 Too Many Requests means the sender should slow down, ideally respecting Retry-After if supplied. HTTP 500, 502, 503, and 504 can be transient and generally warrant bounded exponential backoff with jitter.

SMTP has a similar distinction. A 250 response indicates the remote server accepted the command or message. 4xx replies are temporary failures, while 5xx replies are permanent at that point in the transaction. Enhanced mail status codes add useful detail: 5.1.1 commonly indicates an invalid or unknown recipient address, 4.2.2 indicates a mailbox-related temporary condition such as over quota, and 5.7.1 commonly indicates a policy or permission rejection. Store both the basic SMTP code and full enhanced code when available.

Never infer a hard bounce only from the numeric first digit if your provider supplies a normalized classification. Providers may apply mailbox-specific rules, retry policies, and post-processing. Preserve their raw response, then map to your own suppression policy with documented exceptions.

Use watermarks and reprocessing windows

For polling, retrieve from a slightly earlier watermark than the last successful run, such as five or fifteen minutes earlier, and deduplicate afterward. This protects against late-arriving records and clock differences.

For batch exports, retain the ability to backfill a historical date range. A provider may correct or enrich records after the original export, or your own parser may change. Your pipeline should allow a controlled replay for a date range without mixing new and old output unpredictably.

Connect event data to deliverability operations

An S3 export becomes much more valuable when it informs decisions, not just dashboards. The key is to aggregate by dimensions that reflect how mailbox providers and your own systems behave.

Metrics worth monitoring

At a minimum, calculate metrics by day, sender domain, sending stream, recipient domain, template, and application:

  • acceptance rate;
  • delivery rate;
  • deferred-message rate and age;
  • hard-bounce rate;
  • soft-bounce rate;
  • complaint rate;
  • suppression rate;
  • unsubscribe rate;
  • open and click rate, with caveats;
  • median and p95 time from accepted to delivered.

A single global delivery rate can hide a serious problem. A 99% global rate is not reassuring if one important recipient domain has a 15% temporary-deferral rate or one template is causing complaints.

Build an actionable bounce taxonomy

Create a classification table that groups provider labels and SMTP evidence into categories such as invalid recipient, mailbox full, recipient server unavailable, content or policy rejection, authentication issue, reputation-related block, and unknown.

Then connect classifications to actions:

CategoryTypical action
Invalid recipientSuppress promptly; investigate acquisition quality.
Mailbox full or temporary server issueRetry through provider policy; avoid permanent suppression too early.
Authentication or alignment issueVerify SPF, DKIM, DMARC, and return-path configuration.
Content or policy rejectionReview content, links, sending pattern, authentication, and reputation.
ComplaintSuppress recipient and investigate source, consent, and targeting.

Your event export should include the envelope sender and visible From domain where possible. That makes it easier to associate failures with a particular authenticated sending identity.

DNS records that support interpretable delivery data

S3 export itself requires no DNS records. However, DNS authentication determines whether many delivery events are healthy or problematic, so your event analysis should be able to reference your sending domain configuration.

A real SPF TXT record might look like this:

mail.example.com. 3600 IN TXT "v=spf1 include:spf.emailprovider.example -all"

A DMARC record at the organizational domain might look like:

_dmarc.example.com. 3600 IN TXT "v=DMARC1; p=quarantine; rua=mailto:dmarc-reports@example.com; adkim=s; aspf=s; pct=100"

DKIM records are provider-specific because selectors and public keys differ. A typical TXT record name is shaped like this:

s1._domainkey.mail.example.com. 3600 IN TXT "v=DKIM1; k=rsa; p=BASE64_PUBLIC_KEY"

Do not copy the placeholder key or provider hostname literally. Use the exact record values generated by your sending provider, and verify them with your DNS host, dig, MXToolbox, or the provider’s verification flow. Also remember that SPF has a DNS lookup limit; adding includes casually can make a record fail evaluation.

Privacy, retention, and compliance controls

Email event data can be personal data even if you never store message bodies. An address, hashed address that can be reidentified, IP address, user-agent, link path, message metadata, and account identifier may all fall within a privacy program’s scope.

Start with a written retention decision. For example, your business may require detailed operational event data for 90 days, aggregated delivery data for two years, and immutable audit evidence for a regulated period. The exact values should be set by your legal, security, and business requirements rather than copied from another company.

S3 Lifecycle rules can transition objects to lower-cost classes or expire them automatically. Apply rules by prefix or object tag so raw sensitive files and curated aggregates can have different schedules. Be mindful that lifecycle actions are asynchronous; a configured date is not a promise that the object changes state at a precise moment.

Use least-privilege roles for analysts, data engineers, support staff, and export writers. A support tool may need recipient-level data for a short operational period, while an executive dashboard should need only aggregated counts. Log access to the bucket and set alerts for unusual data reads or policy changes.

If you need deletion workflows, design them upfront. Deleting or masking one recipient from Parquet files can require rewriting partitions. A practical strategy is to keep direct identifiers in a narrowly controlled lookup table and store mostly pseudonymous event facts in the broad analytics data set.

Validate the export before trusting reports

Treat the first production export as an integration test, not a finished reporting system. Send controlled messages to mailboxes you operate, then reconcile what your application sent, what the provider accepted, what your S3 files contain, and what the recipient server reported.

A practical validation checklist

  1. Send one message through the REST API and one through SMTP, with distinct application message IDs.
  2. Confirm that each submission receives its expected synchronous response, such as HTTP 202 or SMTP 250, according to your provider’s behavior.
  3. Verify that the resulting reporting events contain the provider message ID and your application identifier or metadata.
  4. Trigger a controlled failure using an intentionally invalid test address only where appropriate and permitted; verify the exported bounce classification and SMTP evidence.
  5. Confirm that duplicate delivery of a webhook does not inflate canonical metrics.
  6. Confirm that a temporary receiver outage produces retries, dead-letter handling, or alerting rather than silent loss.
  7. Query one date partition and compare counts against the provider’s reporting interface, allowing for documented differences in timing and metric definitions.
  8. Test access using the export role and a non-export role to confirm least privilege is actually enforced.

For a basic Athena-style quality check, compare raw and canonical counts:

SELECT
  event_type,
  COUNT(*) AS raw_events,
  COUNT(DISTINCT event_id) AS distinct_events
FROM email_events_normalized
WHERE event_date = DATE '2026-10-18'
GROUP BY event_type
ORDER BY raw_events DESC;

If raw_events greatly exceeds distinct_events, inspect webhook retries, replay jobs, and producer timeouts. If distinct_events is unexpectedly low, inspect parser failures and ID extraction rules.

Common failure modes and how to troubleshoot them

S3 AccessDenied errors

Most S3 export failures come from a mismatch among IAM policy, bucket policy, KMS key policy, bucket region, or required encryption headers. Check whether the integration needs a bucket-level permission such as s3:GetBucketLocation in addition to object-level s3:PutObject.

Also check the target ARN. A policy granting s3:PutObject on arn:aws:s3:::email-events-prod will not authorize object writes; the resource must include the object path, such as arn:aws:s3:::email-events-prod/exports/provider-a/*.

Missing events

First determine whether the issue is at the source, in transport, or in processing. Compare provider-visible event counts with raw S3 object counts. Then compare raw records with normalized records. This three-way reconciliation reveals whether the provider did not generate an event, the export did not deliver it, or your transformation dropped it.

Check timestamp semantics as well. A dashboard filtered by send time can disagree with an S3 query filtered by event time, especially for messages that deferred overnight or received an open event days later.

Incorrect bounce rates

Do not combine all failures into one bounce metric. Suppressed messages may never be attempted. Deferrals are not final bounces. Policy rejects may require remediation but should not always be treated as invalid addresses. Build separate measures, document them, and ensure the dashboard labels match their calculation.

Unexpected duplicate events

Webhook retry behavior is normal. A provider might retry because your endpoint returned a timeout even though your code wrote the record just before timing out. That is why event ID deduplication and fast acknowledgement matter.

Never rely on exactly-once webhook delivery. Build the consumer so that processing the same event twice has the same final result as processing it once.

Conclusion: make S3 the source of evidence, not another silo

A reliable email event export does more than preserve dashboard metrics. It gives your organization an independent source of truth for message lifecycle analysis, recipient hygiene, deliverability investigations, compliance retention, and cross-system reporting.

Start with a small but durable implementation: raw JSON Lines in a private encrypted bucket, a normalized schema with provider and application identifiers, daily partitions, stable event-ID deduplication, and a clear retention policy. Add Parquet, Glue Catalog tables, dashboards, and streaming alerts after the basics are trustworthy.

The most important design principle is simple: retain raw facts, preserve timing and identifiers, assume retries and duplicates, and avoid drawing stronger conclusions from an event than the event can support. “Delivered” is a receiving-server acceptance signal; “opened” is a tracking signal; a clean S3 pipeline lets you keep those distinctions intact.

FAQ

What is the best file format for email events in S3?

Use compressed JSON Lines for raw ingestion because it preserves event-shaped data and is easy to replay. Convert validated data to partitioned Parquet for frequent analytical queries and lower scan costs.

Should I use webhooks or scheduled exports for email reporting events?

Use webhooks when operations need low-latency signals, such as complaint alerts or delivery troubleshooting. Use scheduled exports for simpler reporting and archival. Many teams use webhooks for fast ingestion plus periodic batch reconciliation.

Does exporting email events to S3 require DNS changes?

No. S3 exports do not require email DNS records. SPF, DKIM, and DMARC records are still essential for authenticated email sending and for interpreting delivery failures, but they are separate from the S3 integration.

Which SMTP status codes should be stored in event data?

Store the basic SMTP reply code, the enhanced status code when present, and the full provider or receiving-server response. Examples include 250 for acceptance, 421 or 450 for temporary conditions, 550 5.1.1 for an invalid recipient, and 550 5.7.1 for many policy-related rejections.

How long should email event data be retained?

Retention depends on your legal, privacy, security, support, and analytics requirements. Keep detailed raw data only as long as necessary, retain aggregated metrics longer where appropriate, and enforce the policy through S3 Lifecycle rules and access controls.