Skip to content

Lists & recipients ​

A recipient is one postal address; a list is a named group of them. Campaigns send to a list — one you built, or one created for you from recipients you passed in.

The five required fields ​

All five are required — they are what makes the mail deliverable.

FieldMaxNotes
name200Person or organisation
street255Street address line
postCode20Postal code
postTown100Town or city
country100Country

A row missing name, street, postCode or postTown cannot be posted. On a file import such rows are skipped and counted, not errors. Inline recipients and single adds instead fail validation — you named that recipient explicitly, so dropping it silently would be wrong.

Custom fields ​

Anything beyond the five is a custom field, filling a placeholder in the template.

json
{
  "customFields": {
    "vip": { "label": "VIP", "value": "yes" },
    "renewal_date": { "label": "Renewal date", "value": "2026-09-01" }
  }
}
  • The key is the placeholder name: alphanumeric plus underscore, ≤64 characters.
  • label is the human-readable name; value is ≤512 characters.
  • Maximum 16 custom fields per recipient.

The field schema, and why it locks ​

A list has one custom-field schema shared by every recipient. It starts unlocked and freezes on the first recipient added.

create list ─── status: "unlocked", fields: []
     │
     ├── first recipient / first import
     │     → the custom fields present define the schema
     │
     └─── status: "locked", fields: [...]  ← now fixed

Once locked, every later add must conform. Read it with GET /api/v1/lists/:listId/schema.

Always read the schema before importing into an existing list

It tells you which custom-field keys the list accepts. Mapping one that is not in the schema is a 422 invalid_mapping — the most common import failure.

Why lock at all? Every recipient is rendered through the same template. If half the list had a vip field and half did not, half the letters would print a gap. The schema guarantees the template can always be filled.

A failed or fully-skipped first import leaves the list unlocked: the schema is defined by data that landed, never by an attempt that stored nothing. So you can retry with a different mapping.

Missing-value policies ​

When the schema expects a custom field and a row omits it, that field's policy decides. Policies are set when the schema is defined — on the first add or import.

PolicyBehaviour when the value is missing
{ "kind": "skip" }Drop the row. Counted in skipped, not an error. Default
{ "kind": "fail" }Reject the whole import with 422
{ "kind": "default", "value": "..." }Substitute this value and keep the row

default requires a non-empty value.

Pass customFieldPolicies on a single add, customFieldMissingValuePolicy on the column's mapping entry for a file import, recipientPolicies for inline recipients at campaign create. All take the same { kind, value? } shape, and all are honoured only while the schema is unlocked.

Empty means missing

A custom field sent as "", or an empty spreadsheet cell, counts as missing, not blank — the policy applies. This keeps {"value": ""} and an absent key behaving identically instead of printing an empty line.

Building an audience ​

One at a time ​

POST /api/v1/recipients with a listId. Good for syncing individual signups.

From a file ​

CSV or Excel with a column mapping. Good for exports. See Recipient files.

Implicitly, at campaign time ​

Pass recipients or recipientsFile to POST /api/v1/campaigns and a list is created for you, named after the campaign. Simplest for a system that already owns the addresses. The list is real and reusable afterwards.

Reading recipients ​

NeedEndpoint
Everything in the accountGET /api/v1/recipients
One list's membersGET /api/v1/recipients/by-list/:listId
A list's schemaGET /api/v1/lists/:listId/schema
All lists with countsGET /api/v1/lists

Both recipient endpoints accept search, a case-insensitive substring match on name only — not on address or custom fields.

No update or delete ​

The API has no endpoints to modify or remove a recipient or a list. Audiences are append-only over the API. To correct an address, add a corrected recipient; to retire an audience, stop sending to it and let retention remove it.

Retention ​

A list is deleted after 90 days without activity. Activity means adding a recipient, importing, or creating a campaign that uses it — reading does not count.

When a list is removed, campaigns that used it are kept as historical records with listId and listName set to null. You never lose the record that mail was sent.

If an audience matters long-term, keep it in your own system as the source of truth and re-import when you send.

Next ​