Email API Documentation
The Email API service provides a robust, multi-provider email delivery system with campaign management, bulk sending capabilities, and webhook event processing.
Base URL
- Production:
https://email.api.eventjuicer.com - Development: Your local or staging URL
Table of Contents
Authentication
All API requests require authentication via an API key. The API key should be included in the request body for email sending operations.
{
"api_key": "your-mailgun-or-resend-api-key",
"driver": "mailgun"
}Endpoints
Send Single Email
Send a single email to one or multiple recipients.
Endpoint: POST /api/email/send
Request Body:
{
"driver": "mailgun",
"api_key": "your-api-key",
"from": "sender@example.com",
"from_name": "Sender Name",
"to": "recipient@example.com",
"subject": "Hello {{name}}!",
"html": "<h1>Hi {{name}}, your code is {{code}}</h1>",
"text": "Hi {{name}}, your code is {{code}}",
"replyTo": "reply@example.com",
"cc": ["cc@example.com"],
"bcc": ["bcc@example.com"],
"tags": ["tag1", "tag2"],
"variables": {
"name": "John Doe",
"code": "12345"
},
"metadata": {
"campaign_id": 123,
"participant_id": 456,
"custom_field": "tracking_value"
}
}Parameters:
| Field | Type | Required | Description |
|---|---|---|---|
driver | string | Yes | Email provider: mailgun or resend |
api_key | string | Yes | Provider API key |
from | string | Yes | Sender email address |
from_name | string | No | Sender display name |
to | string | string[] | Yes | Recipient email address(es) |
subject | string | Yes | Email subject |
html | string | Yes* | HTML email content |
text | string | Yes* | Plain text content |
replyTo | string | No | Reply-to email address |
cc | string | string[] | No | CC recipients |
bcc | string | string[] | No | BCC recipients |
tags | string[] | No | Tags for tracking |
variables | object | No | Variables for template substitution ({{var}}) |
metadata | object | No | Metadata for webhook tracking (returned in webhooks) |
*At least one of html or text is required.
Template Variables vs Metadata:
variables: Used for replacing{{placeholders}}in email content (subject, HTML, text)metadata: Tracking data that comes back in webhook events (not visible in email)
Response:
{
"success": true,
"driverName": "mailgun",
"externalId": "msg-123456",
"message": "Email sent successfully",
"recipients": 1
}cURL Example:
curl -X POST https://email.api.eventjuicer.com/api/email/send \
-H "Content-Type: application/json" \
-d '{
"driver": "mailgun",
"api_key": "your-api-key",
"from": "noreply@example.com",
"to": "user@example.com",
"subject": "Welcome!",
"html": "<h1>Welcome to our service!</h1>"
}'Send Bulk Emails
Send personalized emails to multiple recipients with template variable substitution.
Endpoint: POST /api/email/bulk
Request Body:
{
"driver": "mailgun",
"api_key": "your-api-key",
"from": "sender@example.com",
"from_name": "Sender Name",
"subject": "Hello {{name}}!",
"html": "<h1>Hi {{name}}</h1><p>Your code is: {{code}}</p>",
"text": "Hi {{name}}, your code is: {{code}}",
"recipients": [
{
"email": "user1@example.com",
"variables": { "name": "Alice", "code": "ABC123" },
"metadata": { "participant_id": 101 }
},
{
"email": "user2@example.com",
"variables": { "name": "Bob", "code": "XYZ789" },
"metadata": { "participant_id": 102 }
}
],
"metadata": {
"campaign_id": 456
},
"tags": ["bulk-send", "campaign-2024"]
}Note: When using recipient_ids instead of recipients, the API automatically adds participant_id to each recipient's metadata.
Parameters:
| Field | Type | Required | Description |
|---|---|---|---|
driver | string | Yes | Email provider: mailgun or resend |
api_key | string | Yes | Provider API key |
from | string | Yes | Sender email address |
from_name | string | No | Sender display name |
subject | string | Yes | Email subject (supports {{variables}}) |
html | string | Yes* | HTML content (supports {{variables}}) |
text | string | Yes* | Plain text (supports {{variables}}) |
recipients | array | Yes* | Array of recipient objects |
recipients[].email | string | Yes | Recipient email |
recipients[].variables | object | No | Per-recipient template variables |
recipients[].metadata | object | No | Per-recipient webhook metadata |
recipient_ids | number[] | Yes* | Array of participant IDs (auto-resolves to recipients) |
metadata | object | No | Template-level metadata (applies to all recipients) |
tags | string[] | No | Tags for tracking |
*Either recipients or recipient_ids is required.
Automatic Metadata:
- When using
recipient_ids, the API automatically addsparticipant_idto metadata for each recipient - Template
metadatais merged with recipientmetadata(recipient takes precedence) - Webhook events will contain both
campaign_idandparticipant_idfor easy database lookups
Response:
{
"success": true,
"driverName": "mailgun",
"totalEmails": 2,
"message": "Bulk email sent successfully"
}Variable Substitution:
Use {{variableName}} in your subject, HTML, and text content. Each recipient can have different variable values.
Campaign Management
Create and send email campaigns with queue-based processing.
Create Campaign
Endpoint: POST /api/email/campaigns
Request Body:
{
"driver": "mailgun",
"api_key": "your-api-key",
"name": "Newsletter March 2024",
"from": "newsletter@example.com",
"template_subject": "March Newsletter",
"template_url": "https://example.com/templates/newsletter.html",
"recipients": [
{ "email": "user1@example.com" },
{ "email": "user2@example.com" }
]
}Response:
{
"success": true,
"campaign": {
"uuid": "550e8400-e29b-41d4-a716-446655440000",
"name": "Newsletter March 2024",
"status": "pending",
"recipients_count": 2
}
}Send Campaign
Endpoint: POST /api/email/campaigns/{uuid}/send
Response:
{
"success": true,
"message": "Campaign processing started",
"batches": 1,
"totalRecipients": 2
}Email Templates
Validate external email templates before sending.
Endpoint: POST /api/email/templates
Request Body:
{
"template_url": "https://example.com/templates/welcome.html"
}Response:
{
"valid": true,
"size": 12543,
"preview": "<!DOCTYPE html>..."
}Webhooks
The API provides unified webhook endpoints for receiving email events from both Mailgun and Resend.
Webhook Endpoints
- Mailgun:
POST /api/webhooks/mailgun - Resend:
POST /api/webhooks/resend
Webhook Configuration
Configure these URLs in your email provider's dashboard:
Mailgun:
https://email.api.eventjuicer.com/api/webhooks/mailgun
Resend:
https://email.api.eventjuicer.com/api/webhooks/resend
Webhook Events
All webhooks are verified with cryptographic signatures and normalized to standard event types:
| Event Type | Description |
|---|---|
sent | Email was sent successfully |
delivered | Email was delivered to recipient's mail server |
opened | Recipient opened the email |
clicked | Recipient clicked a link in the email |
bounced | Email bounced (hard bounce) |
failed | Email failed to send |
complained | Recipient marked email as spam |
unsubscribed | Recipient unsubscribed |
delayed | Email delivery was delayed (soft bounce) |
Webhook Payload
Both providers send standardized webhook payloads:
{
"id": "evt_123456",
"type": "delivered",
"timestamp": 1234567890000,
"messageId": "msg-abc123",
"recipient": "user@example.com",
"sender": "noreply@example.com",
"subject": "Welcome Email",
"tags": ["welcome", "onboarding"],
"metadata": {
"campaign_id": 123,
"participant_id": 456,
"custom_field": "value"
},
"raw": {}
}Using Metadata to Track Emails
The metadata field in webhook events allows you to create email tracking records without storing provider message IDs:
Example: Creating EmailRecipient records from webhooks
// In your webhook handler
async function processEmailEvent(event: WebhookEvent) {
const { campaign_id, participant_id } = event.metadata || {};
if (!campaign_id || !participant_id) {
console.warn('Missing metadata in webhook event');
return;
}
// Create or update EmailRecipient record
await db.emailRecipient.upsert({
where: {
campaign_id_participant_id: {
campaign_id,
participant_id
}
},
create: {
campaign_id,
participant_id,
recipient: event.recipient,
status: event.type,
sent_at: event.type === 'sent' ? new Date(event.timestamp) : null,
delivered_at: event.type === 'delivered' ? new Date(event.timestamp) : null,
opened_at: event.type === 'opened' ? new Date(event.timestamp) : null,
clicked_at: event.type === 'clicked' ? new Date(event.timestamp) : null,
bounced_at: event.type === 'bounced' ? new Date(event.timestamp) : null,
},
update: {
status: event.type,
delivered_at: event.type === 'delivered' ? new Date(event.timestamp) : undefined,
opened_at: event.type === 'opened' ? new Date(event.timestamp) : undefined,
clicked_at: event.type === 'clicked' ? new Date(event.timestamp) : undefined,
bounced_at: event.type === 'bounced' ? new Date(event.timestamp) : undefined,
}
});
}Automatic Metadata in Campaigns:
- Campaign sends automatically include
campaign_idin metadata - When using
recipient_ids, each recipient automatically getsparticipant_idin metadata - No need to manually track provider message IDs!
Security
Mailgun Webhooks:
- Uses HMAC-SHA256 signature verification
- Signature included in webhook payload
- Set
MAILGUN_WEBHOOK_SIGNING_KEYenvironment variable
Resend Webhooks:
- Uses Svix-compatible HMAC-SHA256 signature verification
- Requires headers:
svix-id,svix-timestamp,svix-signature - Set
RESEND_WEBHOOK_SECRETenvironment variable
Supported Email Providers
Mailgun
Features:
- Batch sending up to 1,000 recipients per API call
- Template variable substitution
- Webhook events for delivery tracking
- EU and US regions supported
Configuration:
{
"driver": "mailgun",
"api_key": "your-mailgun-api-key"
}Required Environment Variables:
MAILGUN_API_KEY- API key from MailgunMAILGUN_DOMAIN- Your verified domainMAILGUN_WEBHOOK_SIGNING_KEY- For webhook verification (optional)
Resend
Features:
- Batch sending up to 100 recipients per API call
- Template variable substitution
- Webhook events via Svix
- Modern API with great developer experience
Configuration:
{
"driver": "resend",
"api_key": "your-resend-api-key"
}Required Environment Variables:
RESEND_API_KEY- API key from ResendRESEND_WEBHOOK_SECRET- For webhook verification (optional)
Error Handling
The API uses standard HTTP status codes and returns detailed error messages.
Error Response Format
{
"error": "Error message",
"details": "Detailed error description"
}Common Status Codes
| Code | Description |
|---|---|
200 | Success |
400 | Bad Request - Invalid parameters |
401 | Unauthorized - Invalid webhook signature |
500 | Server Error - Processing failed |
Common Errors
Invalid Email Address:
{
"error": "Invalid email addresses",
"invalidEmails": ["invalid-email"]
}Missing Required Fields:
{
"error": "driver, api_key, from, to, subject, and text or html are required"
}Webhook Verification Failed:
{
"error": "Invalid webhook signature"
}Rate Limits
Rate limits depend on your email provider's plan:
Mailgun:
- Foundation: 50,000 emails/month
- Growth: 100,000 emails/month
- Scale: Custom limits
Resend:
- Free: 100 emails/day, 3,000/month
- Pro: 50,000 emails/month
- Enterprise: Custom limits
Best Practices
-
Use Bulk Endpoint for Multiple Recipients
- More efficient than individual sends
- Better for campaigns and newsletters
-
Implement Webhook Handlers
- Track delivery status
- Handle bounces and complaints
- Monitor engagement metrics
-
Use Tags for Organization
- Group emails by campaign
- Track performance by category
- Filter analytics data
-
Handle Errors Gracefully
- Implement retry logic for failed sends
- Log errors for debugging
- Monitor API responses
-
Validate Email Addresses
- Check format before sending
- Remove bounced addresses
- Maintain clean email lists
Support
For issues or questions:
- Check the GitHub repository
- Review webhook logs in your email provider dashboard
- Monitor API response codes and error messages
Changelog
v1.0.0 (2024)
- Initial release
- Support for Mailgun and Resend
- Single and bulk email sending
- Campaign management
- Unified webhook processing
- Standardized event types