eusend
Audiences

Contacts

Add and manage individual contacts within an audience. Contacts can also be imported via CSV in the dashboard.

Add and manage individual contacts within an audience. Contacts can also be imported via CSV in the dashboard.

POST/audiences/:id/contacts

Add a single contact to an audience. If the email already exists in the audience the record is updated (upsert).

ParameterTypeDescription
emailrequiredstringContact email address.
first_namestringContact first name.
last_namestringContact last name.
propertiesobjectCustom properties, usable as {{key}} in a broadcast body. Replaces the stored properties — see below.

Custom properties

Anything you store on a contact under properties becomes a variable in a broadcast, so a contact written with { "plan": "pro" } renders {{plan}} as pro. Combined with the fallback syntax, Hi {{first_name|there}}, you are on the {{plan|free}} plan covers the contacts that are missing the value.

The rules are deliberately narrow, because every value ends up substituted into an email:

  • Values are strings. A number or a date would need a formatting and locale decision that a merge tag has nowhere to express, so make it yourself and send the result.
  • Names are lowercase letters, digits and underscores, starting with a letter, at most 40 characters. Up to 20 properties per contact, each value at most 500 characters.
  • email, name, first_name, last_name and full_name are built in and are rejected as property names. {{email}} always resolves to the contact's real address.

POST replaces, PATCH replaces, an import merges. Sending properties to POST /audiences/:id/contacts or PATCH /audiences/:id/contacts/:contactId replaces the whole object, which is what lets you remove a key. The batch endpoint merges instead, so a CSV carrying only plan will not drop a company an earlier import set.

a contact with custom properties
curl -X POST https://api.eusend.dev/audiences/550e8400-e29b-41d4-a716-446655440000/contacts \
  -H "Authorization: Bearer eu_live_xxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "alice@example.com",
    "first_name": "Alice",
    "properties": { "plan": "pro", "company": "Acme", "renews_on": "3 March 2026" }
  }'
POST/audiences/:id/contacts/batch

Upsert up to 1,000 contacts at once. Existing contacts (matched by email) are updated; new ones are inserted. This is what the dashboard's CSV import calls.

ParameterTypeDescription
contactsrequiredarrayArray of contact objects. Each has email (required), plus optional first_name, last_name, properties, unsubscribed, and created_at.
contacts[].unsubscribedbooleanMarks the contact opted out, so a list migrated from another provider keeps its unsubscribes. An import can only ADD an opt-out — see below.
contacts[].created_atstring (ISO 8601)Original signup time. Applied on insert only; an existing contact keeps the date it already has.
contacts[].propertiesobjectCustom properties. Merged into what the contact already has, not replaced — see Custom properties above.

An upsert only ever adds or corrects — it will not blank a field the payload does not carry, so importing email alone will not wipe the names of the contacts it touches.

A batch write cannot re-subscribe anyone. unsubscribed: false will not clear an existing opt-out, and neither will omitting the field. Re-subscribing is a consent decision and lives on PATCH /audiences/:id/contacts/:contactId, one contact at a time — so a stale spreadsheet can never put somebody back on a list they left.

Stored contacts are capped by volume — 1,000 on Free, rising to 1,000,000 at 2M emails a month (see plans) — counted across every audience in the organization. A write that would exceed the cap returns 403 with code PLAN_LIMIT_EXCEEDED. See Plans & Limits.

batch upsert contacts
curl -X POST https://api.eusend.dev/audiences/550e8400-e29b-41d4-a716-446655440000/contacts/batch \
  -H "Authorization: Bearer eu_live_xxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "contacts": [
      { "email": "alice@example.com", "first_name": "Alice" },
      { "email": "bob@example.com",   "unsubscribed": true, "created_at": "2024-02-01T10:00:00.000Z" }
    ]
  }'
POST/audiences/:id/contacts/batch-delete

Delete up to 1,000 contacts from an audience in one call.

ParameterTypeDescription
contact_idsrequiredstring[]1 to 1,000 contact ids. Ids from another audience are ignored, not deleted.

Returns { "deleted": n }. That count can be lower than the number of ids you sent — an id may already be gone, or may not belong to this audience — so a retry after a dropped response settles at 0 rather than failing.

It takes ids rather than addresses on purpose: ids come from a list you have just read, so a stale address in a script cannot delete somebody who re-subscribed under it since.

Deleting a contact is not an unsubscribe. It removes them from the audience; it does not add them to your suppression list, and anyone already suppressed stays suppressed. To stop mailing somebody while keeping the record, set unsubscribed on the contact instead.

bulk delete contacts
curl -X POST https://api.eusend.dev/audiences/550e8400-e29b-41d4-a716-446655440000/contacts/batch-delete   -H "Authorization: Bearer eu_live_xxxxxxxxxxxx"   -H "Content-Type: application/json"   -d '{ "contact_ids": ["9a8b7c6d-1111-2222-3333-444455556666"] }'

The rest of the contacts surface:

GET/audiences/:id/contacts

List contacts.

GET/audiences/:id/contacts/:contactId

Get a contact.

PATCH/audiences/:id/contacts/:contactId

Update contact (name, unsubscribed, properties).

DELETE/audiences/:id/contacts/:contactId

Remove a contact.

On this page