unsent
unsent.dev
API Reference

Emails

The Emails API allows you to send individual and batch emails, manage scheduling, track delivery status, and monitor bounces, complaints, and unsubscribes.

Base URL

https://api.unsent.dev/v1/emails

Overview

The Emails API provides comprehensive functionality for sending, managing, and tracking emails. It supports both individual and batch sending, template-based emails, scheduling, and detailed tracking capabilities.

Features

Send individual emails

Send single emails with customizable content or templates

Batch email sending

Send up to 100 emails in a single API call

Email scheduling

Schedule emails to be sent at specific times

Email tracking

Track delivery status, bounces, complaints, and unsubscribes

Email management

Update, reschedule, or cancel scheduled emails

Attachments

Send up to 10 attachments with your emails

Custom headers

Add custom SMTP headers to your emails

Quick Start

Send a simple email

curl -X POST https://api.unsent.dev/v1/emails \
  -H "Authorization: Bearer your-api-key" \
  -H "Content-Type: application/json" \
  -d '{
    "to": "recipient@example.com",
    "from": "sender@yourdomain.com",
    "subject": "Hello from Unsent",
    "text": "This is a plain text email",
    "html": "<p>This is an <strong>HTML</strong> email</p>"
  }'

Send with a template

curl -X POST https://api.unsent.dev/v1/emails \
  -H "Authorization: Bearer your-api-key" \
  -H "Content-Type: application/json" \
  -d '{
    "to": "user@example.com",
    "from": "sender@yourdomain.com",
    "templateId": "template_abc123",
    "variables": {
      "name": "John Doe",
      "orderNumber": "12345"
    }
  }'

Schedule an email

curl -X POST https://api.unsent.dev/v1/emails \
  -H "Authorization: Bearer your-api-key" \
  -H "Content-Type: application/json" \
  -d '{
    "to": "recipient@example.com",
    "from": "sender@yourdomain.com",
    "subject": "Scheduled email",
    "html": "<p>This email is scheduled for later.</p>",
    "scheduledAt": "2024-12-25T10:00:00Z"
  }'

API Endpoints

Endpoint Details

Send Email

POST /v1/emails

Send a single email with full customization options.

Request Body Fields

FieldTypeRequiredDescription
tostring or string[]YesRecipient email address(es)
fromstringYesSender email address
subjectstringConditional*Email subject line
htmlstringConditional*HTML email body
textstringConditional*Plain text email body
templateIdstringNoID of a template to use
variablesobjectNoKey-value pairs for template variables (e.g., {{name}})
replyTostring or string[]NoReply-to address(es)
ccstring or string[]NoCC recipient(s)
bccstring or string[]NoBCC recipient(s)
headersobjectNoCustom SMTP headers
attachmentsarrayNoFile attachments (max 10)
scheduledAtstringNoISO 8601 datetime to schedule sending
inReplyToIdstringNoEmail ID this is replying to

*Required: Either subject or templateId must be provided. Either text, html, or templateId must be provided.

Using Templates

When using templateId:

  • The template must belong to your team
  • subject, html, and text are optional (template values will be used)
  • You can override template values by providing them in the request
  • Use variables to replace placeholders like {{name}} in the template

Attachments Format

{
  "attachments": [
    {
      "filename": "invoice.pdf",
      "content": "base64-encoded-file-content"
    }
  ]
}

Response

{
  "emailId": "email_abc123"
}

Batch Send Emails

POST /v1/emails/batch

Send multiple emails in a single request (up to 100 emails per batch).

Request Body

An array of email objects, each following the same schema as the single email endpoint:

[
  {
    "to": "user1@example.com",
    "from": "sender@yourdomain.com",
    "subject": "Welcome!",
    "html": "<p>Welcome to our service!</p>"
  },
  {
    "to": "user2@example.com",
    "from": "sender@yourdomain.com",
    "subject": "Welcome!",
    "html": "<p>Welcome to our service!</p>"
  }
]

Response

{
  "data": [
    { "emailId": "email_abc123" },
    { "emailId": "email_def456" }
  ]
}

List Emails

GET /v1/emails

Retrieve a paginated list of emails sent by your team.

Query Parameters

ParameterTypeDefaultDescription
pagenumber1Page number for pagination
limitnumber20Number of results per page (max 20)
startDatestring-Filter emails created after this ISO 8601 date
endDatestring-Filter emails created before this ISO 8601 date
domainIdstring or string[]-Filter by domain ID(s)

