curl --request POST \
--url https://app.lettr.com/api/campaigns \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"name": "October newsletter",
"subject": "News for October",
"from_email": "news@example.com",
"from_name": "Example",
"reply_to": "jsmith@example.com",
"html_content": "<p>Hello {{first_name}}</p>",
"json_content": {},
"template_id": 123,
"audience_config": {
"all_contacts": true,
"list_ids": [
"<string>"
],
"segment_ids": [
"<string>"
],
"topic_id": "<string>"
},
"language_overrides": {}
}
'import requests
url = "https://app.lettr.com/api/campaigns"
payload = {
"name": "October newsletter",
"subject": "News for October",
"from_email": "news@example.com",
"from_name": "Example",
"reply_to": "jsmith@example.com",
"html_content": "<p>Hello {{first_name}}</p>",
"json_content": {},
"template_id": 123,
"audience_config": {
"all_contacts": True,
"list_ids": ["<string>"],
"segment_ids": ["<string>"],
"topic_id": "<string>"
},
"language_overrides": {}
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
name: 'October newsletter',
subject: 'News for October',
from_email: 'news@example.com',
from_name: 'Example',
reply_to: 'jsmith@example.com',
html_content: '<p>Hello {{first_name}}</p>',
json_content: {},
template_id: 123,
audience_config: {
all_contacts: true,
list_ids: ['<string>'],
segment_ids: ['<string>'],
topic_id: '<string>'
},
language_overrides: {}
})
};
fetch('https://app.lettr.com/api/campaigns', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://app.lettr.com/api/campaigns",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'name' => 'October newsletter',
'subject' => 'News for October',
'from_email' => 'news@example.com',
'from_name' => 'Example',
'reply_to' => 'jsmith@example.com',
'html_content' => '<p>Hello {{first_name}}</p>',
'json_content' => [
],
'template_id' => 123,
'audience_config' => [
'all_contacts' => true,
'list_ids' => [
'<string>'
],
'segment_ids' => [
'<string>'
],
'topic_id' => '<string>'
],
'language_overrides' => [
]
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://app.lettr.com/api/campaigns"
payload := strings.NewReader("{\n \"name\": \"October newsletter\",\n \"subject\": \"News for October\",\n \"from_email\": \"news@example.com\",\n \"from_name\": \"Example\",\n \"reply_to\": \"jsmith@example.com\",\n \"html_content\": \"<p>Hello {{first_name}}</p>\",\n \"json_content\": {},\n \"template_id\": 123,\n \"audience_config\": {\n \"all_contacts\": true,\n \"list_ids\": [\n \"<string>\"\n ],\n \"segment_ids\": [\n \"<string>\"\n ],\n \"topic_id\": \"<string>\"\n },\n \"language_overrides\": {}\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://app.lettr.com/api/campaigns")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"name\": \"October newsletter\",\n \"subject\": \"News for October\",\n \"from_email\": \"news@example.com\",\n \"from_name\": \"Example\",\n \"reply_to\": \"jsmith@example.com\",\n \"html_content\": \"<p>Hello {{first_name}}</p>\",\n \"json_content\": {},\n \"template_id\": 123,\n \"audience_config\": {\n \"all_contacts\": true,\n \"list_ids\": [\n \"<string>\"\n ],\n \"segment_ids\": [\n \"<string>\"\n ],\n \"topic_id\": \"<string>\"\n },\n \"language_overrides\": {}\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://app.lettr.com/api/campaigns")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"name\": \"October newsletter\",\n \"subject\": \"News for October\",\n \"from_email\": \"news@example.com\",\n \"from_name\": \"Example\",\n \"reply_to\": \"jsmith@example.com\",\n \"html_content\": \"<p>Hello {{first_name}}</p>\",\n \"json_content\": {},\n \"template_id\": 123,\n \"audience_config\": {\n \"all_contacts\": true,\n \"list_ids\": [\n \"<string>\"\n ],\n \"segment_ids\": [\n \"<string>\"\n ],\n \"topic_id\": \"<string>\"\n },\n \"language_overrides\": {}\n}"
response = http.request(request)
puts response.read_body{
"message": "Campaign created successfully.",
"data": {
"id": "0193e6a8-1f3a-7c2a-b9e2-1aa1d2e5d3f0",
"name": "Spring Sale",
"status": "sent",
"sent_count": 124,
"created_at": "2026-05-01T09:00:00+00:00",
"stats": {
"injections": 0,
"deliveries": 0,
"bounces": 0,
"spam_complaints": 0,
"opens": 0,
"unique_opens": 0,
"clicks": 0,
"unique_clicks": 0,
"unsubscribes": 0
},
"subject": "<string>",
"from_email": "jsmith@example.com",
"from_name": "<string>",
"reply_to": "jsmith@example.com",
"scheduled_at": "2023-11-07T05:31:56Z",
"total_recipients": 123,
"sent_at": "2023-11-07T05:31:56Z",
"html_content": "<string>",
"language_overrides": {}
}
}Create a campaign
Creates a draft campaign. Give the content as html_content (optionally with the editor’s json_content) or as a template_id, whose HTML is copied onto the campaign. Every list, segment, topic and template must belong to your team. The sender domain is checked when the campaign is sent, not here; language override senders are checked here and must use a verified sending domain. Send or schedule the draft with the send and schedule endpoints. Requires the campaigns:write scope. Not available to sandbox keys.
curl --request POST \
--url https://app.lettr.com/api/campaigns \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"name": "October newsletter",
"subject": "News for October",
"from_email": "news@example.com",
"from_name": "Example",
"reply_to": "jsmith@example.com",
"html_content": "<p>Hello {{first_name}}</p>",
"json_content": {},
"template_id": 123,
"audience_config": {
"all_contacts": true,
"list_ids": [
"<string>"
],
"segment_ids": [
"<string>"
],
"topic_id": "<string>"
},
"language_overrides": {}
}
'import requests
url = "https://app.lettr.com/api/campaigns"
payload = {
"name": "October newsletter",
"subject": "News for October",
"from_email": "news@example.com",
"from_name": "Example",
"reply_to": "jsmith@example.com",
"html_content": "<p>Hello {{first_name}}</p>",
"json_content": {},
"template_id": 123,
"audience_config": {
"all_contacts": True,
"list_ids": ["<string>"],
"segment_ids": ["<string>"],
"topic_id": "<string>"
},
"language_overrides": {}
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
name: 'October newsletter',
subject: 'News for October',
from_email: 'news@example.com',
from_name: 'Example',
reply_to: 'jsmith@example.com',
html_content: '<p>Hello {{first_name}}</p>',
json_content: {},
template_id: 123,
audience_config: {
all_contacts: true,
list_ids: ['<string>'],
segment_ids: ['<string>'],
topic_id: '<string>'
},
language_overrides: {}
})
};
fetch('https://app.lettr.com/api/campaigns', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://app.lettr.com/api/campaigns",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'name' => 'October newsletter',
'subject' => 'News for October',
'from_email' => 'news@example.com',
'from_name' => 'Example',
'reply_to' => 'jsmith@example.com',
'html_content' => '<p>Hello {{first_name}}</p>',
'json_content' => [
],
'template_id' => 123,
'audience_config' => [
'all_contacts' => true,
'list_ids' => [
'<string>'
],
'segment_ids' => [
'<string>'
],
'topic_id' => '<string>'
],
'language_overrides' => [
]
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://app.lettr.com/api/campaigns"
payload := strings.NewReader("{\n \"name\": \"October newsletter\",\n \"subject\": \"News for October\",\n \"from_email\": \"news@example.com\",\n \"from_name\": \"Example\",\n \"reply_to\": \"jsmith@example.com\",\n \"html_content\": \"<p>Hello {{first_name}}</p>\",\n \"json_content\": {},\n \"template_id\": 123,\n \"audience_config\": {\n \"all_contacts\": true,\n \"list_ids\": [\n \"<string>\"\n ],\n \"segment_ids\": [\n \"<string>\"\n ],\n \"topic_id\": \"<string>\"\n },\n \"language_overrides\": {}\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://app.lettr.com/api/campaigns")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"name\": \"October newsletter\",\n \"subject\": \"News for October\",\n \"from_email\": \"news@example.com\",\n \"from_name\": \"Example\",\n \"reply_to\": \"jsmith@example.com\",\n \"html_content\": \"<p>Hello {{first_name}}</p>\",\n \"json_content\": {},\n \"template_id\": 123,\n \"audience_config\": {\n \"all_contacts\": true,\n \"list_ids\": [\n \"<string>\"\n ],\n \"segment_ids\": [\n \"<string>\"\n ],\n \"topic_id\": \"<string>\"\n },\n \"language_overrides\": {}\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://app.lettr.com/api/campaigns")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"name\": \"October newsletter\",\n \"subject\": \"News for October\",\n \"from_email\": \"news@example.com\",\n \"from_name\": \"Example\",\n \"reply_to\": \"jsmith@example.com\",\n \"html_content\": \"<p>Hello {{first_name}}</p>\",\n \"json_content\": {},\n \"template_id\": 123,\n \"audience_config\": {\n \"all_contacts\": true,\n \"list_ids\": [\n \"<string>\"\n ],\n \"segment_ids\": [\n \"<string>\"\n ],\n \"topic_id\": \"<string>\"\n },\n \"language_overrides\": {}\n}"
response = http.request(request)
puts response.read_body{
"message": "Campaign created successfully.",
"data": {
"id": "0193e6a8-1f3a-7c2a-b9e2-1aa1d2e5d3f0",
"name": "Spring Sale",
"status": "sent",
"sent_count": 124,
"created_at": "2026-05-01T09:00:00+00:00",
"stats": {
"injections": 0,
"deliveries": 0,
"bounces": 0,
"spam_complaints": 0,
"opens": 0,
"unique_opens": 0,
"clicks": 0,
"unique_clicks": 0,
"unsubscribes": 0
},
"subject": "<string>",
"from_email": "jsmith@example.com",
"from_name": "<string>",
"reply_to": "jsmith@example.com",
"scheduled_at": "2023-11-07T05:31:56Z",
"total_recipients": 123,
"sent_at": "2023-11-07T05:31:56Z",
"html_content": "<string>",
"language_overrides": {}
}
}html_content or a template_id. Send it with send a campaign now or schedule a campaign. Requires the campaigns:write scope. Not available to sandbox keys.Authorizations
API key for authentication
Body
Campaign name. Required unless subject is given, in which case the subject is used.
255"October newsletter"
998"News for October"
255"news@example.com"
255"Example"
255Email HTML. Merge tags such as {{first_name}} are filled from contact properties. Cannot be combined with template_id; required when json_content is given.
"<p>Hello {{first_name}}</p>"
Editor JSON stored next to html_content so the campaign can be opened in the editor.
Template from one of your projects. Its HTML is copied onto the campaign. Cannot be combined with html_content.
Who receives the campaign. Lists and segments are combined (a contact in any of them receives it); topic_id further limits recipients to contacts subscribed to that topic. With no lists or segments, every subscribed contact receives it.
Show child attributes
Show child attributes
Per-language sender and subject overrides, keyed by language code.
Show child attributes
Show child attributes
Was this page helpful?