Skip to main content

Tags & Lists

Tags and lists are two complementary ways to organize your contacts in SendSeven. Tags are color-coded labels for flexible categorization. Lists are named collections of contacts used for campaigns and bulk operations.

Tags vs. Lists​

AspectTagsLists
Apply toContacts and conversationsContacts only
Use caseFlexible categorization, filteringCampaign targeting, bulk operations
VisualColor-coded labelsNamed groups
MultipleA resource can have many tagsA contact can be in many lists

Use tags for dynamic categorization and visual organization. Use lists for campaign recipient groups and structured audience segments.

Required Scopes​

ScopePurpose
contacts:read or conversations:readList and view tags
tags:createCreate tags
tags:updateRename or recolor tags
tags:deleteDelete tags
contacts:updateAdd/remove tags on contacts
conversations:updateAdd/remove tags on conversations
lists:readList and view contact lists
lists:createCreate new lists
lists:updateAdd/remove contacts from lists
lists:read + contacts:readList the contacts on a list, and preview or count which contacts a set of conditions would match
lists:deleteDelete lists

Tags​

List Tags​

curl -X GET "https://api.sendseven.com/api/v1/tags?page=1&page_size=20" \
-H "Authorization: Bearer s7_api_a1b2c3d4e5f6789012345678abcdef00" \
-H "Content-Type: application/json"
{
"items": [
{
"id": "tag_vip",
"name": "VIP",
"color": "#EF4444",
"description": "High-value customers",
"created_at": "2026-01-10T09:00:00Z"
},
{
"id": "tag_new",
"name": "New Customer",
"color": "#10B981",
"description": "Recently onboarded",
"created_at": "2026-01-12T14:00:00Z"
}
],
"pagination": {
"page": 1,
"page_size": 20,
"total": 3,
"total_pages": 1,
"has_next": false,
"has_prev": false
}
}

Usage counts​

Add include_usage=true to see how many contacts and conversations carry each tag. Every item then also has contact_count and conversation_count (0 when the tag is unused). The counts cover the tags on the returned page. Leave the parameter off when you don't need the counts; the plain list is faster.

curl -X GET "https://api.sendseven.com/api/v1/tags?include_usage=true&page=1&page_size=20" \
-H "Authorization: Bearer s7_api_a1b2c3d4e5f6789012345678abcdef00"
{
"items": [
{
"id": "tag_vip",
"name": "VIP",
"color": "#EF4444",
"description": "High-value customers",
"created_at": "2026-01-10T09:00:00Z",
"contact_count": 128,
"conversation_count": 342
}
],
"pagination": { "page": 1, "page_size": 20, "total": 1, "total_pages": 1, "has_next": false, "has_prev": false }
}

Create a Tag​

curl -X POST "https://api.sendseven.com/api/v1/tags" \
-H "Authorization: Bearer s7_api_a1b2c3d4e5f6789012345678abcdef00" \
-H "Content-Type: application/json" \
-d '{
"name": "Enterprise",
"color": "#6366F1",
"description": "Enterprise-tier customers"
}'
FieldTypeRequiredDescription
namestringYesTag name (max 50 chars, unique per workspace)
colorstringNoHex color code (e.g., #EF4444)
descriptionstringNoTag description (max 255 chars)

Tag names are unique per workspace. The comparison ignores case, accents and trailing spaces, so VIP, vip and vip count as the same name. Creating or renaming a tag to an existing name returns 400:

{"detail": "A tag with the name 'VIP' already exists"}

Tags are also used as team-member skills for routing. See Skill-Based Routing.

Update a Tag​

curl -X PUT "https://api.sendseven.com/api/v1/tags/tag_enterprise" \
-H "Authorization: Bearer s7_api_a1b2c3d4e5f6789012345678abcdef00" \
-H "Content-Type: application/json" \
-d '{
"color": "#8B5CF6",
"description": "Enterprise and strategic accounts"
}'

Delete a Tag​