Example Request

curl -X GET "https://api.unsent.dev/v1/emails?page=1&limit=20&startDate=2024-01-01T00:00:00Z" \
  -H "Authorization: Bearer your-api-key"

Response

{
  "data": [
    {
      "id": "email_abc123",
      "to": "user@example.com",
      "from": "sender@yourdomain.com",
      "subject": "Welcome!",
      "html": "<p>Welcome!</p>",
      "text": null,
      "latestStatus": "DELIVERED",
      "scheduledAt": null,
      "domainId": "domain_xyz",
      "createdAt": "2024-01-15T10:30:00Z",
      "updatedAt": "2024-01-15T10:30:15Z",
      "replyTo": null,
      "cc": null,
      "bcc": null
    }
  ],
  "count": 150
}

Get Email Details

GET /v1/emails/{emailId}

Retrieve detailed information about a specific email, including delivery events.

Response

{
  "id": "email_abc123",
  "teamId": "team_xyz",
  "to": "user@example.com",
  "from": "sender@yourdomain.com",
  "subject": "Welcome!",
  "html": "<p>Welcome!</p>",
  "text": null,
  "createdAt": "2024-01-15T10:30:00Z",
  "updatedAt": "2024-01-15T10:30:15Z",
  "replyTo": null,
  "cc": null,
  "bcc": null,
  "emailEvents": [
    {
      "emailId": "email_abc123",
      "status": "QUEUED",
      "createdAt": "2024-01-15T10:30:00Z",
      "data": {}
    },
    {
      "emailId": "email_abc123",
      "status": "SENT",
      "createdAt": "2024-01-15T10:30:01Z",
      "data": {}
    },
    {
      "emailId": "email_abc123",
      "status": "DELIVERED",
      "createdAt": "2024-01-15T10:30:15Z",
      "data": {}
    }
  ]
}

Email Status Values

  • QUEUED - Email is queued for sending
  • SENT - Email has been sent to the SMTP server
  • DELIVERED - Email was successfully delivered
  • BOUNCED - Email bounced
  • COMPLAINED - Recipient marked as spam
  • OPENED - Recipient opened the email
  • CLICKED - Recipient clicked a link

Get Email Events

GET /v1/emails/{emailId}/events

Retrieve all events (sent, delivered, opened, etc.) for a specific email.

Path Parameters:

ParameterTypeRequiredDescription
emailIdstringYesThe unique identifier of the email

Query Parameters:

ParameterTypeRequiredDefaultDescription
pagenumberNo1Page number
limitnumberNo50Results per page (max 100)
statusstringNo-Filter by event status (e.g., DELIVERED, OPENED)
startDatestringNo-Filter events after this date (ISO 8601)
curl -X GET https://api.unsent.dev/v1/emails/email_123/events?limit=10 \
  -H "Authorization: Bearer your-api-key"

Response (200 OK):

{
  "events": [
    {
      "id": "evt_123",
      "emailId": "email_123",
      "status": "DELIVERED",
      "data": {},
      "teamId": "team_abc",
      "createdAt": "2024-01-15T10:30:15Z"
    },
    {
      "id": "evt_122",
      "emailId": "email_123",
      "status": "SENT",
      "data": {},
      "teamId": "team_abc",
      "createdAt": "2024-01-15T10:30:01Z"
    }
  ],
  "page": 1,
  "limit": 10
}

Error Responses:

  • 400 Bad Request - Invalid parameters
  • 401 Unauthorized - Invalid API key
  • 404 Not Found - Email not found
  • 500 Internal Server Error - Server error

Update Email

PATCH /v1/emails/{emailId}

Update a scheduled email's send time or clear the schedule.

Request Body

{
  "scheduledAt": "2024-12-26T10:00:00Z"  // or null to clear
}

Use Cases

  • Reschedule an email to a different time
  • Clear scheduling by passing null to send immediately

Response

{
  "emailId": "email_abc123"
}

Cancel Email

POST /v1/emails/{emailId}/cancel

Cancel a scheduled email before it's sent.

Note: You can only cancel emails that are scheduled (not yet sent).

Response

{
  "emailId": "email_abc123"
}

Get Bounces

GET /v1/emails/bounces

Retrieve a list of emails that bounced.

