A Flask webhook application for receiving and inspecting inbound emails from Resend. Includes a built-in web interface to view and debug incoming emails in real-time. Ready to deploy to DigitalOcean App Platform with one click.
- π Email Inspector - Web interface to view and debug incoming emails in real-time
- π§ Webhook Receiver - Processes
email.receivedevents from Resend - π Email Dashboard - View sender, recipients, subject, attachments at a glance
- π JSON API - Programmatic access to recent emails via
/emails/json - πΎ In-Memory Storage - Keeps last 20 emails for quick debugging (resets on restart)
- π One-Click Deploy - Deploy to DigitalOcean App Platform instantly
- π Detailed Logging - All webhook events logged for debugging
- β€οΈ Health Monitoring - Built-in health check endpoint
Before deploying, you'll need:
- Resend Account - Sign up at resend.com
- Inbound Email Address - Set up in Resend:
- Go to Resend Dashboard
- Navigate to Emails β Receiving
- Follow the instructions to get your inbound email address (e.g.,
anything@yourdomain.resend.app) - Any email sent to this address will trigger your webhook
- DigitalOcean Account - Sign up at digitalocean.com (optional for local testing)
Deploy this app to DigitalOcean App Platform with one click:
After deployment:
- Get your app URL from DigitalOcean (e.g.,
https://resend-webhook-xxxxx.ondigitalocean.app) - Configure webhook in Resend:
- Go to Resend Dashboard β Webhooks
- Click Add Webhook
- Webhook URL:
https://your-app-url.ondigitalocean.app/webhook - Select event:
email.received - Click Add
- Test it:
- Send an email to your Resend inbound address
- Visit
https://your-app-url.ondigitalocean.app/emailsto see it in the inspector!
Or fork and deploy manually - See detailed deployment instructions below.
# Create a virtual environment (recommended)
python3 -m venv venv
source venv/bin/activate # On Windows: venv\Scripts\activate
# Install dependencies
pip install -r requirements.txt- Go to your Resend Dashboard
- Navigate to Webhooks section
- Click Add Webhook
- Enter your webhook URL (e.g.,
https://yourdomain.com/webhook) - Select the event type:
email.received - Click Add
- In Resend dashboard, go to Emails β Receiving
- View your inbound address (e.g.,
anything@your-domain.resend.app) - Any email sent to this address will trigger the webhook
# Run the Flask app
python app.pyThe app will run on http://localhost:8080 by default.
View received emails:
- Open
http://localhost:8080/emailsin your browser to see the email viewer - Send a test email to your Resend inbound address
- Refresh the page to see new emails appear
To test webhooks locally, use ngrok:
# Install ngrok (if not already installed)
# Then expose your local server
ngrok http 8080
# Use the ngrok URL (e.g., https://abc123.ngrok.io/webhook)
# as your webhook endpoint in ResendFor production, use gunicorn:
gunicorn -w 4 -b 0.0.0.0:8080 app:appIf you want to customize the app before deploying, follow these steps:
- Fork this repository on GitHub
- Click the deploy button above (it will use your forked repo)
- Or manually deploy:
- Go to DigitalOcean App Platform
- Click Create App
- Choose GitHub and select your forked repository
- DigitalOcean will auto-detect settings from the Procfile
- Click Deploy
-
Clone and customize:
git clone https://github.com/ajot/resend-webhook.git cd resend-webhook # Make your changes
-
Create your own GitHub repo:
gh repo create your-resend-webhook --public --source=. --remote=origin git push -u origin main
-
Deploy to DigitalOcean:
- Go to DigitalOcean App Platform
- Click Create App
- Choose GitHub as your source
- Select your repository
- DigitalOcean will auto-detect Python and use the Procfile
- Important: Leave "Run Command" empty (let the Procfile handle it)
- Choose your plan (Basic $5/month is sufficient)
- Click Create Resources
-
Configure Resend Webhook:
- Once deployed, get your app URL (e.g.,
https://your-app-xxxxx.ondigitalocean.app) - Go to Resend Dashboard β Webhooks
- Add webhook:
https://your-app-xxxxx.ondigitalocean.app/webhook - Select event:
email.received
- Once deployed, get your app URL (e.g.,
- β
Procfile- Specifies how to run the app (gunicorn command) - β
requirements.txt- Python dependencies (includes gunicorn) - β
app.py- Main Flask application (configured for port 8080) - β
.do/deploy.template.yaml- One-click deployment configuration
Error: "No application module specified"
- This happens when the Run Command is incomplete or incorrect
- Solution: Go to Settings β Components β web β Run Command
- Either leave it empty (recommended - uses Procfile) or set it to
gunicorn app:app - Never use a partial command like
gunicorn --worker-tmp-dir /dev/shmwithoutapp:app
Build fails with "invalid Procfile entry"
- Check for extra blank lines in your Procfile
- Procfile should contain only:
web: gunicorn app:app
App deployed but webhook not working
- Check your Resend webhook URL includes
/webhookat the end - Verify event type is set to
email.received(notemail.delivered) - Check the app logs in DigitalOcean for error messages
- In App Platform, go to Settings β Domains
- Add your custom domain
- Follow DNS configuration instructions
- Update your Resend webhook URL to use the custom domain
Receives inbound email webhook events from Resend.
Expected Payload:
{
"type": "email.received",
"created_at": "2024-02-22T23:41:12.126Z",
"data": {
"email_id": "56761188-7520-42d8-8898-ff6fc54ce618",
"created_at": "2024-02-22T23:41:11.894719+00:00",
"from": "Acme <onboarding@resend.dev>",
"to": ["delivered@resend.dev"],
"bcc": [],
"cc": [],
"message_id": "<example+123>",
"subject": "Sending this example",
"attachments": [
{
"id": "2a0c9ce0-3112-4728-976e-47ddcd16a318",
"filename": "avatar.png",
"content_type": "image/png",
"content_disposition": "inline",
"content_id": "img001"
}
]
}
}Response:
{
"status": "success",
"message": "Email processed successfully",
"result": {
"email_id": "56761188-7520-42d8-8898-ff6fc54ce618",
"processed_at": "2024-02-22T23:41:12.126Z",
"status": "processed"
}
}Web interface to view recent received emails.
Features:
- Displays last 20 emails in a responsive table
- Shows: timestamp, from, to, subject, attachment count
- Click "View Details" to see full email payload
- Empty state when no emails received yet
- Styled with Tailwind CSS
Access: Visit https://your-app-url.ondigitalocean.app/emails in your browser
Returns recent emails as JSON for programmatic access.
Response:
{
"emails": [
{
"email_id": "56761188-7520-42d8-8898-ff6fc54ce618",
"from": "sender@example.com",
"to": ["recipient@example.com"],
"cc": [],
"bcc": [],
"subject": "Test email",
"message_id": "<message@example.com>",
"attachments": [],
"created_at": "2024-02-22T23:41:11.894719+00:00",
"processed_at": "2024-02-22T23:41:12.126Z"
}
],
"count": 1
}Health check endpoint.
Response:
{
"status": "healthy"
}Edit the process_inbound_email() function in app.py to add your custom logic:
- Store emails in a database
- Forward emails to another service
- Parse email content
- Download and process attachments
- Send notifications
- etc.
PORT- Port to run the application on (default: 8080)
- Email storage: Emails are stored in-memory (last 20 emails). Data resets when the app restarts. For production, consider using a database.
- The webhook currently logs all email data. Adjust logging levels in production.
- Attachment metadata is included, but actual attachment content would need to be fetched separately if needed.
- Consider adding webhook signature verification for production use.
- The
/emailsviewer is great for demos and testing. For production dashboards, build a proper frontend with persistent storage.