Deleting a tag removes it from all contacts and conversations it is applied to. This removal does not send contact.tag_removed webhooks and does not start contact_tag_removed flows; only removing a tag from a contact does.

curl -X DELETE "https://api.sendseven.com/api/v1/tags/tag_enterprise" \
-H "Authorization: Bearer s7_api_a1b2c3d4e5f6789012345678abcdef00"
{"success": true, "message": "Tag deleted successfully"}

A tag that a custom inbox uses in its filter rules cannot be deleted. Remove it from those inboxes first; until then the call returns 409:

{
"error": "This tag can't be deleted because it is used in the filter rules of these inboxes: “Support DE”. Remove it from those inbox rules first (Settings > Inboxes), then delete it.",
"code": "TAG_IN_USE_BY_INBOX",
"details": {"inboxes": [{"id": "inbox_1", "name": "Support DE"}]}
}

Unlike most errors, this body is not wrapped in detail.

Add a Tag to a Contact​

Scope contacts:update. Idempotent: adding a tag the contact already has succeeds without change.

curl -X POST "https://api.sendseven.com/api/v1/contacts/contact_xyz789/tags/tag_vip" \
-H "Authorization: Bearer s7_api_a1b2c3d4e5f6789012345678abcdef00" \
-H "Content-Type: application/json"

Remove a Tag from a Contact​

curl -X DELETE "https://api.sendseven.com/api/v1/contacts/contact_xyz789/tags/tag_vip" \
-H "Authorization: Bearer s7_api_a1b2c3d4e5f6789012345678abcdef00"

Add a Tag to a Conversation​

Scope conversations:update. DELETE on the same path removes it.

curl -X POST "https://api.sendseven.com/api/v1/conversations/conv_abc123/tags/tag_urgent" \
-H "Authorization: Bearer s7_api_a1b2c3d4e5f6789012345678abcdef00" \
-H "Content-Type: application/json"

To tag many contacts at once, use the add_tags / remove_tags bulk actions.


Lists​

List All Lists​

curl -X GET "https://api.sendseven.com/api/v1/lists?page=1" \
-H "Authorization: Bearer s7_api_a1b2c3d4e5f6789012345678abcdef00" \
-H "Content-Type: application/json"
{
"items": [
{
"id": "list_newsletter",
"name": "Newsletter Subscribers",
"description": "Main weekly newsletter recipients",
"list_type": "newsletter",
"contact_count": "2450",
"created_at": "2026-01-05T10:00:00Z",
"updated_at": "2026-02-10T12:00:00Z"
}
],
"pagination": {
"page": 1,
"page_size": 20,
"total": 5,
"total_pages": 1,
"has_next": false,
"has_prev": false
}
}

contact_count is returned as a string. A dynamic list's segment_conditions are included only when your API key has contacts:read, because conditions can name custom fields; without it they are null. Ids, names and counts are always returned.

Create a List​

curl -X POST "https://api.sendseven.com/api/v1/lists" \
-H "Authorization: Bearer s7_api_a1b2c3d4e5f6789012345678abcdef00" \
-H "Content-Type: application/json" \
-d '{
"name": "Product Launch Invites",
"description": "Contacts to invite to the Q2 product launch event",
"list_type": "newsletter"
}'
FieldTypeRequiredDescription
namestringYesList name (1 to 255 characters)
list_typestringYesnewsletter (contacts subscribe per channel) or dynamic (membership computed from segment_conditions). static exists only for older lists
descriptionstringNoList description (max 1000 characters)
segment_conditionsobjectDynamic listsThe condition tree, see Dynamic lists
slug, newsletter_config, show_in_preference_center, opt_in_actionNoNewsletter settings (sign-up keywords, welcome messages, preference center). See Getting Subscribers
is_defaultbooleanNoMake this the workspace's default newsletter list. Not allowed for dynamic lists (400)

Dynamic lists​

A dynamic list has no members of its own: it shows every contact that matches its segment_conditions at the moment you read it. Every dynamic list must start from the subscribers of at least one newsletter list, so nobody who never subscribed to anything can end up in it. The top level of the tree is an AND group whose first child is that list membership:

