Overview
The Resend integration enables Fanfare to send transactional emails to consumers. Resend provides reliable email delivery with detailed analytics and is used for all email communications including:
- Appointment confirmations and reminders
- Draw and auction outcome notifications
- Queue admission alerts
- Waitlist updates
- OTP codes for authentication
Prerequisites
The Resend integration is pre-configured for the Fanfare platform at the infrastructure level. Individual organizations can customize their email sender settings.
Note: Fanfare uses a shared Resend account for email delivery. Organizations can customize the “From” name and
reply-to address in their messaging settings.
Email Templates
Authentication Templates
| Template | Description |
|---|
OTP_LOGIN | One-time password for passwordless login |
PERSONAL_ACCESS_CODE | Unique access codes for experiences |
Appointment Templates
| Template | Description |
|---|
APPOINTMENT_CONFIRMATION | Confirms booking with all details |
APPOINTMENT_REMINDER | Reminds of upcoming appointment |
APPOINTMENT_CANCELLED | Notifies of cancellation |
APPOINTMENT_RESCHEDULED | Notifies of date/time change |
Experience Outcome Templates
| Template | Description |
|---|
AUCTION_WON | Notifies winner with checkout link |
AUCTION_LOST | Notifies non-winners |
DRAW_WON | Notifies draw winner with checkout link |
DRAW_ENTERED | Confirms draw entry |
DRAW_NOT_SELECTED | Notifies non-selection |
QUEUE_ADMITTED | Notifies queue admission with checkout link |
Waitlist Templates
| Template | Description |
|---|
WAITLIST_CONFIRMATION | Confirms waitlist signup |
WAITLIST_NOTIFIED | Notifies of product/experience availability |
Email Branding
Brand Customization
Each email includes organization branding:
| Element | Source |
|---|
| Organization Name | Organization settings |
| Organization Logo | Brand settings or header media asset |
| Primary Color | Brand theme settings |
| Support Email | Messaging settings |
Email Sender Settings
Organizations can customize sender information in Settings > Organization:
| Field | Description |
|---|
| From Email | Email address shown as sender |
| From Name | Display name for sender |
| Reply-To Email | Where replies are directed |
| Reply-To Name | Display name for reply-to |
Domain Verification: Custom “From” email addresses require domain verification. Contact support to configure a
custom sending domain.
Message Delivery
How Emails Are Sent
┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Fanfare │────▶│ SNS │────▶│ SQS │────▶│ Lambda │
│ Event │ │ Topic │ │ Queue │ │ Processor │
└─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘
│
│
▼
┌─────────────┐
│ Resend │
│ API │
└─────────────┘
- An event triggers an email (e.g., consumer wins draw)
- Email is queued via SNS/SQS
- Lambda processor renders template and sends via Resend
- Delivery status tracked via Svix webhooks
Delivery Status Events
Resend sends webhook events for email delivery:
| Event Type | Description |
|---|
email.sent | Email accepted for delivery |
email.delivered | Email delivered to recipient server |
email.bounced | Email bounced (hard/soft) |
email.complained | Recipient marked as spam |
email.clicked | Link in email clicked |
email.opened | Email was opened |
email.delivery_delayed | Delivery is being retried |
Delivery Event Mapping
Resend events are mapped to Fanfare’s delivery event types:
| Resend Event | Fanfare Event |
|---|
email.sent | SENT |
email.delivered | DELIVERED |
email.bounced | BOUNCED |
email.complained | COMPLAINED |
email.clicked | CLICKED |
email.opened | OPENED |
email.delivery_delayed | DELIVERY_DELAYED |
Webhook Events
Webhook Security
Resend uses Svix for webhook delivery, providing:
- Cryptographic signature verification
- Automatic retries on failure
- Timestamp validation
// Svix webhook verification
const wh = new SvixWebhook(secret);
wh.verify(raw, {
"svix-id": svixId,
"svix-timestamp": svixTimestamp,
"svix-signature": svixSignature,
});
| Header | Description |
|---|
svix-id | Unique webhook event ID |
svix-timestamp | Unix timestamp of event |
svix-signature | HMAC signature for verification |
Idempotency
Webhook events are processed idempotently:
- Duplicate events (same
svix-id) are ignored
- Events are stored with provider event ID
- Multiple status updates for same email are recorded
API Reference
Queue Email Message
Send a transactional email.
POST /api/messaging/send-sample
Content-Type: application/json
{
"channel": "EMAIL",
"template": "DRAW_WON",
"toEmail": "[email protected]",
"templateData": {
"template": "DRAW_WON",
"userName": "John Doe",
"productName": "Limited Edition Sneakers",
"productImage": "https://example.com/sneakers.jpg",
"checkoutUrl": "https://fanfare.io/checkout/abc123",
"expiresAt": "2024-06-25T23:59:59Z"
}
}
Response:
{
"status": "queued",
"id": "msg_abc123"
}
Template Data Schemas
Each template requires specific data plus common branding fields:
Common Branding Fields (auto-populated):
{
"organizationName": "My Store",
"organizationLogo": "https://example.com/logo.png",
"supportEmail": "[email protected]",
"theme": {
"primaryColor": "#3B82F6"
}
}
DRAW_WON:
{
"template": "DRAW_WON",
"userName": "John Doe",
"productName": "Limited Edition Sneakers",
"productImage": "https://example.com/product.jpg",
"checkoutUrl": "https://fanfare.io/checkout/abc123",
"expiresAt": "2024-06-25T23:59:59Z"
}
APPOINTMENT_CONFIRMATION:
{
"template": "APPOINTMENT_CONFIRMATION",
"userName": "Jane Smith",
"productName": "Exclusive Preview",
"productImage": "https://example.com/preview.jpg",
"appointmentDate": "2024-06-25T14:00:00Z",
"timeZone": "America/New_York",
"location": "Fifth Avenue Store"
}
OTP_LOGIN:
{
"template": "OTP_LOGIN",
"userName": "Customer",
"otpCode": "123456"
}
Rate Limiting
API Rate Limits
The messaging endpoint is rate-limited:
| Limit | Window | Scope |
|---|
| 10 requests | 60 seconds | Per user per organization |
Resend Rate Limits
Resend has the following limits:
- 100 emails/second for verified domains
- 3,000 emails/day on free tier
- Higher limits available on paid plans
Testing
Sending Test Emails
Use the messaging endpoint to send test emails:
POST /api/messaging/send-sample
Content-Type: application/json
{
"channel": "EMAIL",
"template": "QUEUE_ADMITTED",
"toEmail": "[email protected]"
}
If templateData is omitted, sample data is generated using:
- Organization branding from settings
- Placeholder product/user information
- Mock URLs and dates
Test messages include metadata:
isTest: true
requestedBy: <userId>
ts: <timestamp>
Email Rendering
Template Architecture
Emails are rendered using React Email components:
- Template data is validated against schema
- Brand data is fetched from organization settings
- React components render HTML
- Rendered HTML is sent via Resend
Caching
Brand data is cached for performance:
- Organization branding data
- Experience-specific branding overrides
- Cache invalidated on settings update
// Invalidate cache when branding changes
invalidateExperienceBrandingCache(organizationId, experienceId);
Troubleshooting
Common Issues
Email not delivered
- Verify email address is valid
- Check spam/junk folders
- Review Resend delivery logs
Webhook not received
- Verify Svix signature headers present
- Check webhook secret configuration
- Review Resend webhook logs
Missing organization branding
- Ensure organization settings are configured
- Upload organization logo
- Set support email in messaging settings
Template rendering error
- Verify all required fields are provided
- Check field types match schema
- Review Lambda processor logs
Checking Delivery Status
Query transactional messages:
SELECT
id,
status,
provider_message_id,
template_name,
created_at,
sent_at
FROM transactional_messages
WHERE organization_id = 'org_123'
AND channel_type = 'EMAIL'
ORDER BY created_at DESC;
Delivery Event Logs
View detailed delivery events:
SELECT
event_type,
event_time,
recipient_identifier,
raw_webhook_data
FROM message_delivery_events
WHERE transactional_message_id = 'msg_123'
ORDER BY event_time;
Security
Email Content
- Links use HTTPS
- Checkout links include secure tokens
- OTP codes expire after short window
Data Privacy
- Email addresses encrypted at rest
- Webhook payloads contain minimal PII
- Logs redact sensitive information
Webhook Authentication
- All webhooks verified via Svix signature
- Invalid signatures rejected with 401
- Events deduplicated by provider ID
Limitations
| Limitation | Description |
|---|
| HTML only | Plain text fallback not included |
| Single recipient | Batch sending not supported |
| English templates | Templates are in English only |
| Domain verification | Custom domains require setup |