API Reference
List Memberships
Manage contact memberships in CRM lists. Uses the secret key for authentication.
Add or Update Membership
POST
/api/v1/crm/list_memberships
Secret Key
30/min
Add a contact to a list, or update their membership if they already belong to it. If the contact is already a member, their status and consent fields are updated.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| list_id | integer | Yes | ID of the list to add the contact to |
| Option A — Existing contact | |||
| contact_id | integer | * | ID of an existing contact. Returns 404 if not found. |
| Option B — Contact by email (creates if needed) | |||
| contact | object | * | Identify a contact by email. Creates the contact if no match exists. Blank fields on an existing contact are filled from the provided values — existing data is never overwritten. |
| contact[email] | string | Yes | Email address (required within the contact object) |
| contact[first_name] | string | No | First name |
| contact[last_name] | string | No | Last name |
| contact[country] | string | No | Country |
| contact[time_zone] | string | No | Time zone (e.g. "America/New_York") |
|
* Exactly one of |
|||
| status | string | No | Membership status. One of: subscribed, confirmed, unsubscribed, bounced, complained. Defaults to subscribed. |
| consent_source | string | No | How consent was obtained (e.g. "signup_form", "import") |
| consent_text_version | string | No | Version identifier of the consent text shown to the contact |
| consent_ip | string | No | IP address of the user at the time of consent. Automatically anonymised before storage (IPv4 to /24, IPv6 to /48). Only recorded on initial subscription, not on re-subscribe. |
Example — Using contact_id
{
"list_id": 1,
"contact_id": 7,
"status": "subscribed",
"consent_source": "signup_form",
"consent_ip": "203.0.113.42"
}
Example — Using contact hash
{
"list_id": 1,
"contact": {
"email": "jane@example.com",
"first_name": "Jane",
"last_name": "Doe",
"country": "US",
"time_zone": "America/New_York"
},
"consent_source": "signup_form",
"consent_ip": "203.0.113.42"
}
Success Response (201 Created / 200 OK)
Returns 201 for new memberships, 200 when updating an existing membership.
{
"id": 42,
"list_id": 1,
"contact_id": 7,
"email": "jane@example.com",
"status": "subscribed",
"subscribed_at": "2026-02-26T10:30:00Z",
"confirmed_at": null,
"unsubscribed_at": null,
"consent_source": "signup_form",
"consent_text_version": "v2",
"consent_ip": "203.0.113.0",
"created_at": "2026-02-26T10:30:00Z"
}
Error Responses
// 422 — Missing or invalid params
{ "error": "list_id is required" }
{ "error": "contact_id or contact is required" }
{ "error": "contact[email] is required" }
// 404 — Not found
{ "error": "list not found" }
{ "error": "contact not found" }
Remove Membership
DELETE
/api/v1/crm/list_memberships
Secret Key
30/min
Unsubscribe a contact from a list. Sets the membership status to "unsubscribed" rather than deleting the record.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| list_id | integer | Yes | ID of the list to remove the contact from |
| contact_id | integer | * | ID of the contact to unsubscribe |
| contact | object | * | Identify the contact by email (same as subscribe endpoint) |
|
* Exactly one of |
|||
Success Response (200 OK)
{
"id": 42,
"list_id": 1,
"contact_id": 7,
"email": "jane@example.com",
"status": "unsubscribed",
"subscribed_at": "2026-02-26T10:30:00Z",
"confirmed_at": null,
"unsubscribed_at": "2026-02-26T12:00:00Z",
"consent_source": "signup_form",
"consent_text_version": "v2",
"consent_ip": "203.0.113.0",
"created_at": "2026-02-26T10:30:00Z"
}
Error Responses
// 422 — Missing params
{ "error": "list_id is required" }
{ "error": "contact_id or contact is required" }
// 404 — Not found
{ "error": "list not found" }
{ "error": "contact not found" }
{ "error": "membership not found" }