Distribution lists
Named recipient groups
A distribution list is a named group of recipients that lives in CommuniQueue, scoped to a workspace. Use it as a saved default recipient on a project, folder, or template, or name it directly in a send call. Every plan includes lists; higher tiers include more of them, and every plan holds up to 1,000 members per list.
Concepts
A list has a name and an optional description. A member has an email address, an optional name, and up to 50 custom fields (string values) for per-recipient personalization - a plan tier, an account id, anything your template wants to reference. Adding the same email address to two different lists does not create two separate people: the name and fields belong to the address itself, not to the list.
Contacts are shared across lists
Because a member's name and fields belong to the address across the whole workspace, updating ada@example.com's name on one list changes what every other list she belongs to sees for her too. Removing her from her last list deletes her contact record entirely - there is no orphaned "contact with no list" state.
Managing lists
| Method | Path | What it does |
|---|---|---|
| GET | /api/v1/distribution-lists | Page through lists, optionally filtered by exact name (case-insensitive). |
| POST | /api/v1/distribution-lists | Create a list. Returns 201 with the list. |
| GET | /api/v1/distribution-lists/{listId} | One list. |
| PATCH | /api/v1/distribution-lists/{listId} | Change name and/or description. |
| DELETE | /api/v1/distribution-lists/{listId} | Delete the list. 409 if a saved default recipient still references it. |
Create a list
curl -X POST https://api.communiqueue.com/api/v1/distribution-lists \
-H "X-Api-Key: cqk_..." \
-H "Content-Type: application/json" \
-d '{ "name": "Ops team", "description": "Internal ops alerts" }'Response - 201
{
"id": "0f2b8a1e-6c9d-4b3a-9e7f-2d4c8a1b6e3f",
"name": "Ops team",
"description": "Internal ops alerts",
"memberCount": 0,
"usageCount": 0,
"createdAt": "2026-09-27T12:00:00Z",
"updatedAt": "2026-09-27T12:00:00Z"
}Managing members
| Method | Path | What it does |
|---|---|---|
| GET | /api/v1/distribution-lists/{listId}/members | Page through members, optionally filtered by a search string matched against email or name. |
| GET | /api/v1/distribution-lists/{listId}/members/{email} | One member by URL-encoded email. |
| POST | /api/v1/distribution-lists/{listId}/members | Add or update up to 1,000 members. |
| POST | /api/v1/distribution-lists/{listId}/members/remove | Remove up to 1,000 members by email. |
| PUT | /api/v1/distribution-lists/{listId}/members | Replace the whole membership with up to 1,000 members. |
- Every write is all-or-nothing: if any member in the batch fails validation, nothing is written. The 422 response's errorCode is the first rule that fired, errorCodes lists every distinct rule that fired, and itemErrors names every bad entry by index and code.
- The same email appearing twice in one write is rejected outright, before anything is written.
- A field key you send with a value sets it; a field key sent as null deletes it; a field you don't mention is left as it was. Omitting name keeps the current name - send an empty string to clear it.
- PUT replaces which contacts are members of this list, never another list's view of a shared contact - an explicit empty members: [] clears the whole list in one call. POST members and POST members/remove treat that same empty array as a mistake and refuse it with distribution_lists.batch_empty.
- Adding members past 1,000 on one list is refused with distribution_lists.member_limit_reached. A list holds at most 1,000 members, which is also the most one send can reach, so remove members first to make room.
Add or update members
curl -X POST https://api.communiqueue.com/api/v1/distribution-lists/{listId}/members \
-H "X-Api-Key: cqk_..." \
-H "Content-Type: application/json" \
-d '{
"members": [
{ "email": "ada@example.com", "name": "Ada Lovelace", "fields": { "plan": "pro" } },
{ "email": "grace@example.com" }
]
}'Response - 200
{ "added": 2, "updated": 0, "removed": 0, "total": 2 }Sending to a list
emailOptions on the send and preview calls gains four optional arrays of list names, matched case-insensitively: toLists, ccLists, and bccLists add every member of the named lists exactly as to, cc, and bcc add plain addresses. overrideLists joins overrideRecipients: together they replace every saved default recipient and every additive field above. An unknown list name fails the whole request with 422 notifications.email.distribution_list_not_found, naming every unknown name in the response. List membership is expanded once, when the send is accepted - a retry never re-reads live membership, so nobody is skipped or mailed twice if the list changes mid-delivery.
Whether members see each other depends on the version's email type. A Transactional version sends one email that every To and Cc recipient can see, list members included, so put a list in bccLists when its members should not see each other's addresses. A Marketing version sends one email per To recipient and would copy every Cc and Bcc recipient onto each of them, so a Marketing send that involves a list cannot also have Cc or Bcc recipients, saved ones included. It is refused with 422 notifications.email.marketing_list_with_copies.
curl -X POST https://api.communiqueue.com/api/v1/notifications/send \
-H "X-Api-Key: cqk_..." \
-H "Content-Type: application/json" \
-d '{
"tenantId": "<your workspace id>",
"templateId": "<a Marketing template id>",
"tags": [],
"emailOptions": { "toLists": ["Ops team"] }
}'Recipient variables
A Marketing template version renders once per To recipient (override recipients count as To), so it can reference the recipient it is being rendered for: {{recipient.email}}, {{recipient.name}}, and {{recipient.fieldKey}} for any custom field on that member. A recipient who came from a plain address rather than a list has only {{recipient.email}} - a missing field renders empty and is reported the same way a missing tag is today. A Transactional version - one shared copy for everyone - is refused at send time with 422 notifications.recipient_variables_require_marketing if its content references {{recipient.*}} at all. Because of this, recipient is a reserved tag key: a send whose tags include a key named recipient or starting with recipient. fails with 422 notifications.tags.recipient_reserved. The preview call accepts an optional top-level recipient object (email, name, fields) so a personalized template can be previewed with sample data. Preview renders the live version, or the published version you pin with versionNumber; there is no test send through the API. In the app, a test send of a draft takes a sample recipient's name and fields the same way.
Permissions and rate limits
Every route under /distribution-lists requires the key's ManageLists permission. /notifications/send and /notifications/preview require only Send, even when the request names a list - a send-only key can mail a list by name without ever being able to see or change who's on it. A key without the required permission gets 403 api_keys.permission_denied, naming the missing permission. Existing keys carry Send only, so nothing already integrated changes. Permissions are chosen when a key is created and can't be edited afterward - create a new key with the permissions you need and revoke the old one. Every /distribution-lists route sits behind its own public-lists rate-limit policy: 120 requests per minute per API key, and separately 120 requests per minute per source IP - both independent of the send and preview budget.
Error codes
| Code | Status | Meaning |
|---|---|---|
| distribution_lists.not_found | 404 | No list matches the given listId in this workspace. |
| distribution_lists.name_invalid | 422 | The list name is empty after trimming, or longer than 100 characters. |
| distribution_lists.name_conflict | 409 | Another list in this workspace already has this name, compared case-insensitively. |
| distribution_lists.description_too_long | 422 | The list description is longer than 500 characters. |
| distribution_lists.in_use | 409 | The list is still referenced by a saved default recipient on a project, folder, or template. The response lists every place it is used. |
| distribution_lists.paging_invalid | 400 | limit is outside 1 to 200, or cursor is malformed. |
| distribution_lists.limit_exceeded | 403 | Creating this list would put the workspace over the plan's list count. The response carries the usual resourceType/current/limit billing-limit shape. |
| distribution_lists.limit_not_configured | 403 | The workspace's plan has no list-count limit configured, so the create cannot be authorized. Contact support. |
| distribution_lists.member_not_found | 404 | No member with that email exists on this list. |
| distribution_lists.member_email_invalid | 422 | A member entry's email is empty, blank, or not a valid address. |
| distribution_lists.member_name_too_long | 422 | A member's name is longer than 200 characters. |
| distribution_lists.field_key_invalid | 422 | A custom field key does not match ^[A-Za-z][A-Za-z0-9_]{0,63}$. |
| distribution_lists.field_key_reserved | 422 | A custom field key is email or name, which are reserved for the built-in fields. |
| distribution_lists.field_value_invalid | 422 | A custom field value is not a string or null, or is longer than 1,000 characters. Send null to remove a field. |
| distribution_lists.too_many_fields | 422 | A member would have more than 50 custom fields, counting the fields already stored for that address, or one member carries more than 100 field keys, counting null removals. |
| distribution_lists.batch_too_large | 422 | The request names more than 1,000 members in one call. |
| distribution_lists.batch_empty | 422 | The request's members or emails array is empty or missing. A PUT with an explicit empty members array is not this error - it means clear the list. |
| distribution_lists.duplicate_email_in_batch | 422 | The same email appears twice in one write, compared case-insensitively. |
| distribution_lists.member_limit_reached | 422 | Adding these members would put the list over its 1,000-member cap. The response's currentCount/attempted/limit extensions say by how much. |
| api_keys.permission_denied | 403 | The key is valid, but it doesn't carry the permission this route requires. The response names the missing permission. |
| notifications.email.distribution_list_not_found | 422 | One or more names in toLists, ccLists, bccLists, or overrideLists don't match a list in this workspace. The response lists every unknown name. |
| notifications.email.marketing_list_with_copies | 422 | A Marketing send that involves a list also has Cc or Bcc recipients, from the request or saved on the template. Each copy would carry one member's address and personal content. Remove the copies, or send the list as Transactional. |
| notifications.recipient_variables_require_marketing | 422 | The template's content references recipient.* but the version's email type is Transactional, which sends one shared copy to everyone. Switch the version to Marketing, or remove the recipient.* references. |
| notifications.tags.recipient_reserved | 422 | A tag key is recipient or starts with recipient., which is reserved for per-recipient list data. |
See Errors and rate limits for the RFC 7807 body shape every one of these arrives in.