Remove application-level CORS and IP whitelisting as security is now handled at CloudFlare edge. CORS is not applicable for backend webhook service, and IP whitelisting is more effectively managed at infrastructure layer. Also translate Dockerfile comments to English and add registry URL to build script. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
177 lines
5.1 KiB
Markdown
177 lines
5.1 KiB
Markdown
# MailGun Webhook API Specification
|
|
|
|
## Overview
|
|
|
|
This document specifies the API for receiving webhook events from MailGun. The service logs all received event data to the console for monitoring and debugging purposes.
|
|
|
|
## API Endpoints
|
|
|
|
### POST /webhook
|
|
|
|
Receives webhook events from MailGun when email events occur.
|
|
|
|
#### Request Format
|
|
|
|
- **Method**: POST
|
|
- **Content-Type**: `application/x-www-form-urlencoded` or `multipart/form-data`
|
|
- **Headers**:
|
|
- No custom headers required for initial implementation
|
|
|
|
#### Request Parameters
|
|
|
|
MailGun sends various parameters depending on the event type. Common parameters include:
|
|
|
|
**Event Identification:**
|
|
- `event` (string) - Type of event (delivered, failed, opened, clicked, bounced, complained, unsubscribed)
|
|
- `timestamp` (number) - Unix timestamp when the event occurred
|
|
- `token` (string) - Randomly generated string for message signature verification
|
|
- `signature` (string) - String with hexadecimal digits for signature verification
|
|
|
|
**Message Information:**
|
|
- `message-id` (string) - MailGun message ID
|
|
- `recipient` (string) - Email address of the recipient
|
|
- `domain` (string) - Domain from which the email was sent
|
|
- `Message-Id` (string) - SMTP Message-ID header
|
|
|
|
**Event-Specific Parameters:**
|
|
|
|
For **delivered** events:
|
|
- `message-headers` (string) - JSON string of message headers
|
|
|
|
For **failed** events:
|
|
- `severity` (string) - Severity level (temporary/permanent)
|
|
- `reason` (string) - Reason for failure
|
|
- `notification` (string) - Detailed notification message
|
|
|
|
For **opened** events:
|
|
- `city` (string) - City where email was opened
|
|
- `country` (string) - Country code
|
|
- `device-type` (string) - Device type (desktop/mobile/tablet)
|
|
- `client-os` (string) - Operating system
|
|
- `client-name` (string) - Email client name
|
|
- `ip` (string) - IP address
|
|
|
|
For **clicked** events:
|
|
- `url` (string) - URL that was clicked
|
|
- `city`, `country`, `device-type`, `client-os`, `client-name`, `ip` - Same as opened events
|
|
|
|
For **bounced** events:
|
|
- `code` (string) - SMTP error code
|
|
- `error` (string) - Detailed error message
|
|
- `notification` (string) - Bounce notification
|
|
|
|
For **complained** events:
|
|
- No additional parameters
|
|
|
|
For **unsubscribed** events:
|
|
- No additional parameters
|
|
|
|
#### Success Response
|
|
|
|
- **HTTP Status**: 200 OK
|
|
- **Content-Type**: `application/json`
|
|
- **Response Body**:
|
|
```json
|
|
{
|
|
"status": "received",
|
|
"message": "Webhook event logged successfully"
|
|
}
|
|
```
|
|
|
|
#### Error Responses
|
|
|
|
**Invalid Request (400 Bad Request)**:
|
|
- **Content-Type**: `application/json`
|
|
- **Response Body**:
|
|
```json
|
|
{
|
|
"error": "Invalid request format"
|
|
}
|
|
```
|
|
|
|
**Server Error (500 Internal Server Error)**:
|
|
- **Content-Type**: `application/json`
|
|
- **Response Body**:
|
|
```json
|
|
{
|
|
"error": "Internal server error"
|
|
}
|
|
```
|
|
|
|
## Execution Flow
|
|
|
|
1. **Receive webhook POST request** from MailGun
|
|
2. **Parse request body** (form-urlencoded or multipart data)
|
|
3. **Extract event data** from request parameters
|
|
4. **Log event data to console** with structured formatting:
|
|
- Event type
|
|
- Timestamp (both Unix and human-readable)
|
|
- Recipient
|
|
- All additional event-specific parameters
|
|
5. **Return success response** to MailGun
|
|
|
|
## Edge Cases
|
|
|
|
### Missing Event Type
|
|
- **Detection**: Check if `event` parameter is present
|
|
- **Handling**: Log warning and return 400 Bad Request
|
|
|
|
### Malformed Timestamp
|
|
- **Detection**: Check if `timestamp` can be parsed as number
|
|
- **Handling**: Log with current timestamp instead, continue processing
|
|
|
|
### Large Payload
|
|
- **Detection**: Monitor request body size
|
|
- **Handling**: Log truncated data if exceeds reasonable size
|
|
|
|
### Duplicate Events
|
|
- **Detection**: MailGun may send duplicate webhooks
|
|
- **Handling**: Log all events (no deduplication in initial implementation)
|
|
|
|
## Security Considerations
|
|
|
|
### Future Enhancements
|
|
For production deployment, consider:
|
|
- **Signature Verification**: Verify webhook authenticity using `timestamp`, `token`, and `signature`
|
|
- **IP Whitelisting**: Restrict to MailGun's IP ranges (implemented via CloudFlare)
|
|
- **Rate Limiting**: Prevent abuse
|
|
|
|
## Database Integration
|
|
|
|
- **Current Implementation**: No database operations required
|
|
- **Future Enhancement**: Store events in database for analysis
|
|
|
|
## Third-Party API Calls
|
|
|
|
- **Current Implementation**: No third-party API calls
|
|
- **Future Enhancement**: Could integrate with notification services
|
|
|
|
## Logging Format
|
|
|
|
Console output format:
|
|
```
|
|
========================================
|
|
MailGun Webhook Event Received
|
|
========================================
|
|
Event Type: delivered
|
|
Timestamp: 1234567890 (2024-01-01 12:00:00 UTC)
|
|
Recipient: user@example.com
|
|
Domain: mail.example.com
|
|
Message ID: <20240101120000.1.ABC123@mail.example.com>
|
|
----------------------------------------
|
|
Additional Parameters:
|
|
{
|
|
"message-headers": "[...]",
|
|
"token": "...",
|
|
"signature": "..."
|
|
}
|
|
========================================
|
|
```
|
|
|
|
## Implementation Notes
|
|
|
|
- Use Express body-parser middleware for form data parsing
|
|
- All logging should use structured logger (debug package)
|
|
- Maintain type safety with TypeScript interfaces for event data
|
|
- Follow template's error handling patterns
|