Posts API

Publish newsletter posts from your CMS, blog or automation: create a post, then send it to your subscribers right away or at a time you choose.

A post created through the API is the same as one written in the editor: it appears under Posts in your account, where you can still edit, reschedule or cancel it until sending starts. Posts are written in the name of the newsletter's owner.

Before you start

Where to find it
API key and API secretIn your obanox account under Account → API. See Authentication.
Your newsletter's slugSee Your newsletter's slug.
A newsletter ready to sendOnly needed to send or schedule, not for drafts: a confirmed email address, an imprint and a sender address in the settings, and a plan that allows sending.

Create a post

POST https://obanox.com/api/v1/{slug}/posts

Headers

HeaderValue
Content-Typeapplication/json
API-KEYYour API key
API-SECRETYour API secret

Body fields

FieldTypeRequiredDescription
subjectstringyesTitle of the post and subject line of the email. Max. 120 characters, single line.
htmlstringyesContent of the post as HTML. Scripts, styles and unsafe attributes are removed; links must use http, https or mailto.
subtitlestringnoShort teaser shown below the title. Max. 255 characters, single line.
template_idinteger or nullnoThe template the email is sent in. Left out: your newsletter's default template. null: the built-in layout.
use_templatebooleannoDefault true. false sends html as a complete email in your own design, without any template. Can't be combined with template_id.
web_visiblebooleannoWhether the post is published on your newsletter website once sent. Default true.
sendbooleannotrue sends the post right away.
send_atstringnoSend at this time instead. ISO 8601 with time zone, e.g. 2026-11-02T09:00:00+01:00 or 2026-11-02T08:00:00Z. Must be in the future.

Without send or send_at the post is saved as a draft, to be reviewed and sent in your account. Use either send or send_at, not both.

{
  "subject": "Our November update",
  "subtitle": "New features and three tips for the holidays",
  "html": "<p>Hello,</p><p>here is what is new this month …</p>",
  "template_id": 12,
  "send_at": "2026-11-02T09:00:00+01:00"
}

Response

201 Created: the post was saved, and scheduled if you asked to send it.

{
  "id": 4711,
  "status": "scheduled",
  "slug": "our-november-update",
  "use_template": true,
  "template_id": 12,
  "send_after": "2026-11-02T09:00:00+01:00"
}
statusMeaning
draftSaved, not sent. Send it from your account.
scheduledWill be sent at send_after. With send: true that is now, and sending starts within a minute.
sendingBeing sent to your subscribers.
sentSent to all subscribers.

Examples

curl -X POST "https://obanox.com/api/v1/my-newsletter/posts" \
  -H "Content-Type: application/json" \
  -H "API-KEY: your-api-key" \
  -H "API-SECRET: your-api-secret" \
  -d '{
    "subject": "Our November update",
    "html": "<p>Here is what is new this month …</p>",
    "send": true
  }'
<?php
$payload = [
    'subject' => 'Our November update',
    'html'    => '<p>Here is what is new this month …</p>',
    'send'    => true,
];

$ch = curl_init('https://obanox.com/api/v1/my-newsletter/posts');
curl_setopt_array($ch, [
    CURLOPT_POST           => true,
    CURLOPT_POSTFIELDS     => json_encode($payload),
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_TIMEOUT        => 10,
    CURLOPT_HTTPHEADER     => [
        'Content-Type: application/json',
        'API-KEY: ' . getenv('OBANOX_API_KEY'),
        'API-SECRET: ' . getenv('OBANOX_API_SECRET'),
    ],
]);
$body = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
curl_close($ch);

if ($status !== 201) {
    $error = json_decode($body, true)['error'] ?? $body;
    // handle the error, see "Errors"
}
const response = await fetch('https://obanox.com/api/v1/my-newsletter/posts', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'API-KEY': process.env.OBANOX_API_KEY,
    'API-SECRET': process.env.OBANOX_API_SECRET,
  },
  body: JSON.stringify({
    subject: 'Our November update',
    html: '<p>Here is what is new this month …</p>',
    send: true,
  }),
});

if (response.status !== 201) {
  const { error } = await response.json();
  // handle the error, see "Errors"
}
import os
import requests

response = requests.post(
    "https://obanox.com/api/v1/my-newsletter/posts",
    headers={
        "API-KEY": os.environ["OBANOX_API_KEY"],
        "API-SECRET": os.environ["OBANOX_API_SECRET"],
    },
    json={
        "subject": "Our November update",
        "html": "<p>Here is what is new this month …</p>",
        "send": True,
    },
    timeout=10,
)

if response.status_code != 201:
    error = response.json().get("error")
    # handle the error, see "Errors"

Check a post

