Email Servers API
The Email Servers API provides full CRUD (Create, Read, Update, Delete) operations for managing email server configurations. You can create, list, view, update, delete, and test email server connections programmatically.
This is particularly useful for automating the setup of email infrastructure or integrating with AI tools like Claude for automated onboarding flows.
Required Permissions
All endpoints require authentication via an API token with appropriate permissions:
- Read Permission: Required for GET endpoints (list, show)
- Write Permission: Required for POST, PATCH, DELETE endpoints, including test connection (it updates the server’s
activeandconnection_failed_atvalues) and copy to channel
Email Server Object
The email server object contains the following fields:
| Field | Description |
|---|---|
| `id` | Unique identifier of the email server |
| `label` | Display name for the email server |
| `vendor` | Email service provider (aws_ses, sendgrid, postmark, mailgun, inboxroad, resend, smtp_com, test_server, other) |
| `delivery_method` | How emails are sent (smtp or api) |
| `active` | Whether the server is active and can be used for sending |
| `emails_per_hour` | Rate limit for emails sent per hour |
| `use_for_broadcasts` | Whether this server can be used for broadcast emails |
| `use_for_sequences` | Whether this server can be used for sequence emails |
| `use_for_transactionals` | Whether this server can be used for transactional emails |
| `include_unsubscribe_header` | Whether to include List-Unsubscribe header in emails |
| `include_unsubscribe_link` | Whether to include unsubscribe link in email body |
| `use_custom_unsubscribe_link` | Whether to use a custom unsubscribe URL |
| `custom_unsubscribe_link` | Custom unsubscribe URL (if enabled) |
| `smtp_address` | SMTP server hostname |
| `smtp_port` | SMTP server port (typically 587 or 465) |
| `smtp_username` | SMTP username (returned in plain text) |
| `smtp_password` | SMTP password (redacted in responses) |
| `smtp_authentication` | Authentication method (plain, login, cram_md5) |
| `smtp_enable_starttls_auto` | Whether STARTTLS is automatically enabled |
| `aws_region` | AWS region for SES (e.g., us-east-1) |
| `aws_ses_configuration_set` | AWS SES configuration set name |
| `aws_access_key_id` | AWS access key for webhook auto-setup (redacted) |
| `aws_secret_access_key` | AWS secret key for webhook auto-setup (redacted) |
| `outbound_aws_access_key_id` | AWS access key for API delivery (redacted) |
| `outbound_aws_secret_access_key` | AWS secret key for API delivery (redacted) |
| `aws_setup_completed` | Whether AWS webhook auto-setup has been completed |
| `postmark_api_token` | Postmark API token (redacted) |
| `postmark_unsubscribe_handling` | Postmark only, read-only. The unsubscribe handling of the server’s `broadcast` message stream as Broadcast last read it: `Custom`, `Postmark`, `None`, or `missing` (the stream does not exist). `null` until Broadcast checks it. See [Postmark unsubscribe handling](#postmark-unsubscribe-handling). |
| `postmark_unsubscribe_handling_checked_at` | Postmark only, read-only. When Broadcast last read the handling (ISO 8601), or `null` |
| `postmark_unsubscribe_handling_error` | Postmark only, read-only. Why Postmark refused to switch the stream to `Custom` handling (Postmark’s own error text), or `null` |
| `postmark_subscription_sync` | Postmark only. When `true` (the default), an unsubscribe through Postmark’s own link unsubscribes the subscriber in Broadcast, and a resubscribe in Broadcast removes the Postmark suppression |
| `inboxroad_api_token` | Inboxroad API token (redacted) |
| `inboxroad_sync_enabled` | Whether Inboxroad bounce/complaint sync is enabled |
| `smtp_com_api_key` | SMTP.com API key (redacted) |
| `smtp_com_channel` | SMTP.com channel name |
| `smtp_com_callback_setup` | Whether SMTP.com callback has been configured |
| `email_headers` | Array of custom email headers (each with id, key, value) |
| `connection_failed_at` | Timestamp of last connection failure (null if healthy) |
| `created_at` | When the email server was created |
| `updated_at` | When the email server was last updated |
Security Note
For security, sensitive credentials (passwords, API tokens, secret keys) are redacted in API responses. You can set these values when creating or updating an email server, but they will never be returned in plain text.
Redacted fields include:
- smtp_password
- aws_secret_access_key
- outbound_aws_secret_access_key
- aws_access_key_id
- outbound_aws_access_key_id
- postmark_api_token
- inboxroad_api_token
- smtp_com_api_key
Supported Vendors
| Vendor | Value | Delivery Methods |
|---|---|---|
| AWS SES | `aws_ses` | SMTP, API |
| Postmark | `postmark` | SMTP, API |
| SendGrid | `sendgrid` | SMTP (API in the dashboard only) |
| Mailgun | `mailgun` | SMTP (API in the dashboard only) |
| Inboxroad | `inboxroad` | SMTP |
| Resend | `resend` | SMTP |
| SMTP.com | `smtp_com` | SMTP |
| Test Server | `test_server` | SMTP |
| Other | `other` | SMTP |
List Email Servers
GET /api/v1/email_servers
Retrieve a list of all email servers for the authenticated broadcast channel.
Query Parameters
limit(optional): Maximum number of email servers to return, from 1 to 1000offset(optional): Number of email servers to skip, 0 or more
A limit or offset outside those ranges, or that is not a whole number, returns 422 Unprocessable Entity.
Request
curl -X GET \ -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \ https://your-domain.com/api/v1/email_servers
Response
{ "data": [ { "id": 1, "label": "Production AWS SES", "vendor": "aws_ses", "delivery_method": "smtp", "active": true, "emails_per_hour": 1000, "use_for_broadcasts": true, "use_for_sequences": true, "use_for_transactionals": false, "include_unsubscribe_header": true, "include_unsubscribe_link": true, "use_custom_unsubscribe_link": false, "custom_unsubscribe_link": null, "smtp_address": "email-smtp.us-east-1.amazonaws.com", "smtp_port": 587, "smtp_username": "AKIAIOSFODNN7EXAMPLE", "smtp_password": "••••••••", "smtp_authentication": "plain", "smtp_enable_starttls_auto": true, "aws_region": "us-east-1", "aws_ses_configuration_set": "my-config-set", "aws_access_key_id": null, "aws_secret_access_key": null, "outbound_aws_access_key_id": null, "outbound_aws_secret_access_key": null, "aws_setup_completed": false, "postmark_api_token": null, "postmark_unsubscribe_handling": null, "postmark_unsubscribe_handling_checked_at": null, "postmark_unsubscribe_handling_error": null, "postmark_subscription_sync": true, "inboxroad_api_token": null, "inboxroad_sync_enabled": false, "smtp_com_api_key": null, "smtp_com_channel": null, "smtp_com_callback_setup": false, "email_headers": [], "connection_failed_at": null, "created_at": "2024-01-01T12:00:00Z", "updated_at": "2024-01-01T12:00:00Z" } ], "total": 1 }
Get Email Server
GET /api/v1/email_servers/:id
Retrieve details of a specific email server.
Request
curl -X GET \ -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \ https://your-domain.com/api/v1/email_servers/1
Response
{ "id": 1, "label": "Production AWS SES", "vendor": "aws_ses", "delivery_method": "smtp", "active": true, "emails_per_hour": 1000, "use_for_broadcasts": true, "use_for_sequences": true, "use_for_transactionals": false, "include_unsubscribe_header": true, "include_unsubscribe_link": true, "use_custom_unsubscribe_link": false, "custom_unsubscribe_link": null, "smtp_address": "email-smtp.us-east-1.amazonaws.com", "smtp_port": 587, "smtp_username": "AKIAIOSFODNN7EXAMPLE", "smtp_password": "••••••••", "smtp_authentication": "plain", "smtp_enable_starttls_auto": true, "aws_region": "us-east-1", "aws_ses_configuration_set": "my-config-set", "aws_access_key_id": null, "aws_secret_access_key": null, "outbound_aws_access_key_id": null, "outbound_aws_secret_access_key": null, "aws_setup_completed": false, "postmark_api_token": null, "postmark_unsubscribe_handling": null, "postmark_unsubscribe_handling_checked_at": null, "postmark_unsubscribe_handling_error": null, "postmark_subscription_sync": true, "inboxroad_api_token": null, "inboxroad_sync_enabled": false, "smtp_com_api_key": null, "smtp_com_channel": null, "smtp_com_callback_setup": false, "email_headers": [], "connection_failed_at": null, "created_at": "2024-01-01T12:00:00Z", "updated_at": "2024-01-01T12:00:00Z" }
Create Email Server
POST /api/v1/email_servers
Create a new email server configuration.
Required Parameters
label(required): Display name for the email serveremails_per_hour(required): Rate limit (minimum 15)- At least one of:
use_for_broadcasts,use_for_sequences,use_for_transactionalsmust be true
SMTP Parameters (required for SMTP delivery)
smtp_address: SMTP server hostnamesmtp_port: SMTP server portsmtp_username: SMTP usernamesmtp_password: SMTP passwordsmtp_authentication: Authentication method (plain, login, cram_md5)
Optional Parameters
vendor: Email provider (default:aws_ses). Always send it.delivery_method: smtp or api (default: smtp)active: Whether server is active (default: true)smtp_enable_starttls_auto: Enable STARTTLS (default: true)include_unsubscribe_header: Include List-Unsubscribe header (default: true)include_unsubscribe_link: Include unsubscribe link in emails (default: true)use_custom_unsubscribe_link: Use custom unsubscribe URLcustom_unsubscribe_link: Custom unsubscribe URLemail_headers_attributes: Array of custom email headers (see below)
Custom Email Headers
You can add custom email headers to be included with every email sent through this server:
{ "email_server": { "email_headers_attributes": [ { "key": "X-Custom-Header", "value": "custom-value" }, { "key": "X-Campaign-ID", "value": "spring-2024" } ] } }
To update existing headers, include the id. To remove a header, include _destroy: true:
{ "email_server": { "email_headers_attributes": [ { "id": 1, "key": "X-Custom-Header", "value": "updated-value" }, { "id": 2, "_destroy": true } ] } }
Vendor-Specific Parameters
AWS SES:
- aws_region: AWS region (e.g., us-east-1)
- aws_ses_configuration_set: SES configuration set name
- aws_access_key_id: For webhook auto-setup
- aws_secret_access_key: For webhook auto-setup
- outbound_aws_access_key_id: For API delivery method
- outbound_aws_secret_access_key: For API delivery method
Postmark:
- postmark_api_token: Required for API delivery method
- postmark_subscription_sync: true or false (default true). See Postmark unsubscribe handling.
SendGrid and Mailgun:
The API cannot set the SendGrid or Mailgun API credentials (sendgrid_api_key, mailgun_api_key, mailgun_domain, mailgun_region). It ignores them with an unrecognized_parameter warning. Create these servers with delivery_method: "smtp" and the provider’s SMTP credentials. An API-delivery server for SendGrid or Mailgun needs the dashboard.
Inboxroad:
- inboxroad_api_token: For bounce/complaint sync
- inboxroad_sync_enabled: Enable automatic sync
SMTP.com:
- smtp_com_api_key: For callback configuration
- smtp_com_channel: SMTP.com channel name
Example: Create SMTP Server (AWS SES)
curl -X POST \ -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "email_server": { "label": "Production AWS SES", "vendor": "aws_ses", "delivery_method": "smtp", "smtp_address": "email-smtp.us-east-1.amazonaws.com", "smtp_port": 587, "smtp_username": "AKIAIOSFODNN7EXAMPLE", "smtp_password": "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY", "smtp_authentication": "plain", "emails_per_hour": 1000, "use_for_broadcasts": true, "use_for_sequences": true, "use_for_transactionals": false, "aws_region": "us-east-1" } }' \ https://your-domain.com/api/v1/email_servers
Example: Create API Server (Postmark)
curl -X POST \ -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "email_server": { "label": "Postmark Transactional", "vendor": "postmark", "delivery_method": "api", "postmark_api_token": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", "emails_per_hour": 500, "use_for_broadcasts": false, "use_for_sequences": false, "use_for_transactionals": true } }' \ https://your-domain.com/api/v1/email_servers
Response
{ "id": 1, "label": "Production AWS SES", "vendor": "aws_ses", "delivery_method": "smtp", "active": true, "emails_per_hour": 1000, "use_for_broadcasts": true, "use_for_sequences": true, "use_for_transactionals": false, "include_unsubscribe_header": true, "include_unsubscribe_link": true, "use_custom_unsubscribe_link": false, "custom_unsubscribe_link": null, "smtp_address": "email-smtp.us-east-1.amazonaws.com", "smtp_port": 587, "smtp_username": "AKIAIOSFODNN7EXAMPLE", "smtp_password": "••••••••", "smtp_authentication": "plain", "smtp_enable_starttls_auto": true, "aws_region": "us-east-1", "aws_ses_configuration_set": null, "aws_access_key_id": null, "aws_secret_access_key": null, "outbound_aws_access_key_id": null, "outbound_aws_secret_access_key": null, "aws_setup_completed": false, "postmark_api_token": null, "postmark_unsubscribe_handling": null, "postmark_unsubscribe_handling_checked_at": null, "postmark_unsubscribe_handling_error": null, "postmark_subscription_sync": true, "inboxroad_api_token": null, "inboxroad_sync_enabled": false, "smtp_com_api_key": null, "smtp_com_channel": null, "smtp_com_callback_setup": false, "email_headers": [], "connection_failed_at": null, "created_at": "2024-01-01T12:00:00Z", "updated_at": "2024-01-01T12:00:00Z" }
Update Email Server
PATCH /api/v1/email_servers/:id
Update an existing email server configuration.
Request
curl -X PATCH \ -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "email_server": { "label": "Updated Server Name", "emails_per_hour": 2000 } }' \ https://your-domain.com/api/v1/email_servers/1
Response
{ "id": 1, "label": "Updated Server Name", "vendor": "aws_ses", "delivery_method": "smtp", "active": true, "emails_per_hour": 2000, "use_for_broadcasts": true, "use_for_sequences": true, "use_for_transactionals": false, "include_unsubscribe_header": true, "include_unsubscribe_link": true, "use_custom_unsubscribe_link": false, "custom_unsubscribe_link": null, "smtp_address": "email-smtp.us-east-1.amazonaws.com", "smtp_port": 587, "smtp_username": "AKIAIOSFODNN7EXAMPLE", "smtp_password": "••••••••", "smtp_authentication": "plain", "smtp_enable_starttls_auto": true, "aws_region": "us-east-1", "aws_ses_configuration_set": null, "aws_access_key_id": null, "aws_secret_access_key": null, "outbound_aws_access_key_id": null, "outbound_aws_secret_access_key": null, "aws_setup_completed": false, "postmark_api_token": null, "postmark_unsubscribe_handling": null, "postmark_unsubscribe_handling_checked_at": null, "postmark_unsubscribe_handling_error": null, "postmark_subscription_sync": true, "inboxroad_api_token": null, "inboxroad_sync_enabled": false, "smtp_com_api_key": null, "smtp_com_channel": null, "smtp_com_callback_setup": false, "email_headers": [], "connection_failed_at": null, "created_at": "2024-01-01T12:00:00Z", "updated_at": "2024-01-01T13:00:00Z" }
Postmark unsubscribe handling
Postmark adds its own unsubscribe link to a broadcast when the server’s broadcast message stream has Postmark unsubscribe handling. Broadcast checks the stream when you connect the server and when you click Check now on the email server page. If the stream has Postmark handling, Broadcast asks Postmark to switch it to Custom. Postmark allows Custom only for accounts it approved. The three postmark_unsubscribe_handling fields show the result. They are read-only: a value you send is ignored with a parameter_ignored warning.
postmark_subscription_sync keeps the two unsubscribe lists in agreement while Postmark handling is in effect. Set it like any other field:
curl -X PATCH \ -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "email_server": { "postmark_subscription_sync": false } }' \ https://your-domain.com/api/v1/email_servers/1
- A value that is not
trueorfalsereturns422 Unprocessable Entityand changes nothing. - If Broadcast set up the server’s Postmark webhooks (the server was connected with a Postmark account token), a change also turns Postmark’s
SubscriptionChangewebhook on or off, as the switch on the email server page does. If Postmark refuses, the change is still saved and the response has aprovider_setup_failedwarning with Postmark’s error.
Delete Email Server
DELETE /api/v1/email_servers/:id
Delete an email server configuration.
Request
curl -X DELETE \ -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \ https://your-domain.com/api/v1/email_servers/1
Response
{ "message": "Email server deleted successfully" }
Test Connection
POST /api/v1/email_servers/:id/test_connection
Test the SMTP connection for an email server. This will attempt to connect to the SMTP server and verify credentials. If successful, the server will be marked as active. If it fails, the server will be marked as inactive with a connection_failed_at timestamp.
Request
curl -X POST \ -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \ https://your-domain.com/api/v1/email_servers/1/test_connection
Success Response
{ "success": true, "message": "Connection successful" }
Failure Response (422 Unprocessable Entity)
{ "success": false, "message": "Connection failed" }
Copy to Channel
POST /api/v1/email_servers/:id/copy_to_channel
Copies an email server’s full configuration into another channel, creating a new server there. Useful when you run several channels through the same provider and do not want to re-enter credentials for each one.
Warning
This endpoint requires an admin API token. A channel token is scoped to a single channel and cannot write to another one, so it gets 403 Forbidden with Admin API token required for cross-channel operations. See API Authentication.
Parameters
| Parameter | Required | Description |
|---|---|---|
target_channel_id |
Yes | The channel to copy the server into |
broadcast_channel_id |
Yes | The channel that owns the source server, as with every admin-token request |
Request
curl -X POST \ -H "Authorization: Bearer YOUR_ADMIN_API_TOKEN" \ -H "Content-Type: application/json" \ -d '{"target_channel_id": 4}' \ "https://your-broadcast-domain.com/api/v1/email_servers/1/copy_to_channel?broadcast_channel_id=3"
Response
Returns 201 Created with the newly created email server, which has its own id and belongs to the target channel. The source server is left untouched.
Errors
| Status | Condition |
|---|---|
403 Forbidden |
Called with a channel token rather than an admin token |
404 Not Found |
target_channel_id does not match a channel you can reach |
422 Unprocessable Entity |
The target channel is the same as the source channel, or the copy failed validation |
Error Responses
401 Unauthorized
{ "error": "Unauthorized" }
Returned when the API token is missing, invalid, or lacks the required permissions.
404 Not Found
{ "error": "Email server not found" }
Returned when the specified email server doesn’t exist or belongs to a different channel.
422 Unprocessable Entity
{ "error": "Label can't be blank, At least one use case (broadcasts, sequences, or transactionals) must be selected" }
Returned when validation fails. Common validation errors include: - Missing required fields (label, SMTP credentials for SMTP delivery) - No use case selected - Invalid emails_per_hour (must be at least 15) - Missing API credentials when using API delivery method
Example: Complete Setup Flow
Here’s an example of setting up an email server and testing it:
# 1. Create the email server curl -X POST \ -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "email_server": { "label": "SendGrid Production", "vendor": "sendgrid", "smtp_address": "smtp.sendgrid.net", "smtp_port": 587, "smtp_username": "apikey", "smtp_password": "SG.xxxxxxxxxxxxxxxxxxxx", "smtp_authentication": "plain", "emails_per_hour": 5000, "use_for_broadcasts": true, "use_for_sequences": true, "use_for_transactionals": true } }' \ https://your-domain.com/api/v1/email_servers # Response: { "id": 5, ... } # 2. Test the connection curl -X POST \ -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \ https://your-domain.com/api/v1/email_servers/5/test_connection # Response: { "success": true, "message": "Connection successful" } # 3. Verify the server is active curl -X GET \ -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \ https://your-domain.com/api/v1/email_servers/5 # Response shows active: true and connection_failed_at: null