Query Parameters

ParameterTypeDefaultDescription
pagenumber1Page number
limitnumber20Results per page (max 100)

Response

{
  "data": [
    {
      "id": "email_abc123",
      "email": "bounced@example.com",
      "subject": "Welcome!",
      "status": "BOUNCED",
      "createdAt": "2024-01-15T10:30:00Z"
    }
  ],
  "count": 5
}

Get Complaints

GET /v1/emails/complaints

Retrieve emails that were marked as spam by recipients.

Query Parameters

ParameterTypeDefaultDescription
pagenumber1Page number
limitnumber20Results per page (max 100)

Response

{
  "data": [
    {
      "id": "email_abc123",
      "email": "user@example.com",
      "subject": "Newsletter",
      "status": "COMPLAINED",
      "createdAt": "2024-01-15T10:30:00Z"
    }
  ],
  "count": 2
}

Get Unsubscribes

GET /v1/emails/unsubscribes

Retrieve contacts who have unsubscribed from your emails.

Query Parameters

ParameterTypeDefaultDescription
pagenumber1Page number
limitnumber20Results per page (max 100)

Response

{
  "data": [
    {
      "id": "contact_abc123",
      "email": "user@example.com",
      "firstName": "John",
      "lastName": "Doe",
      "reason": "UNSUBSCRIBED",
      "contactBookId": "book_xyz",
      "createdAt": "2024-01-15T10:30:00Z"
    }
  ],
  "count": 10
}

Idempotency

The Emails API supports idempotency for both single and batch email sending to prevent duplicate sends during retries.

How It Works

Pass an Idempotency-Key header (up to 256 characters) with your request:

  • Same key + same request body → Returns the original emailId with 200 OK without re-sending
  • Same key + different request body → Returns 409 Conflict with code NOT_UNIQUE
  • Same key while request is processing → Returns 409 Conflict; retry after completion

Idempotency entries expire after 24 hours.

Example Usage

curl -X POST https://api.unsent.dev/v1/emails \
  -H "Authorization: Bearer your-api-key" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: order-12345-confirmation" \
  -d '{
    "to": "customer@example.com",
    "from": "orders@yourdomain.com",
    "subject": "Order Confirmation",
    "html": "<p>Thank you for your order!</p>"
  }'

Best Practices

  • Use unique keys per logical operation (e.g., order ID, signup ID)
  • Include context in the key for debugging (e.g., order-12345-confirmation)
  • Reuse the same key when retrying a failed request
  • Works with both /v1/emails and /v1/emails/batch endpoints

Common Use Cases

Transactional Emails

  • Password resets
  • Order confirmations
  • Shipping notifications
  • Account verifications

Marketing Emails

  • Newsletters
  • Promotional campaigns
  • Product announcements
  • Customer engagement

Automated Notifications

  • System alerts
  • Status updates
  • Reminder emails
  • Report deliveries

Rate Limits

  • Send email (POST /v1/emails): 100 requests per minute
  • Batch send (POST /v1/emails/batch): 1000 emails per minute (max 100 per batch)
  • List/Get endpoints: 100 requests per minute
  • Tracking endpoints (bounces/complaints/unsubscribes): 100 requests per minute

Best Practices

Sending Emails

  1. Use templates for consistent branding and easier maintenance
  2. Include both text and HTML versions for better deliverability and accessibility
  3. Verify your sending domain before going to production
  4. Use idempotency keys for critical transactional emails to prevent duplicates
  5. Set appropriate reply-to addresses to handle customer responses

Performance

  1. Use batch sending (/v1/emails/batch) when sending to multiple recipients
  2. Schedule emails during off-peak hours for better deliverability
  3. Limit attachments to necessary files only (max 10 per email)

Tracking & Monitoring

  1. Monitor bounces regularly via /v1/emails/bounces and remove invalid addresses
  2. Handle complaints by checking /v1/emails/complaints and respecting opt-outs
  3. Track unsubscribes via /v1/emails/unsubscribes and update your contact lists
  4. Use email events from GET /v1/emails/{emailId} to track delivery lifecycle

Template Usage

  1. Create reusable templates in the dashboard for common email types
  2. Use variables ({{name}}, {{orderNumber}}) for personalization
  3. Override template content when needed by passing subject, html, or text in the request
  4. Test templates before using them in production campaigns