GET https://obanox.com/api/v1/{slug}/posts/{id}

Same headers as above, without a body. Returns the post's current state:

{
  "id": 4711,
  "status": "sent",
  "subject": "Our November update",
  "slug": "our-november-update",
  "use_template": true,
  "template_id": 12,
  "send_after": "2026-11-02T09:00:00+01:00",
  "published_at": "2026-11-02T09:00:12+01:00",
  "total_recipients": 1250,
  "total_sent": 1250,
  "send_error": null,
  "url": "https://my-newsletter.obanox.com/our-november-update"
}
  • send_error: why the post could not be sent yet, e.g. a limit of your plan; null otherwise. Fix the cause in your account; the post is sent once it can be.
  • url: the post on your newsletter website, once sending started and if web_visible is on; null otherwise.

Templates

Every post is sent in a template: an HTML design with your header, footer and unsubscribe link. Templates are created in your account under Templates, where you can also choose the default for new posts. Without one of your own, posts use the built-in layout with your logo and accent color.

GET https://obanox.com/api/v1/{slug}/templates

Lists your newsletter's templates. Use an id as template_id when creating a post.

[
  { "id": 12, "name": "Monthly update", "default": true },
  { "id": 15, "name": "Short announcement", "default": false }
]

Your own design

If your website already builds the complete email, send it with "use_template": false. The html is then sent exactly as you send it: a full HTML document with <head>, <style> and inline styles. Only <script> blocks are removed.

  • Required: an unsubscribe link with the placeholder {{ unsubscribe_url }}, e.g. <a href="{{ unsubscribe_url }}">Unsubscribe</a>. It is replaced with each subscriber's personal link.
  • Placeholders work as in templates: {{ first_name }}, {{ last_name }}, {{ email }}, {{ web_url }} (view in browser), {{ newsletter_name }}, {{ newsletter_imprint }} and more.
  • Tracking (if enabled for your newsletter) works as usual: links are tracked and an open pixel is added.
  • On your newsletter website the post is shown in the website's design: only the content of <body> is used, while head, title, styles and anything unsafe are removed. The post subject is the page heading, so headings in your html move down one level (<h1> becomes <h2>; <h6> stays <h6>), and the first <h1> is left out if it repeats the subject. Hidden elements such as a preheader (display:none) are not shown.
  • Such posts can be previewed, test-sent and cancelled in your account, but not edited in the post editor.
{
  "subject": "Our November update",
  "use_template": false,
  "html": "<!doctype html><html><head><style>h1 { color: #e4572e; }</style></head><body><h1>Hello {{first_name}}</h1><p>…</p><a href=\"{{unsubscribe_url}}\">Unsubscribe</a></body></html>",
  "send": true
}

Errors

Errors return a JSON body with an error message. When a post can't be sent, problems lists every reason:

{
  "error": "The post cannot be sent.",
  "problems": [
    "Please enter your imprint in the settings first."
  ]
}
StatuserrorWhat it meansWhat to do
401Invalid api-key or api-secret.Headers missing or wrong.Check your credentials. Send them as API-KEY / API-SECRET (with a hyphen).
403Access to this list denied.The slug doesn't exist or belongs to another account.Check the slug.
404Post not found.No post with this id in this newsletter.Check the id and slug.
413The request is too large.The body is over 1 MB.Link to images instead of embedding them.
422The body must be a JSON object.The body isn't valid JSON.Send a JSON object with Content-Type: application/json.
422Missing or invalid fields: …The listed fields are missing, invalid, too long or contain line breaks. html is also refused when nothing is left of it after cleaning, template_id when it isn't a template of this newsletter.Fix the listed fields.
422html must contain {{ unsubscribe_url }} when use_template is false.Your own design has no unsubscribe link.Add a link to {{ unsubscribe_url }}.
422send_at must be in the future.The time has passed.Use send: true to send now.
422The post cannot be sent.Sending was requested, but the newsletter isn't ready (see problems). Nothing was saved.Fix the listed problems in your account, then repeat the request, or leave out send/send_at to save a draft.
5xx–Temporary problem on our side.Retry after a short wait, see Good practice.

Good practice

  • Avoid duplicate posts. Each successful request creates a new post. If a request times out, check under Posts in your account before retrying, or the post may be sent twice.
  • Draft first when unsure. Leave out send and send_at to review the post in your account and send a test email before it goes out.
  • Host your images. Reference images by their https URL in the HTML.

Limits

Request size1 MB
TemplatesUp to 10 per newsletter; or your own design with "use_template": false
Cover imageNot supported over the API; add it in your account
Emails sentAccording to your plan

Support

Questions? Contact obanox support and include the post id from the API response.