Email Campaigns
SendSeven uses a 3-tier architecture for email campaigns that separates design from content. This guide walks you through the full workflow: choosing a template, creating content, and launching a campaign.
Required Scopes
| Scope | Purpose |
|---|---|
campaigns:read | View templates, content, campaigns, and analytics |
campaigns:create | Create templates, content, and campaigns |
campaigns:update | Edit campaign content/settings and schedule |
campaigns:send | Send, pause, resume, and cancel campaigns |
Architecture
EmailTemplate (MJML template with slots)
|
EmailContent (fills the slots with actual content)
|
EmailCampaign (campaign instance with recipients and schedule)
This approach lets you create reusable templates and populate them with different content for each campaign.
Pricing
Every plan is a fixed monthly base fee that includes a pool of usage shared across messages, campaign messages, KB queries, and campaign emails. Emails beyond that pool are billed per email:
| Plan | Base fee | Included pool / month | Per email beyond the pool |
|---|---|---|---|
| Basic | EUR 49 | 2,500 | EUR 0.010 |
| Professional | EUR 79 | 2,500 | EUR 0.010 |
| Scale | EUR 199 | 10,000 | EUR 0.0075 |
| Enterprise | EUR 499 | 40,000 | EUR 0.005 |
| API Only | EUR 9 per connected channel | 1,000 per connected channel | EUR 0.0025 |
Managed (SendSeven handles delivery) and BYOK (you connect your own SendGrid, Mailgun, or AWS SES account) cost the same per email — BYOK is about owning your sending reputation and provider relationship, not a lower rate.
The included pool is applied to your most expensive usage first, so emails — the cheapest unit — are the last to draw from it. See the pricing page for current rates.
Step 1: Email Templates
Email templates define the visual structure using MJML (a responsive email markup language). They contain named slots that are filled by email content.
List Templates
curl -X GET "https://api.sendseven.com/api/v1/email-templates" \
-H "Authorization: Bearer s7_api_a1b2c3d4e5f6789012345678abcdef00" \
-H "Content-Type: application/json"
{
"items": [
{
"id": "layout_newsletter_01",
"name": "Standard Newsletter",
"description": "Single-column newsletter with header image, body, and footer",
"slots": ["header_image", "headline", "body", "cta_text", "cta_url"],
"created_at": "2026-01-05T10:00:00Z"
}
]
}
Create a Template
curl -X POST "https://api.sendseven.com/api/v1/email-templates" \
-H "Authorization: Bearer s7_api_a1b2c3d4e5f6789012345678abcdef00" \
-H "Content-Type: application/json" \
-d '{
"name": "Simple Announcement",
"description": "Clean single-column announcement layout",
"mjml": "<mjml><mj-body><mj-section><mj-column><mj-image src=\"{{header_image}}\" /><mj-text>{{headline}}</mj-text><mj-text>{{body}}</mj-text><mj-button href=\"{{cta_url}}\">{{cta_text}}</mj-button></mj-column></mj-section></mj-body></mjml>"
}'
Step 2: Email Content
Email content fills the template's slots with actual text, images, and links.
Create Content
curl -X POST "https://api.sendseven.com/api/v1/email-contents" \
-H "Authorization: Bearer s7_api_a1b2c3d4e5f6789012345678abcdef00" \
-H "Content-Type: application/json" \
-d '{
"layout_id": "layout_newsletter_01",
"name": "March Newsletter Content",
"subject": "March highlights from Acme",
"sender_name": "Acme Team",
"sender_email": "[email protected]",
"slot_data": {
"header_image": "https://cdn.acme.com/images/march-header.jpg",
"headline": "March Highlights",
"body": "<p>Spring is here and so are new features! Check out what we have launched.</p>",
"cta_text": "See What Is New",
"cta_url": "https://acme.com/blog/march-2026"
}
}'
| Field | Type | Required | Description |
|---|---|---|---|
layout_id | string | Yes | Email template to use |
name | string | Yes | Internal name |
subject | string | Yes | Email subject line |
sender_name | string | Yes | Sender display name |
sender_email | string | Yes | Sender email address |
slot_data | object | Yes | Key-value pairs matching the template's slots |
Always use email_content_id when creating campaigns to reference your email content.
Searching email content
GET /api/v1/email-contents accepts a search query parameter that matches on content name and subject. When search is provided, pagination is skipped and the endpoint returns up to 50 matches across all pages in a single response — this keeps the Send-Message node's email-content picker snappy without forcing the caller to paginate.
curl -X GET "https://api.sendseven.com/api/v1/email-contents?search=march" \
-H "Authorization: Bearer s7_api_a1b2c3d4e5f6789012345678abcdef00"
For normal browse-style listing (no search parameter), the endpoint paginates as usual using page / page_size.
Step 3: Create and Send the Campaign
Create an Email Campaign
curl -X POST "https://api.sendseven.com/api/v1/email-campaigns" \
-H "Authorization: Bearer s7_api_a1b2c3d4e5f6789012345678abcdef00" \
-H "Content-Type: application/json" \
-d '{
"name": "March Newsletter",
"email_content_id": "content_mar_newsletter",
"list_ids": ["list_newsletter"],
"scheduled_at": "2026-03-01T08:00:00Z"
}'
| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Campaign name |
email_content_id | string | Yes | Email content to send |
list_ids | array | Yes | Contact list IDs |
scheduled_at | string | No | ISO 8601 datetime for scheduled delivery |
Send Immediately
curl -X POST "https://api.sendseven.com/api/v1/email-campaigns/ecamp_march_01/send" \
-H "Authorization: Bearer s7_api_a1b2c3d4e5f6789012345678abcdef00" \
-H "Content-Type: application/json"
Get Analytics
curl -X GET "https://api.sendseven.com/api/v1/email-campaigns/ecamp_march_01/analytics" \
-H "Authorization: Bearer s7_api_a1b2c3d4e5f6789012345678abcdef00" \
-H "Content-Type: application/json"
{
"campaign_id": "ecamp_march_01",
"sent": 12500,
"delivered": 12210,
"opened": 4884,
"clicked": 1587,
"bounced": 290,
"complained": 5,
"unsubscribed": 23,
"delivery_rate": 97.68,
"open_rate": 40.00,
"click_rate": 13.00,
"bounce_rate": 2.32,
"complaint_rate": 0.04
}
Complete Workflow (Python)
import requests
BASE_URL = "https://api.sendseven.com/api/v1"
HEADERS = {
"Authorization": "Bearer s7_api_a1b2c3d4e5f6789012345678abcdef00",
"Content-Type": "application/json",
}
# Step 1: Choose an email template
templates = requests.get(f"{BASE_URL}/email-templates", headers=HEADERS).json()
layout_id = templates["items"][0]["id"]
print(f"Using template: {layout_id}")
print(f"Available slots: {templates['items'][0]['slots']}")
# Step 2: Create email content
content = requests.post(
f"{BASE_URL}/email-contents",
headers=HEADERS,
json={
"layout_id": layout_id,
"name": "April Promo Content",
"subject": "Spring savings - 25% off this week only!",
"sender_name": "Acme Store",
"sender_email": "[email protected]",
"slot_data": {
"header_image": "https://cdn.acme.com/spring-sale.jpg",
"headline": "Spring Savings Event",
"body": "<p>Save 25% on all orders this week. Use code SPRING25.</p>",
"cta_text": "Shop Now",
"cta_url": "https://shop.acme.com/spring-sale",
},
},
).json()
print(f"Created content: {content['id']}")
# Step 3: Create and schedule the campaign
campaign = requests.post(
f"{BASE_URL}/email-campaigns",
headers=HEADERS,
json={
"name": "Spring Sale Email",
"email_content_id": content["id"],
"list_ids": ["list_newsletter", "list_active_customers"],
"scheduled_at": "2026-04-01T08:00:00Z",
},
).json()
print(f"Campaign scheduled: {campaign['id']} for {campaign['scheduled_at']}")
print(f"Recipients: {campaign['recipient_count']}")
JavaScript Example
const BASE_URL = "https://api.sendseven.com/api/v1";
const HEADERS = {
"Authorization": "Bearer s7_api_a1b2c3d4e5f6789012345678abcdef00",
"Content-Type": "application/json",
};
// Create email content
const contentRes = await fetch(`${BASE_URL}/email-contents`, {
method: "POST",
headers: HEADERS,
body: JSON.stringify({
layout_id: "layout_newsletter_01",
name: "Product Update Email",
subject: "New feature: Automated Workflows",
sender_name: "Acme Product Team",
sender_email: "[email protected]",
slot_data: {
header_image: "https://cdn.acme.com/workflows.jpg",
headline: "Introducing Automated Workflows",
body: "<p>Set up triggers, conditions, and actions to automate your messaging.</p>",
cta_text: "Learn More",
cta_url: "https://acme.com/blog/automated-workflows",
},
}),
});
const content = await contentRes.json();
// Create and send the campaign
const campaignRes = await fetch(`${BASE_URL}/email-campaigns`, {
method: "POST",
headers: HEADERS,
body: JSON.stringify({
name: "Workflows Announcement",
email_content_id: content.id,
list_ids: ["list_newsletter"],
}),
});
const campaign = await campaignRes.json();
// Send immediately
await fetch(`${BASE_URL}/email-campaigns/${campaign.id}/send`, {
method: "POST",
headers: HEADERS,
});
console.log("Campaign is sending!");
Error Responses
| Status | Error Code | Description |
|---|---|---|
| 401 | INVALID_TOKEN | Token is invalid or expired |
| 403 | INSUFFICIENT_SCOPE | Token lacks required campaigns scope |
| 404 | RESOURCE_NOT_FOUND | Template, content, or campaign not found |
| 409 | CONFLICT | Cannot modify a campaign that is already sending |
| 422 | VALIDATION_ERROR | slot_data does not match template slots |
Next Steps
- Messaging Campaigns -- channel-based campaigns
- Email Channel -- email channel setup
- Tags & Lists -- manage recipient lists