Skip to main content
Use these routes to see who has a tag, combine duplicate audience labels, find where a custom field is configured, or review email engagement for one contact. Tags are team-scoped labels. Contact attributes are custom fields; an attribute definition describes the field, while each hub can configure how it appears. A team contact is a contact’s association with your team, identified by team_contact_id. All routes below use this base path:
Read routes require Authorization: Bearer <token> with either the team owner’s JWT or a full-access API key belonging to the same team. A platform user who is not the owner, a key for another team, or a key without full access receives 403. Merging tags requires the owner’s JWT; API keys receive 403, even with full access. Responses use application/vnd.api+json. Send that content type for the merge request body too.

Tag usage

Check how many contacts currently have each tag, or inspect one tag:
Both return tags resources with these read-only fields in attributes, in addition to the tag’s name, slug, and other details: These fields describe current assignments, not a history of everyone who ever had the tag. Removing the latest assignment can make last_assigned_at move back to an older one. An unused tag has contact_count: 0 and both latest assignment fields set to null. The tag list is ordered by tag ID ascending. It accepts page[size] from 1 to 100 (default 20) and uses a tag ID as page[after]. Its response has meta.has_more and links.next.

Tag assignments

List the team contacts that currently have a tag:
Each item is a tag_assignments resource. Its id identifies the assignment, and its attributes contain: Only assignments to non-deleted team contacts are included. Results are ordered by assignment ID ascending, rather than by assigned_at. An existing unused tag returns an empty data array. See pagination and read errors for the cursor format and response fields.

Merge duplicate tags

Combine a source tag into a target tag when both represent the same audience label. The target keeps its identity and details. Contacts with only the source receive the target, retaining the original assignment ID, time, and user attribution. Contacts with both keep their existing target assignment, and the duplicate source assignment is removed. The source tag is then deleted. Existing mappings from numeric legacy tag IDs are redirected to the target, so those imported IDs resolve to the surviving tag. Event RSVP tags that still point to the source are also redirected, including those on past or cancelled events. A scheduled event that has not ended can block the merge, as described below. Deleted events are left out. Send this request with the team owner’s JWT:
Use an existing target tag ID from the same team. The body accepts only this shape; extra fields are rejected. A successful request returns 200 with the surviving tags resource in data and these top-level meta fields: Moved and duplicate counts include assignments to deleted team contacts too, so they can exceed the change in the target’s contact_count.
Merging is not idempotent and has no Idempotency-Key support. A retry after a successful merge returns 404 tag_not_found because the source is gone. If a response is lost, inspect the source and target before deciding what to do next.

References that block a merge

The API refuses to merge a source tag that is still used by current audience, automation, access, or event settings. It does not rewrite those rules for you. A 409 tag_in_use error includes errors[0].meta.references, a preview of at most 20 references, each with category, id, and name. errors[0].meta.counts gives the full count for every category, including those not shown in the preview: Update the blocking settings, then try again. Counts describe references in each category; one automation can contribute to multiple categories.

Merge errors

Find hubs using an attribute

Check which hubs have settings for a contact attribute:
Each item is a contact_attribute_hub_usages resource. Its id identifies the hub’s attribute configuration, and its attributes contain: Only non-deleted hubs in the team are included. Explicit configurations with all flags set to false are still returned; a hub with no configuration is absent. No contact values are returned.
Results use configuration ID ascending. Preserve this page order when loading more results: position describes the field’s display position within a hub, and does not sort this list of hubs.

Contact email engagement

Review tracked opens and clicks for emails sent to one team contact:
Each item is a contact_email_engagements resource whose id identifies an email send. Results are newest first by queued_at, then by send ID descending when times match. Each item’s attributes contain: Authentication emails, including those containing sign-in or recovery links, are omitted. The response does not include email addresses, message bodies, links, or raw delivery feedback.
Opens include automated and prefetch opens; the API does not filter them out. Zero opens does not prove an email was unread. Tracking may be disabled, a custom SMTP connection may not provide feedback, or feedback may be missing. Treat these fields as observed events, not proof that a person read the email.

Read definitions from the hub editor

The hub inline editor’s onboarding question picker needs the team’s attribute definitions, but the editor signs in as a hub contact, not a team user. Use the hub-scoped read:
It returns the same document as GET /api/v1/teams/{team_id}/contact-attributes, with the same ordering and page[size] limits (1 to 100, default 20). The team-level route stays limited to team owners and admins. Callers allowed on the hub-scoped route:
  • a team owner or admin;
  • the owner or admin contact of the hub named in the path;
  • platform and API-key callers, with the same access as the team route.
The hub in the path scopes the grant. A contact can only read definitions for a team in which it edits that hub. A hub outside the team, or a contact with no standing on the hub, gets 404. A member or moderator gets 403. Definitions are paged by (position, id). Pass meta.page.next_cursor back as page[after] and treat it as opaque. Cursors issued by this route start with cad1:, but you should not build or parse them. A cursor that is malformed or out of range returns 422 invalid_cursor with source.parameter set to page[after].
Reordering definitions between two page requests can repeat or skip a row. A walk with no reorder in between is exact. Restart from the first page if you need a clean list after a reorder.

Pagination and read errors

The tag-assignment, attribute-hub, and email-engagement lists accept page[size] from 1 to 100 (default 20) and an opaque page[after] cursor. Responses include meta.page.size, meta.page.has_more, meta.page.next_cursor, and links.self / links.next. Follow links.next, or pass meta.page.next_cursor as the next request’s page[after]. At the end, has_more is false and both next values are null. Cursors belong to the team and the specific tag, definition, or contact in the request. Do not reuse them for another list. If the item the cursor points to is removed from that list, restart without a cursor.
During alpha, pagination differs between these lists and GET /tags, which uses a tag ID cursor and meta.has_more. Returned pagination links currently omit the /api/v1 prefix (/api/teams/...). Either follow them as-is or pass meta.page.next_cursor as page[after] on the /api/v1/teams/... path.