{
"name": "German VIP subscribers",
"list_type": "dynamic",
"segment_conditions": {
"operator": "AND",
"groups": [
{"type": "condition", "field": "has_optin", "operator": "equals", "value": true, "list_id": "list_newsletter", "channel_type": "any"},
{"type": "condition", "field": "has_tag", "value": "tag_vip"},
{"type": "condition", "field": "custom_field", "key": "country", "operator": "equals", "value": "DE"}
]
}
}

Several membership conditions at the top level mean "subscribed to all of them"; wrap them in an OR group (containing only membership conditions) for "subscribed to any of them". Violations return 422 with detail.code audience_list_membership_required (no top-level membership), audience_membership_list_invalid (the list is unknown or not a newsletter list) or audience_conditions_invalid (malformed tree).

How conditions match:

  • Negative operators (not_equals, not_contains, is_empty, ...) also match contacts that have no value at all.
  • A condition on a custom field that has been deleted matches nobody, so the list shrinks rather than silently widening. If you later create a field with the same key, the condition uses the new field.
  • Number custom fields compare numerically (gt, gte, lt, lte, between): 10 is greater than 9. Values that are not plain numbers never match a number comparison.
  • A malformed condition matches nobody.

Check a tree before saving it with POST /lists/evaluate-count (body: the condition tree, returns {"count": n}), or preview the matching contacts for an existing list with POST /lists/{list_id}/evaluate (same body, returns the contacts and total_contacts; nothing is saved). Both need lists:read and contacts:read, because they reveal which contacts match. Count a saved list with POST /lists/{list_id}/count (lists:read only).

Add a Contact to a List​

curl -X POST "https://api.sendseven.com/api/v1/lists/list_launch/members" \
-H "Authorization: Bearer s7_api_a1b2c3d4e5f6789012345678abcdef00" \
-H "Content-Type: application/json" \
-d '{
"contact_id": "contact_001",
"channel_type": "whatsapp"
}'
{"success": true, "message": "Contact subscribed to list", "member_id": "sub_8c1d2e"}
FieldRequiredDescription
contact_idYesThe contact
channel_typeNewsletter listsThe channel the contact subscribes on: email, sms, whatsapp, telegram, messenger, instagram, ... A contact can subscribe to the same list on several channels. Omitting it still falls back to email, but that fallback is deprecated
channel_idTelegram, Messenger, InstagramThe channel the subscription belongs to

Dynamic lists take their members from their conditions: add the contact to the newsletter list the dynamic list starts from instead. To add many contacts at once, use the add_to_list bulk action or an import with list_id.

Remove a Contact from a List​

curl -X DELETE "https://api.sendseven.com/api/v1/lists/list_launch/members/contact_003?channel_type=whatsapp" \
-H "Authorization: Bearer s7_api_a1b2c3d4e5f6789012345678abcdef00"

Returns 204. For newsletter lists pass channel_type: it removes that one channel's subscription. Dynamic lists return 400 (Cannot remove contacts from dynamic lists).

Contacts on a List​

GET /lists/{list_id}/contacts (scopes lists:read and contacts:read) returns the list's contacts, paginated. You can also filter GET /contacts by list_id together with list_subscription_status and list_subscription_channel (see List Contacts).

Error Responses​

Most errors return detail as a message string; the cases below that carry a machine-readable code are listed with it. See Error Handling for all response shapes.

StatusdetailDescription
400"A tag with the name 'VIP' already exists"Tag name already used in the workspace
401"Invalid or expired API token"Missing or invalid token
403"Missing required permission(s): tags:create"Token lacks a required scope
404"Tag not found", "List not found", "Contact not found"Unknown ID, or it belongs to another workspace
409code: TAG_IN_USE_BY_INBOXThe tag is used by a custom inbox filter (body not wrapped in detail, see Delete a Tag)
422code: audience_*Invalid dynamic list conditions
422list of field errorsInvalid request body

Next Steps​