Docs
API reference Sending Snippets

Snippets

Snippets are values that any template, block or subject line can use, such as the next webinar’s title, date and registration link. An email reads them with Liquid when it sends:

{{ snippets.webinar.title }}

The root is always snippets, plural. Changing a snippet therefore changes every email that hasn’t sent yet. That includes draft and scheduled broadcasts and library emails already pinned to a sequence step, with no re-save and no new version.

A set (webinar) defines the fields (title, date, url, …) and how its snippets take turns. Each snippet in a set is an entry in this API: one row of values. One entry is current at any moment, or none is:

rotationWhich entry is current
singleIts one entry. Change it by hand.
next_upcomingThe entry whose order_field date comes next. It rolls off when its date passes, and the next one takes over. Use it for webinars and events.
scheduledThe latest entry whose starts_at has passed. It shows until the next one starts. Use it for a featured guide or a monthly offer.

Values lock when a send starts. A broadcast reads them once, when it starts sending, and every recipient gets those values, including every A/B variant and a split test’s winner. Each later day of a spread send locks its own values when that day goes out. A sequence email locks them when that email sends. View in browser shows the values the recipient received.

Required scopes#

Snippets ride the template scopes, as blocks do.

EndpointScope
GET /api/snippetstemplates:read
GET /api/snippets/{set}templates:read
GET /api/snippets/resolvetemplates:read
POST /api/snippetstemplates:write
PATCH /api/snippets/{set}templates:write
DELETE /api/snippets/{set}templates:write
POST /api/snippets/{set}/entriestemplates:write
PUT /api/snippets/{set}/entriestemplates:write
PATCH /api/snippets/{set}/entries/{id}templates:write
DELETE /api/snippets/{set}/entries/{id}templates:write

{set} is the set’s UUID or its key.

Fields and values#

Each field has a key (its Liquid name), a label and a type:

typeValue you sendWhat Liquid reads
text, multiline_texta stringthe string, HTML-escaped in the body
rich_textan HTML stringthe HTML, unescaped in the body
url, imagean http(s) URLthe URL
numbera numberthe number
booleantrue or falsethe boolean
dateYYYY-MM-DDYYYY-MM-DD
datetimean ISO-8601 instant, or YYYY-MM-DDTHH:MM in the account’s time zonethe instant written in the account’s UTC offset, e.g. 2026-10-22T13:00:00-04:00

A datetime prints in the account’s time zone with the date filter: {{ snippets.webinar.date | date: "%a %b %-d, %-I:%M %p" }} reads Thu Oct 22, 1:00 PM. Keys are lowercase letters, digits and _, starting with a letter. A field you leave out of an entry renders empty.

Limits: 50 sets per account, 30 fields per set, 200 entries per set, 10 KB per value.

Create a set#

POST /api/snippets
{
  "name": "Webinar",
  "key": "webinar",
  "rotation": "next_upcoming",
  "order_field": "date",
  "fields": [
    { "key": "title", "label": "Title", "type": "text" },
    { "key": "description", "label": "Description", "type": "rich_text" },
    { "key": "date", "label": "Webinar date", "type": "datetime" },
    { "key": "url", "label": "Registration link", "type": "url" }
  ]
}

Returns 201 with the set. A key already in use returns 409 KEY_TAKEN.

List and read sets#

GET /api/snippets

Each set comes with current (the entry showing now, or null), upcoming_count, and runs_out_at: when the last lined-up entry of a next_upcoming set rolls off, after which nothing is current.

GET /api/snippets/{set}

The set with every entry, in queue order. Each entry has values, shows_from / shows_until (its window), state (past, current or upcoming) and updated_by (the user or API key that last changed it).

Change or delete a set#

PATCH /api/snippets/{set}

Send any of name, fields, rotation, order_field, key. Once the set has entries, its shape is locked except for names: you can rename it, relabel fields and add new ones, but changing the key, the rotation, or an existing field’s key or type returns 409 SET_LOCKED.

DELETE /api/snippets/{set}?force=true

Refused with 409 HAS_ENTRIES while the set has entries, unless force=true. Emails that read a deleted set render its fields empty.

Add an entry#

POST /api/snippets/{set}/entries
{
  "values": {
    "title": "Raising prices without losing customers",
    "date": "2026-10-22T13:00",
    "url": "https://example.com/webinar/raising-prices"
  }
}

A next_upcoming entry is placed by its order_field value, which is required. A scheduled entry needs starts_at (an ISO instant, or YYYY-MM-DDTHH:MM in the account’s time zone). Two entries can’t share a place in the queue: a second entry at the same date or start returns 409 SLOT_TAKEN. A single set holds one entry: a second returns 409 ENTRY_EXISTS. Returns 201 with the entry.

Upsert an entry#

PUT /api/snippets/{set}/entries

Same body as adding one, plus an optional match_field. Use it from a sync job, such as a weekly task that loads the next webinar: running it twice doesn’t make a duplicate. Returns 201 when it added an entry and 200 when it replaced one.

  • With match_field, the key of a text or URL field such as a registration link or your own id, it replaces the entry whose value in that field matches. If the webinar was rescheduled, the entry moves to its new date instead of being added again. Prefer this for anything that can change its date.
  • Without it, it replaces the entry at the same place in the queue: the same date for next_upcoming, the same starts_at for scheduled, or the set’s one entry for single.
{
  "match_field": "url",
  "values": {
    "title": "Value metrics that scale",
    "date": "2026-12-03T13:00",
    "url": "https://example.com/webinar/value-metrics"
  }
}

Change or delete an entry#

PATCH /api/snippets/{set}/entries/{id}

values replaces the entry’s values as a whole: a field you leave out is cleared. starts_at moves a scheduled entry.

DELETE /api/snippets/{set}/entries/{id}

Emails already sent keep the values they were sent with.

Resolve at a moment#

GET /api/snippets/resolve?at=2026-12-10T15:00:00Z

What an email sent at at (default: now) would read as snippets:

{
  "at": "2026-12-10T15:00:00.000Z",
  "values": {
    "webinar": {
      "title": "Discounting without damage",
      "date": "2027-01-14T13:00:00-05:00",
      "url": "https://example.com/webinar/discounting"
    },
    "resource": null
  }
}

In an email#

Wrap a section in a check, so it disappears when nothing is current:

{% if snippets.webinar %}
  <h2>{{ snippets.webinar.title }}</h2>
  <a href="{{ snippets.webinar.url }}">Save my seat</a>
{% endif %}

In MJML, put the check in <mj-raw> tags around the section. MJML discards bare text between its elements, so a bare {% if %} there would be dropped and the section would always render:

<mj-raw>{% if snippets.webinar %}</mj-raw>
<mj-section>…</mj-section>
<mj-raw>{% endif %}</mj-raw>

Links in snippets are click-tracked like any other link.

Errors#

CodeStatusMeaning
INVALID_INPUT400A field definition or value failed validation. The message names it.
NOT_FOUND404No set (or entry) with that id or key.
KEY_TAKEN409Another set already uses this key.
SET_LOCKED409The change would strand stored entries or move the queue.
ENTRY_EXISTS409A single set already has its entry. Change it instead.
SLOT_TAKEN409Another entry already has this date (or start). Change it, or edit that entry.
HAS_ENTRIES409Delete with force=true, or delete the entries first.
LIMIT_REACHED400A per-account or per-set limit was hit.