Skip to main content
A contact belongs to one workspace and is unique by email. Emails are trimmed and lower-cased before use. Lists are subscription lists, and a contact can be on many of them.

Upserting a contact

The response is 201 with created: true for a new contact and 200 with created: false for an update. Any field you leave out is left as it is.
  • Subscription status is never changed by an upsert. Use the list endpoints for membership.
  • customFields are written only for keys defined under Settings → Custom Fields. Unknown keys are ignored without an error.
  • source and metadata are written only when the contact is created.
  • When the plan’s contact limit is reached, creating a new contact answers 403 with code: "contact_limit_reached". Updating an existing contact still works.

List membership

POST /v1/lists/{id}/subscribe creates the contact if it does not exist, with status ACTIVE and source api. It then makes the membership ACTIVE, and a membership that was unsubscribed becomes active again. The call also fires the CONTACT_ADDED_TO_LIST webhook and starts journeys that trigger on joining the list.
This endpoint sends no double opt-in confirmation, whatever the list’s double opt-in setting. Subscribing someone here is a statement that you already have their consent.
POST /v1/lists/{id}/unsubscribe marks the membership UNSUBSCRIBED (it isn’t deleted, so the opt-out is remembered). The contact and its other lists are untouched. removed: false means there was nothing to unsubscribe. contactCount on GET /v1/lists counts every membership on the list, including pending and unsubscribed ones.