Skip to main content
Lists (contact groups) organize your audience — see the merchant-side guide for the concepts. The API lets your systems manage membership: add a customer to “Active subscribers” when they start a plan, remove them when they cancel. All requests require authentication.

List your lists

GET /api/v1/contact-groups Returns the lists and segments in your organization, with their IDs. Use this to find the list ID you need for the add-contact call.
GET /api/v1/contact-groups/{id} returns a single list.

Add a contact to a list

POST /api/v1/contact-groups/{listId}/contacts Creates or updates the contact and adds them to the list, in one call. Returns 200 OK. The body is a flat contact object — the same field set as the Contacts API. Do not wrap it in a contact object and do not send an array; both are rejected with 400.
email
string
required
The contact’s email address. Required on this endpoint, even though the Contacts API accepts phone instead.
first_name
string
Max 100 characters.
last_name
string
Max 100 characters.
email_opt_in
string
SUBSCRIBED, NON_SUBSCRIBED, or UNSUBSCRIBED. See the warning below — always set this explicitly.
phone
string
7–20 characters.
source
string
Where the contact came from. Defaults to api.
Every other contact field is accepted here too.
Example request

Response

200 OK
action is created or updated, telling you whether the contact already existed. Note this endpoint returns a plain object — unlike the Contacts API, which returns a JSON:API envelope. A 404 means the list ID doesn’t exist. The contact may still have been created; it just isn’t in that list.
Always send email_opt_in explicitly on this endpoint. If you omit it, the result depends on whether the contact already existed: a brand-new contact is created as NON_SUBSCRIBED, but an existing contact is set to SUBSCRIBED. The same request body produces different consent outcomes depending on state you can’t see.

List membership as a trigger

Joining a list can trigger an automation — which makes this API a simple, robust way to start flows from your backend:
  1. Create a list like “Trial started” in the dashboard.
  2. Build a welcome/onboarding automation triggered by Subscribed to list → Trial started.
  3. From your backend, add users to the list when their trial begins.
The trigger fires once per contact per list. Adding someone who’s already a member won’t fire it again.
For richer use cases — where the triggering moment carries data you want in filters and emails — prefer custom events. Lists-as-triggers shine when the only fact that matters is membership itself.
Adding someone to a list does not make them marketable — their subscription status still governs whether marketing email reaches them. Manage consent through the email_opt_in field.

Contacts not showing up?

Contacts are written synchronously — if you got a success response with a contactUUID, the contact exists. If you can’t see it:
  1. Check action in the response. A response without a contact object means you’re not hitting this endpoint — verify the URL.
  2. Verify the list ID. A wrong ID returns 404; look it up with GET /api/v1/contact-groups.
  3. Match the API key to the organization you’re viewing in the dashboard. A key belongs to exactly one organization.
  4. Check email_opt_in. Contacts created as NON_SUBSCRIBED are hidden by the default subscription filters on the Members tab.
  5. Clear filters on the Members view, and hard-refresh — the dashboard caches client-side.