Contact lists
A contact list is a static grouping of contacts — a manually curated audience like "newsletter subscribers", "VIPs", or "Q1 2026 import". Lists are the simple cousin to audience segments: segments are dynamic queries that re-evaluate; lists are explicit membership rows you add and remove. This page covers ripllo.contactLists on the Node SDK. For the HTTP surface, see API → Contact lists.
Namespace
ripllo.contactLists.list()
ripllo.contactLists.get(id)
ripllo.contactLists.create(input)
ripllo.contactLists.addMember(id, input)
ripllo.contactLists.removeMember(id, contactId)
ripllo.contactLists.delete(id)
Six methods. There's no update, and no PATCH /api/v1/contact-lists/:id behind it either — a list's name and description are fixed once created, so renaming means creating the new list and re-adding members. Membership is mutated through addMember / removeMember, not by reassigning a list of IDs.
Methods
contactLists.create
Signature. ripllo.contactLists.create(input): Promise<ContactList>
Creates an empty list. Membership is added afterwards.
const list = await ripllo.contactLists.create({
name: 'Newsletter — Indonesia',
description: 'Buyers who opted in via the homepage modal.',
});
contactLists.list
Signature. ripllo.contactLists.list(): Promise<ContactList[]>
Returns every list in the workspace, newest first. No pagination — most workspaces have well under 100 lists. Each entry includes a memberCount integer so you don't have to count membership yourself.
const lists = await ripllo.contactLists.list();
for (const l of lists) console.log(l.name, l.memberCount);
The payload is a bare array, not
{ lists }. The SDK's return type says{ lists }, but this router responds with the array itself, soconst { lists } = await …yieldsundefinedand the loop after it throws. Assign the result directly. (Wrapping is per-router here:audienceSegments.list()really does return{ segments }.)
contactLists.get
Signature. ripllo.contactLists.get(id): Promise<ContactList>
Fetches by ID (an opaque cuid). Returns the list shape plus memberCount and a members array — the 100 most recently added membership rows, each with its contact inlined. There is no member-pagination endpoint; for a roster larger than 100 you'll need to hold your own copy as you add.
contactLists.addMember
Signature. ripllo.contactLists.addMember(id, input): Promise<{ added: number }>
Adds contacts to a list. Takes an array, up to 500 per call, and returns how many were upserted — not a boolean. Re-adding an existing member is a no-op that still counts toward added.
await ripllo.contactLists.addMember('<list-id>', { contactIds: ['<contact-id>', '<contact-id-2>'] });
The field is
contactIds, plural and an array. The SDK types this parameter as{ contactId: string }; that body fails zod validation with400 VALIDATION, so the method is unusable as typed. Pass{ contactIds: [...] }. If none of the IDs belong to your workspace you get400 NO_VALID_CONTACTS.
contactLists.removeMember
Signature. ripllo.contactLists.removeMember(id, contactId): Promise<{ id: string; contactId: string }>
Removes a contact from a list and resolves to the pair it acted on. Removing a contact that wasn't a member is silently fine — there's no removed flag to check, so compare memberCount before and after if you need to know.
await ripllo.contactLists.removeMember('<list-id>', '<contact-id>');
contactLists.delete
Signature. ripllo.contactLists.delete(id): Promise<{ id: string }>
Hard-deletes the list and resolves to { id } (not { deleted: true }, despite the SDK's type). Membership rows go with it; the underlying contacts are not touched.
await ripllo.contactLists.delete('<list-id>');
Types
interface ContactList {
id: string; // opaque cuid — no 'cl_' prefix
accountId: string;
name: string;
description: string | null;
memberCount: number;
createdAt: string;
updatedAt: string;
}
get also returns members — up to 100 rows of { listId, contactId, addedAt, contact }, newest first. list does not.
Common patterns
Bulk-add from a contact import
const list = await ripllo.contactLists.create({ name: 'CSV import 2026-05' });
const { contacts } = await ripllo.contacts.list({ limit: 100 });
await ripllo.contactLists.addMember(list.id, { contactIds: contacts.map((c) => c.id) });
One call takes up to 500 IDs, so batch rather than looping. If your audience is rule-shaped rather than a fixed roster, prefer an audience segment — it re-evaluates at send time instead of going stale.
Sync a list against an external CRM
async function syncList(listId: string, desiredContactIds: Set<string>) {
// The 100 most recent members come back on `get`; there is no
// member-pagination endpoint, so keep your own roster for larger lists.
const { members } = await ripllo.contactLists.get(listId);
const current = new Set(members.map((m) => m.contactId));
const toAdd = [...desiredContactIds].filter((id) => !current.has(id));
if (toAdd.length) await ripllo.contactLists.addMember(listId, { contactIds: toAdd });
for (const id of current) {
if (!desiredContactIds.has(id)) await ripllo.contactLists.removeMember(listId, id);
}
}
Wire a list to a broadcast
const list = await ripllo.contactLists.create({ name: 'May newsletter audience' });
// ...add members...
await ripllo.broadcasts.create({
name: 'May newsletter',
providers: ['email_resend'],
content: { email: { subject: 'Your May update', html: '…', text: '…' } },
audience: { listIds: [list.id] },
});
audience.listIds is the seam between lists and sends — and it's on broadcasts, not marketingCampaigns. See broadcasts for the send-time mechanics.
Errors
| Code | Status | Cause |
|---|---|---|
VALIDATION |
400 | Bad name (empty / over 120 chars), or a members body that isn't { contactIds: [...] } with 1–500 entries. |
NO_VALID_CONTACTS |
400 | None of the supplied contact IDs belong to this workspace. |
NAME_TAKEN |
409 | A list with that name already exists in the workspace. |
NOT_FOUND |
404 | List doesn't exist in this workspace. |
NO_ACCOUNT |
403 | Credential carries no accountId. |
See Errors for handling.
Next
- Audience segments — the dynamic alternative.
- Broadcasts — how a list becomes a send.
- API → Contact lists — full HTTP reference.