Amazon S3 is a strong place to store uploads, exports, invoices, reports, and generated files—but storage alone does not notify people. This guide shows how to send email from Amazon S3 when a new object is created, using S3 Event Notifications, Amazon EventBridge, AWS Lambda, and the Volanea REST API.
There is no native Volanea app, Amazon S3 marketplace listing, or one-click S3 plugin to install. The reliable integration is an event-driven AWS workflow: S3 emits an object event, EventBridge filters and routes it, Lambda formats the notification, and Volanea sends the message.
What actually triggers an email in Amazon S3
The trigger is an Amazon S3 event with the EventBridge detail type Object Created. S3 emits it when an object is created in an EventBridge-enabled bucket, including common upload and write operations such as PutObject, multipart-upload completion, and copy operations.
That distinction matters. The trigger is not “a record created,” “a form submitted,” or a generic webhook action. Amazon S3 is object storage, so the relevant unit is an object: a file stored under a bucket and object key.
For example, a customer upload might create this object:
- Bucket:
customer-uploads-prod - Object key:
incoming/acme/2026-09-29/invoice-1042.pdf - Event type:
Object Created - Event reason:
PutObject
Your EventBridge rule can match every object creation in the bucket, or only events for a specific bucket. For a production alert workflow, filter as early as possible. A bucket that receives thumbnails, temporary files, logs, and customer documents should not send an email for every write unless that is truly the intended behavior.
The recommended architecture: S3 to EventBridge to Lambda to Volanea
Amazon S3 does not offer a direct arbitrary-webhook destination in its standard event-notification configuration. Its native destinations include AWS services such as Lambda, SNS, SQS, and EventBridge. EventBridge can invoke public HTTPS APIs through API Destinations, but the Lambda route is the better production design when you need to validate fields, format useful email content, generate safe idempotency keys, and control retry behavior.
Use this flow:
- An object is uploaded or otherwise created in an S3 bucket.
- S3 delivers an
Object Createdevent to Amazon EventBridge. - An EventBridge rule matches the bucket and event type.
- The rule invokes a Lambda function.
- Lambda maps the S3 event into a Volanea
POST /v1/sendrequest. - Volanea queues and dispatches the transactional email.
This architecture keeps storage events separate from email delivery. S3 remains responsible for object storage, EventBridge handles routing, Lambda handles application logic, and Volanea handles the sending pipeline.
It also gives you an important safety boundary: the Lambda function can reject unexpected events before an email is created. That is harder to accomplish with a purely declarative event-to-HTTP transformation.
Enable Amazon EventBridge delivery for the S3 bucket
First, enable EventBridge notifications on the bucket that should produce email alerts. S3 sends supported bucket events to the account’s default EventBridge event bus after this setting is enabled.
You can enable the setting in the S3 console under the bucket’s event-notification configuration, or configure it programmatically. The AWS CLI form is concise:
aws s3api put-bucket-notification-configuration \
--bucket customer-uploads-prod \
--notification-configuration '{
"EventBridgeConfiguration": {}
}'
Enabling EventBridge is bucket-wide. Once enabled, S3 sends its supported events to EventBridge, where rules determine which events are acted on. Plan the EventBridge rule carefully rather than assuming every emitted S3 event should result in an email.
A focused EventBridge event pattern for one bucket looks like this:
{
"source": ["aws.s3"],
"detail-type": ["Object Created"],
"detail": {
"bucket": {
"name": ["customer-uploads-prod"]
}
}
}
This is the concrete trigger configuration for the integration: an S3 Object Created event whose detail.bucket.name is customer-uploads-prod.
You can create separate rules for separate workflows. For example, one rule could notify operations when a nightly export appears under exports/, while another invokes processing code for files under incoming/. Keep notification rules narrow; otherwise a high-volume bucket can become an accidental high-volume email sender.
The Amazon S3 EventBridge payload you receive
EventBridge receives an event envelope with routing fields at the top level and S3-specific data inside detail. The object name, bucket name, size, ETag, version ID when applicable, request ID, source IP, and event reason are available in the S3 detail object.
A representative Object Created event has this shape:
{
"version": "0",
"id": "2f2d0c72-2caf-4da4-a893-819a42d2ef15",
"detail-type": "Object Created",
"source": "aws.s3",
"account": "123456789012",
"time": "2026-09-29T18:42:11Z",
"region": "us-east-1",
"resources": [
"arn:aws:s3:::customer-uploads-prod"
],
"detail": {
"version": "0",
"bucket": {
"name": "customer-uploads-prod"
},
"object": {
"key": "incoming/acme/invoice-1042.pdf",
"size": 483921,
"etag": "d41d8cd98f00b204e9800998ecf8427e",
"version-id": "3HL4kqtJlcpXrof3fjVBH40Nrjfkd",
"sequencer": "0066512F9D0D6A9A5A"
},
"request-id": "7F3C6A1C68A19C42",
"requester": "AIDAEXAMPLEUSER",
"source-ip-address": "203.0.113.10",
"reason": "PutObject"
}
}
The important mapping is straightforward:
| S3 EventBridge field | Email use |
|---|---|
detail.bucket.name | Identify the bucket in the subject and message body |
detail.object.key | Show the uploaded file’s object key |
detail.object.size | Show the file size, if present |
detail.object.etag | Provide a content identifier for operations teams |
detail.object.version-id | Identify the exact object version, if bucket versioning is enabled |
detail.object.sequencer | Help reason about ordering for the same object key |
detail.reason | Explain whether the event came from PutObject, copy, or another create operation |
top-level id | Use as the Volanea idempotency key for EventBridge delivery retries |
Do not build a production email around every field being present. Some properties are conditional: for example, version-id depends on versioning and the event context, and object metadata can vary by event type. Treat bucket name, object key, and top-level event ID as the essential fields for this workflow; enrich the message only when optional fields exist.
Send the event with AWS Lambda and the Volanea REST API
The Lambda function below receives the EventBridge event, validates that the expected fields exist, maps the payload into an email, and calls Volanea’s POST /v1/send endpoint.
It uses:
fromfor the authenticated sender address.tofor the internal recipient who should receive the alert.subjectfor a concise operational summary.textfor a portable plain-text message.Idempotency-Keyto protect against duplicate sends when EventBridge retries the same delivery.
Install @aws-sdk/client-secrets-manager with your Lambda deployment package or bundle it with your server-side application code. The secret used here contains JSON in the form { "apiKey": "sk_..." }.
// index.mjs
import {
GetSecretValueCommand,
SecretsManagerClient
} from "@aws-sdk/client-secrets-manager";
const secrets = new SecretsManagerClient({});
let cachedApiKey;
async function getVolaneaApiKey() {
if (cachedApiKey) return cachedApiKey;
const response = await secrets.send(
new GetSecretValueCommand({
SecretId: process.env.VOLANEA_SECRET_ID
})
);
const secret = JSON.parse(response.SecretString);
if (!secret.apiKey || !secret.apiKey.startsWith("sk_")) {
throw new Error("VOLANEA_SECRET_ID does not contain a Volanea secret key");
}
cachedApiKey = secret.apiKey;
return cachedApiKey;
}
function decodeS3Key(key) {
// S3 event object keys can be URL-encoded. Convert + back to spaces first.
return decodeURIComponent(key.replace(/\+/g, " "));
}
export const handler = async (event) => {
const bucket = event?.detail?.bucket?.name;
const rawKey = event?.detail?.object?.key;
const eventId = event?.id;
if (!bucket || !rawKey || !eventId) {
console.error("Unexpected S3 EventBridge payload", JSON.stringify(event));
throw new Error("Missing bucket name, object key, or EventBridge event ID");
}
const key = decodeS3Key(rawKey);
const object = event.detail.object ?? {};
const apiKey = await getVolaneaApiKey();
const lines = [
"A new Amazon S3 object was created.",
"",
`Bucket: ${bucket}`,
`Object key: ${key}`,
`Reason: ${event.detail.reason ?? "unknown"}`,
`Size: ${object.size ?? "not supplied"} bytes`,
`ETag: ${object.etag ?? "not supplied"}`,
`Version ID: ${object["version-id"] ?? "not supplied"}`,
`Sequencer: ${object.sequencer ?? "not supplied"}`,
`AWS Region: ${event.region ?? "not supplied"}`,
`Event time: ${event.time ?? "not supplied"}`,
`Event ID: ${eventId}`
];
const emailPayload = {
from: process.env.FROM_EMAIL,
to: process.env.ALERT_RECIPIENT,
subject: `New S3 object: ${key}`,
text: lines.join("\n")
};
const response = await fetch("https://api.volanea.com/v1/send", {
method: "POST",
headers: {
"Authorization": `Bearer ${apiKey}`,
"Content-Type": "application/json",
"Idempotency-Key": `s3-eventbridge-${eventId}`
},
body: JSON.stringify(emailPayload)
});
const responseBody = await response.text();
if (!response.ok) {
console.error("Volanea send failed", {
status: response.status,
responseBody,
eventId
});
throw new Error(`Volanea returned HTTP ${response.status}`);
}
console.log("Volanea email accepted", {
bucket,
key,
eventId,
responseBody
});
return {
statusCode: response.status,
body: responseBody
};
};
The code performs the field mapping explicitly rather than forwarding the raw AWS event to the email API. That gives recipients a readable message and prevents AWS-specific fields from becoming accidental email fields.
The Volanea request is the essential send operation:
POST /v1/send HTTP/1.1
Host: api.volanea.com
Authorization: Bearer sk_...
Content-Type: application/json
Idempotency-Key: s3-eventbridge-2f2d0c72-2caf-4da4-a893-819a42d2ef15
{
"from": "storage-alerts@example.com",
"to": "ops@example.com",
"subject": "New S3 object: incoming/acme/invoice-1042.pdf",
"text": "A new Amazon S3 object was created..."
}
For the complete endpoint contract, available send options, and current API examples, use the email API reference and setup guides.
Configure authentication without exposing the Volanea API key
The Volanea API key belongs in AWS server-side secret storage, not in an S3 object, a browser app, an uploaded configuration file, frontend JavaScript, or a public repository.
In this architecture, the key lives in AWS Secrets Manager. Lambda receives only the secret identifier through the non-sensitive VOLANEA_SECRET_ID environment variable, then retrieves the actual sk_... key at runtime. Grant the Lambda execution role permission to read that one secret with secretsmanager:GetSecretValue.
A practical configuration has these environment variables:
VOLANEA_SECRET_ID=prod/volanea/s3-alerts
FROM_EMAIL=storage-alerts@example.com
ALERT_RECIPIENT=ops@example.com
The secret value should contain the API key, for example:
{
"apiKey": "sk_live_replace_with_your_secret_key"
}
Do not put the actual key in FROM_EMAIL, ALERT_RECIPIENT, an EventBridge input transformer, a CloudFormation parameter committed to source control, or Lambda logs. The Volanea secret key authorizes API requests, so anyone who obtains it could send mail through the project.
The sender address also needs to be valid for your Volanea project and authenticated sending domain. Use an operational mailbox or dedicated alert sender such as storage-alerts@example.com, rather than an address that users might reply to without monitoring.
Create the EventBridge rule and Lambda target
After deploying the Lambda function, create an EventBridge rule on the default event bus using the Object Created event pattern shown earlier. Set the Lambda function as the target.
The Lambda function should have a resource-based permission allowing EventBridge to invoke it. AWS tooling usually adds this when you configure the rule target through the console, SAM, CDK, or CloudFormation, but verify it when deploying through custom automation.
Before turning on production notifications, test the whole path with a deliberately named object. Upload a file such as:
incoming/test/s3-volanea-integration-check.txt
Then verify four separate things:
- The S3 bucket has EventBridge notifications enabled.
- The EventBridge rule matched the event and invoked Lambda.
- Lambda logs show a successful Volanea API response without exposing the API key.
- The recipient received one email with the expected bucket and object key.
Testing each hop separately makes troubleshooting much faster. An absent email does not automatically mean Volanea failed; the event may not have been emitted, the EventBridge rule may not have matched, Lambda may lack secret access, or the outbound API request may have been rejected.
Design the email for operators, not for raw event dumps
The default code sends a plain-text alert because plain text works everywhere and is ideal for operational messages. You can switch to HTML when you need a more structured internal notification, but avoid turning an alert email into a raw JSON archive.
A useful object-created alert normally answers these questions immediately:
- What happened?
- Which bucket received the object?
- What is the object key?
- Which AWS Region and event time are associated with it?
- Was the object created by
PutObject, copy, or another reason? - Can the recipient correlate it with a specific EventBridge event ID?
For customer-facing messages, do not email the object key blindly. Object keys can contain customer names, internal IDs, email addresses, or other sensitive data. Instead, map a safe business identifier from a known prefix or fetch approved metadata from a trusted database before generating the message.
Likewise, do not include a direct S3 URL unless you have deliberately designed access control around it. Most S3 objects are private, and a URL that works for one internal operator may fail for another. If a human needs file access, generate a short-lived presigned URL in Lambda only after confirming the recipient and access policy are appropriate.
When a direct EventBridge API Destination is enough
Amazon EventBridge API Destinations can invoke HTTPS endpoints and can use a Connection with API key authentication. That makes a direct EventBridge-to-Volanea request possible for a very simple, fixed-recipient alert. EventBridge input transformers can reshape an S3 event into a JSON request body.
However, direct delivery is best reserved for narrowly scoped notifications where the payload is simple and the operational consequences of retries are fully understood. A Lambda layer is usually preferable for this use case because it can:
- Validate bucket names, key prefixes, and required fields.
- Decode and sanitize object keys.
- Branch recipients based on a prefix, tenant, or object type.
- Exclude sensitive paths from email content.
- Fetch data from another system before composing the message.
- Set Volanea’s
Idempotency-Keyfrom the EventBridge event ID. - Add application-level duplicate protection beyond one delivery attempt.
EventBridge Connections store API authorization information in AWS Secrets Manager when you use an API Destination. That is a valid secure option for a simple direct route. But once you need robust deduplication or conditional business logic, use Lambda rather than trying to force all logic into an input template.
When this breaks: retries, duplicate sends, timeouts, and missing fields
Event-driven email integrations fail in predictable ways. Design for them before an upload surge, incident, or credential rotation exposes the weak points.
EventBridge retries can create duplicate send attempts
Amazon S3 event notifications are designed for at-least-once delivery, and event ordering is not guaranteed. EventBridge also retries failed target deliveries. If Lambda calls Volanea successfully but fails before EventBridge receives a successful invocation result, the same event can be processed again.
The first defense is the code’s idempotency header:
Idempotency-Key: s3-eventbridge-<event.id>
Volanea uses the idempotency key to recognize a retry of the same send request, helping prevent the same EventBridge event from becoming multiple emails. Keep the key stable for retries of one event; do not generate a random UUID inside the Lambda handler.
A second defense is business-level deduplication. S3 can produce duplicate notifications, and duplicate notifications can be represented by more than one event envelope. If one email per unique object version is a hard requirement, store a deduplication record keyed by a stable combination such as bucket name, object key, version ID, and sequencer in DynamoDB before sending.
A webhook-style timeout is not a reason to ignore retries
EventBridge API Destinations have a maximum client execution timeout of five seconds. That is one reason not to place slow work on an HTTP target path. Even with Lambda, the key rule remains: return success only after the Volanea request has been accepted, and throw an error for retryable failures.
The example checks response.ok. A Volanea 2xx response allows Lambda to finish successfully. A non-2xx result throws an error, allowing EventBridge’s target retry policy to handle the failed invocation. Configure a dead-letter queue for the EventBridge target so exhausted failures are preserved for investigation instead of silently disappearing.
Do not retry every 4xx response forever. A malformed sender address, a revoked key, or an unauthorized sender generally requires configuration repair, not a hundred repeat requests. Use logs and a DLQ to distinguish configuration faults from transient network or service errors.
Payload fields can be absent or differ by event context
Amazon S3 does not provide a separate “plan” level that changes the standard S3 EventBridge payload into a richer or poorer edition. The real source of missing fields is event context: object version IDs depend on versioning, optional object attributes may not be relevant to every event, and event schemas can gain backward-compatible fields over time.
Write defensive mappings. In the example, size, etag, version-id, and sequencer have fallbacks, while bucket, object.key, and top-level id are treated as required for this specific notification workflow.
If a missing field is business-critical, do not infer it from a blank email. Use the bucket and object key to call HeadObject from Lambda, with a tightly scoped IAM policy, then retrieve the metadata you need. This is especially useful when an email needs object tags, user-defined metadata, content type, or a definitive current version.
Object keys can be encoded or unsafe for email content
S3 event object keys can contain spaces and special characters. Decode them carefully before displaying them, as the example does with decodeURIComponent after converting plus signs to spaces.
But decoding is not the same as trusting. An object key is external input. Escape it before including it in HTML, avoid placing it in headers other than the subject after applying length limits, and never use it as a shell command, SQL fragment, or authorization decision without validation.
High object volume can become high email volume
An object-created trigger may be far noisier than expected. Multipart workflows, generated previews, copies, and automated rewrites can all create objects. If your integration emails an individual person for every one, the workflow can produce alert fatigue and damage the sending reputation of an otherwise healthy mail stream.
For high-volume workloads, choose one of these patterns instead:
- Filter EventBridge rules to a specific bucket or application prefix.
- Send only for selected file extensions, enforced in Lambda.
- Aggregate events in SQS or DynamoDB and send a periodic digest.
- Notify a shared operational mailbox rather than customers.
- Use a stored Volanea template with controlled variables for consistent formatting.
If object activity is expected to grow, review transactional email pricing alongside the AWS costs for EventBridge, Lambda, Secrets Manager, and any queue or database used for deduplication.
Operational checklist before production
Use this checklist before relying on S3-created emails for customer workflows or incident response:
- Confirm the S3 bucket has EventBridge notifications enabled.
- Match only
source: aws.s3anddetail-type: Object Createdevents for the intended bucket. - Restrict the rule further in Lambda by prefix, extension, account, or environment where needed.
- Authenticate the Volanea sending domain and use a valid
FROM_EMAILaddress. - Store the Volanea
sk_...key in AWS Secrets Manager. - Grant Lambda only
secretsmanager:GetSecretValuefor the required secret. - Use the EventBridge event ID as a stable Volanea idempotency key.
- Configure EventBridge target retries deliberately and add a dead-letter queue.
- Log event IDs and Volanea responses, but never log API keys or full secret values.
- Test with a non-production recipient before enabling a broad bucket rule.
The durable lesson is that email is a side effect, not merely an HTTP call. A correct integration must account for an upload being repeated, an event arriving late, a network response being uncertain, and recipients receiving alerts they can act on.
Conclusion
To send email from Amazon S3 with Volanea, use S3’s real object-storage trigger: the Object Created event. Enable EventBridge delivery for the bucket, match the event with an EventBridge rule, invoke Lambda, and call POST /v1/send with a secure Volanea secret key.
The Lambda layer is small, but it provides the safeguards that matter in production: field validation, readable message construction, secret retrieval, stable idempotency keys, and controlled error handling. Start with a single bucket and a single internal recipient, validate the full event path, then add routing, templates, presigned links, or aggregation only when the workflow needs them.
FAQ
Can Amazon S3 send an HTTP webhook directly?
Not as a native S3 event-notification destination. S3 can send notifications to AWS services including EventBridge, Lambda, SNS, and SQS. EventBridge can invoke HTTPS API Destinations, but Lambda is the recommended layer when you need validation, safe retries, or dynamic email logic.
What Amazon S3 action sends the email?
The integration reacts to the S3 EventBridge detail type Object Created. It occurs when an object is created, such as after a PutObject, completed multipart upload, or supported copy operation.
Where should the Volanea API key be stored?
Store the Volanea sk_... secret key in AWS Secrets Manager. Let the Lambda execution role retrieve it at runtime. Never expose it in browser code, uploaded files, public configuration, or source control.
How do I prevent duplicate S3 email alerts?
Use the EventBridge top-level event id as the Volanea Idempotency-Key for the send request. For stricter deduplication across duplicate S3 notifications, persist a bucket-plus-key-plus-version or sequencer record in DynamoDB before sending.
Can I send an email with a download link to the new S3 object?
Yes, but do not expose a permanent public object URL by default. Generate a short-lived presigned URL in Lambda only when the recipient is authorized to access the object, then include that link in the Volanea message.