Email Logs Guide
The Email Logs API provides access to detailed delivery status and activity information for all emails sent through EDITH.
Overview
Email logs allow you to:
- Track email delivery status
- View open and click activity
- Debug delivery issues
- Generate reports and analytics
- Monitor campaign performance
Get Email Logs
Endpoint
GET /v1/email/logs
Authentication
Requires Bearer token authentication.
Query Parameters
All parameters are optional and can be combined for precise filtering. Every query is automatically scoped to your account — you never pass a tenant or account ID. Note the date defaults and limits below the table.
| Parameter | Type | Description |
|---|---|---|
email | string | Filter by recipient email address (exact match) |
recipientDomain | string | Filter by recipient's email domain (exact match) |
refId | string | Filter by email reference ID (returned when sending) |
status | string | Filter by delivery status |
fromAddress | string | Filter by sender email address (exact match) |
subject | string | Filter by email subject (exact match) |
campaignId | string | Filter by campaign ID |
recipientType | string | Filter by recipient type: "to", "cc", "bcc" |
failedReason | string | Filter by failure reason |
startCreatedAt | string | Start date for creation date filter (RFC3339 format) |
endCreatedAt | string | End date for creation date filter (RFC3339 format) |
startDeliveredDate | string | Start date for delivery date filter (RFC3339 format) |
endDeliveredDate | string | End date for delivery date filter (RFC3339 format) |
pageSize | integer | Number of results per page (default: 20, max: 50) |
pageToken | string | Opaque cursor for the next page (see Pagination) |
Defaults & Limits
These rules are enforced by the API — read them before building queries:
- Default window is the last 7 days. If you send neither
startCreatedAtnorendCreatedAt, the API returns the most recent 7 days. - A date window cannot exceed 7 days. A wider range returns
400 "date range cannot exceed 7 days". To cover a longer period, page through it in ≤ 7-day windows. - Send
startCreatedAtandendCreatedAttogether. Providing only one returns400("endCreatedAt is required"/"startCreatedAt is required"), andstartCreatedAtmust not be afterendCreatedAt. pageSizemust be 1–50 (default20). Values outside that range are rejected with400 "pageSize must be between 1 and 50"— they are not silently clamped.- The same 7-day defaults and limits apply to the delivery-date window (
startDeliveredDate/endDeliveredDate).
Email Status Values
| Status | Description |
|---|---|
queued | Email is queued for sending |
sent | Email sent to the mail server |
delivered | Email successfully delivered to recipient's inbox |
opened | Recipient opened the email (if tracking enabled) |
clicked | Recipient clicked a link (if tracking enabled) |
bounced | Email bounced (soft or hard) |
failed | Email failed to send |
spam | Email marked as spam |
unsubscribed | Recipient unsubscribed |
Example Requests
Get All Logs (Paginated)
curl -X GET "https://api.sparrowmailer.com/v1/email/logs?pageSize=20" \
-H "Authorization: Bearer YOUR_TOKEN"
Filter by Recipient Email
curl -X GET "https://api.sparrowmailer.com/v1/email/logs?email=customer@example.com" \
-H "Authorization: Bearer YOUR_TOKEN"
Filter by Status
curl -X GET "https://api.sparrowmailer.com/v1/email/logs?status=bounced" \
-H "Authorization: Bearer YOUR_TOKEN"
Filter by Campaign
curl -X GET "https://api.sparrowmailer.com/v1/email/logs?campaignId=welcome-series-2024" \
-H "Authorization: Bearer YOUR_TOKEN"
Filter by Date Range
Send startCreatedAt and endCreatedAt together, within a 7-day window:
curl -X GET "https://api.sparrowmailer.com/v1/email/logs?startCreatedAt=2024-01-15T00:00:00Z&endCreatedAt=2024-01-21T23:59:59Z" \
-H "Authorization: Bearer YOUR_TOKEN"
Filter by Reference ID
curl -X GET "https://api.sparrowmailer.com/v1/email/logs?refId=01JC3BBW8S9YGX2VNKG5MD7BTA" \
-H "Authorization: Bearer YOUR_TOKEN"
Combine Multiple Filters
curl -X GET "https://api.sparrowmailer.com/v1/email/logs?campaignId=promo-jan&status=delivered" \
-H "Authorization: Bearer YOUR_TOKEN"
Response Format
{
"success": true,
"emailLogs": [
{
"refId": "01JC3BBW8S9YGX2VNKG5MD7BTA",
"accountId": 12345,
"tenantId": 67890,
"fromAddress": "notifications@mail.yourcompany.com",
"recipientAddress": "customer@example.com",
"recipientDomain": "example.com",
"recipientType": "to",
"subject": "Your Order Confirmation",
"status": "delivered",
"priority": "normal",
"campaignId": "order-confirmation",
"opened": true,
"clicked": true,
"failedReason": null,
"createdAt": "2024-01-15T10:30:00Z",
"deliveredDate": "2024-01-15T10:30:05Z",
"updatedAt": "2024-01-15T11:45:00Z",
"track": {
"opens": [
{
"timestamp": "2024-01-15T11:00:00Z",
"userAgent": "Mozilla/5.0...",
"ip": "192.168.1.1"
}
],
"clicks": [
{
"timestamp": "2024-01-15T11:05:00Z",
"url": "https://yourcompany.com/orders/12345",
"userAgent": "Mozilla/5.0...",
"ip": "192.168.1.1"
}
]
}
}
],
"nextPageToken": "eyJJZCI6IjAxSkM0...",
"totalCount": 1
}
Response Fields Explained
Response Envelope
| Field | Type | Description |
|---|---|---|
success | boolean | true when the request succeeded |
emailLogs | array | The matching log entries for this page |
nextPageToken | string | Cursor for the next page. Omitted when there are no more results — see Pagination |
totalCount | integer | Number of records returned in this response — not a global total across all pages |
Email Log Entry
| Field | Type | Description |
|---|---|---|
refId | string | Unique reference ID for this email (returned when sending) |
accountId | integer | Your account identifier |
tenantId | integer | Tenant/organization identifier |
fromAddress | string | Sender email address |
recipientAddress | string | Recipient email address |
recipientDomain | string | Domain portion of recipient email |
recipientType | string | How recipient was addressed: "to", "cc", "bcc" |
subject | string | Email subject line |
status | string | Current delivery status |
priority | string | Email priority: "normal" or "high" |
campaignId | string | Campaign identifier (if provided when sending) |
opened | boolean | Whether email has been opened (if tracking enabled) |
clicked | boolean | Whether any links were clicked (if tracking enabled) |
failedReason | string | Reason for failure (if status is failed/bounced) |
createdAt | string | When the email was created (RFC3339) |
deliveredDate | string | When the email was delivered (RFC3339) |
updatedAt | string | Last update timestamp (RFC3339) |
track | object | Detailed tracking events (opens, clicks) |
Track Object
| Field | Type | Description |
|---|---|---|
opens | array | List of open events with timestamp, userAgent, and IP |
clicks | array | List of click events with timestamp, URL, userAgent, and IP |
Pagination
The API uses cursor-based pagination. Each response returns a nextPageToken while more results exist; pass it back as pageToken to fetch the next page.
When to use it: a single page returns at most 50 records, so any query that matches more than pageSize rows — exporting a busy 7-day window, pulling every bounce for a campaign — needs paging to read the full set.
Initial Request
curl -X GET "https://api.sparrowmailer.com/v1/email/logs?status=bounced&pageSize=50" \
-H "Authorization: Bearer YOUR_TOKEN"
Next Page
Use the nextPageToken from the previous response as pageToken, keeping every other filter identical:
curl -X GET "https://api.sparrowmailer.com/v1/email/logs?status=bounced&pageSize=50&pageToken=eyJJZCI6IjAxSkM0..." \
-H "Authorization: Bearer YOUR_TOKEN"
Pagination Tips
pageSizemust be 1–50 (default20); out-of-range values are rejected, not clamped.nextPageTokenis omitted from the response when there are no more results — stop paging when it's absent.- Treat the token as opaque: pass it back exactly as received. Don't parse, build, or modify it.
- Keep your filters identical across pages — changing a filter mid-pagination invalidates the cursor.
totalCountis the size of the current page, not a count of all matching records. To get a full total, page to the end and sum the pages.- Remember the 7-day window cap: to page across a longer period, repeat the loop for each ≤ 7-day window.
Use Cases
1. Track Specific Email Delivery
# Using the ref_id from send response
curl -X GET "https://api.sparrowmailer.com/v1/email/logs?refId=01JC3BBW8S9YGX2VNKG5MD7BTA" \
-H "Authorization: Bearer YOUR_TOKEN"
2. Find Bounced Emails
curl -X GET "https://api.sparrowmailer.com/v1/email/logs?status=bounced&pageSize=50" \
-H "Authorization: Bearer YOUR_TOKEN"
3. Emails from a Sender
Find everything sent from a given fromAddress, then narrow with common combinations:
# All emails from a sender
curl -X GET "https://api.sparrowmailer.com/v1/email/logs?fromAddress=notifications@mail.yourcompany.com" \
-H "Authorization: Bearer YOUR_TOKEN"
# Sender + only failures
curl -X GET "https://api.sparrowmailer.com/v1/email/logs?fromAddress=notifications@mail.yourcompany.com&status=failed" \
-H "Authorization: Bearer YOUR_TOKEN"
Any of these can also be scoped to a date window — see Filter by Date Range.
4. Emails to a Recipient
Pull a recipient's full history, or focus on a specific outcome:
# Every email to a recipient
curl -X GET "https://api.sparrowmailer.com/v1/email/logs?email=customer@example.com" \
-H "Authorization: Bearer YOUR_TOKEN"
# Only what landed as a primary (To) recipient and was delivered
curl -X GET "https://api.sparrowmailer.com/v1/email/logs?email=customer@example.com&recipientType=to&status=delivered" \
-H "Authorization: Bearer YOUR_TOKEN"
5. Campaign Performance
# All sends for a campaign
curl -X GET "https://api.sparrowmailer.com/v1/email/logs?campaignId=black-friday-2024" \
-H "Authorization: Bearer YOUR_TOKEN"
# Delivered vs. bounced for a campaign
curl -X GET "https://api.sparrowmailer.com/v1/email/logs?campaignId=black-friday-2024&status=bounced" \
-H "Authorization: Bearer YOUR_TOKEN"
6. Domain-Wide Issues
# Check if emails to a specific domain are failing
curl -X GET "https://api.sparrowmailer.com/v1/email/logs?recipientDomain=problematic-domain.com&status=failed" \
-H "Authorization: Bearer YOUR_TOKEN"
7. Recent Failures
# Recent failures (defaults to the last 7 days)
curl -X GET "https://api.sparrowmailer.com/v1/email/logs?status=failed&pageSize=50" \
-H "Authorization: Bearer YOUR_TOKEN"
Best Practices
1. Use Specific Filters
Narrow down results to improve performance:
# Good: specific filters
?campaignId=promo-jan&status=delivered
# If you add a date bound, add both — a lone startCreatedAt returns 400
?campaignId=promo-jan&startCreatedAt=2024-01-15T00:00:00Z # 400: endCreatedAt is required
2. Store Reference IDs
When sending emails, store the ref_id for easy lookup:
const response = await sendEmail(payload);
await database.saveEmailRecord({
orderId: order.id,
emailRefId: response.ref_id,
sentAt: new Date()
});
3. Use Campaign IDs Consistently
Tag emails with meaningful campaign IDs:
const payload = {
// ... email content
campaign_id: `${emailType}-${year}-${quarter}`
// e.g., "welcome-2024-q1", "invoice-2024-q1"
};
4. Handle Pagination Properly
async function getAllBouncedEmails() {
const allLogs = [];
let pageToken = null;
do {
const params = new URLSearchParams({
status: 'bounced',
pageSize: '50'
});
if (pageToken) params.set('pageToken', pageToken);
const response = await fetch(
`https://api.sparrowmailer.com/v1/email/logs?${params}`,
{ headers: { 'Authorization': `Bearer ${token}` } }
);
const data = await response.json();
allLogs.push(...data.emailLogs);
pageToken = data.nextPageToken;
} while (pageToken);
return allLogs;
}
5. Monitor Failed Emails
Set up regular checks for failures:
// Daily bounce report — pass both bounds (within 7 days)
const now = new Date();
const yesterday = new Date(now.getTime() - 86400000);
const bouncedEmails = await getEmailLogs({
status: 'bounced',
startCreatedAt: yesterday.toISOString(),
endCreatedAt: now.toISOString()
});
if (bouncedEmails.length > threshold) {
alertOps('High bounce rate detected');
}
Common Errors
| Error | Cause | Solution |
|---|---|---|
Invalid query parameter: <name> | An unrecognized query parameter was sent | Use only the parameters listed above |
date range cannot exceed 7 days | startCreatedAt–endCreatedAt spans more than 7 days | Split into ≤ 7-day windows and page through them |
endCreatedAt is required / startCreatedAt is required | Only one date bound was sent | Send both bounds together, or neither (defaults to last 7 days) |
startCreatedAt cannot be after endCreatedAt | Date bounds are reversed | Ensure start ≤ end |
pageSize must be between 1 and 50 | pageSize outside the allowed range | Use a value from 1 to 50 |
| Wrong date format | Date not RFC3339 | Use RFC3339 (e.g., 2024-01-15T10:30:00Z) |
invalid page token | pageToken was altered or malformed | Pass the nextPageToken exactly as returned; don't modify it |
Unauthorized | Invalid or expired bearer token | Refresh authentication token |
Related Endpoints
- Send Email - Get ref_id for tracking
- Webhooks - Real-time event notifications
- Request Tracing & Rescue - Trace the full flow by trace ID when ref_id is missing
- Subscribe/Unsubscribe - Manage recipient preferences