Skip to content
Pathbound DOCS

Tags

A tag is a short label on a contact, a company or a deal. Use tags for what your team or your AI agents decide about a record: the campaign it belongs to, where it came from, the tier you put it in.

Tags need no setup. A tag exists from the moment a record carries it, and it leaves the workspace’s tag list when no record carries it anymore. Every contact, company and deal the API returns has a tags array, empty when the record has none.

The MCP tools list_tags, add_contact_tags, remove_contact_tags, add_company_tags, remove_company_tags, add_deal_tags and remove_deal_tags call the endpoints on this page. See MCP tools below and the tools reference.

EndpointScopeWhat it does
GET /v1/tagsone of contacts:read, companies:read, deals:readLists every tag in the workspace with how many records carry it.
POST /v1/contacts/:contact_id/tagscontacts:writeAdds tags to one contact.
DELETE /v1/contacts/:contact_id/tagscontacts:writeRemoves tags from one contact.
POST /v1/contacts/tags/bulkcontacts:writeAdds and removes tags on up to 1,000 contacts, or on every contact in a segment.
POST /v1/companies/:company_id/tagscompanies:writeAdds tags to one company.
DELETE /v1/companies/:company_id/tagscompanies:writeRemoves tags from one company.
POST /v1/companies/tags/bulkcompanies:writeAdds and removes tags on up to 1,000 companies, or on every company in a segment.
POST /v1/deals/:deal_id/tagsdeals:writeAdds tags to one deal.
DELETE /v1/deals/:deal_id/tagsdeals:writeRemoves tags from one deal.
POST /v1/deals/tags/bulkdeals:writeAdds and removes tags on up to 1,000 deals.

None of these scopes is opt-in. See Scopes.

No endpoint on this page counts against your plan. See Usage and metering.

Every tag is rewritten the same way before it is stored, and before it is compared in a filter or a removal:

  1. Whitespace at the start and the end is removed.
  2. Letters are lowercased.
  3. Each run of whitespace inside the tag becomes one space.
  4. The result is cut to 64 characters.

So " VIP Customer " is stored as vip customer, and asking for VIP Customer later finds it.

  • A record holds at most 50 tags.
  • A tag longer than 64 characters is cut, not refused.
  • A blank tag is dropped, and two tags that come out the same are one tag.
  • Tags keep the order in which they were added.
  • Do not put a comma in a tag. A comma is stored like any other character, but the list filters split their value on commas, so such a tag cannot be filtered on.

Nothing else is enforced about a tag’s form. A key:value pattern, such as campaign:oct-2026 or source:webinar, keeps a workspace’s tags readable. Before you introduce a new tag, list the existing ones and reuse a spelling that is already there.

GET /v1/tags

Required scope: any one of contacts:read, companies:read, deals:read

Returns every tag that at least one contact, company or deal carries, with a count for each record type. The most used tags come first. The endpoint takes no parameters and returns the whole list in one response.

Terminal window
curl https://api.pathbound.ai/v1/tags \
-H "Authorization: Bearer $PATHBOUND_API_KEY"
{
"status": "success",
"tags": [
{ "tag": "customer", "contact_count": 412, "company_count": 96, "deal_count": 0 },
{ "tag": "campaign:oct-2026", "contact_count": 180, "company_count": 0, "deal_count": 0 },
{ "tag": "renewal", "contact_count": 0, "company_count": 12, "deal_count": 31 }
],
"timestamp": "2026-10-13T09:20:11.482Z"
}

All three counts are returned whichever of the three scopes the key holds. The counts can be up to 30 seconds old.

POST /v1/contacts/:contact_id/tags
POST /v1/companies/:company_id/tags
POST /v1/deals/:deal_id/tags

Required scope: contacts:write, companies:write or deals:write, for the record type in the path

Adds tags to one record and returns the record’s tags as they are afterwards.

ParameterTypeDescription
contact_id, company_id or deal_idstringThe record’s id (24 hex characters). Any other value is a 400.
FieldTypeRequiredDescription
tagsstring[]yes1 to 50 tags to add.
Terminal window
curl -X POST https://api.pathbound.ai/v1/contacts/665f1c2ab3d4e5f6012345ab/tags \
-H "Authorization: Bearer $PATHBOUND_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "tags": ["Campaign:Oct-2026", "VIP"] }'
{
"status": "success",
"tags": ["customer", "campaign:oct-2026", "vip"],
"timestamp": "2026-10-13T09:21:40.117Z"
}

The tags the record already had stay where they are, and the new ones follow in the order you sent them. A tag the record already has is not added again, so repeating the request changes nothing.

A request whose tags are all blank is refused with 400.

DELETE /v1/contacts/:contact_id/tags
DELETE /v1/companies/:company_id/tags
DELETE /v1/deals/:deal_id/tags

Required scope: contacts:write, companies:write or deals:write, for the record type in the path

Removes tags from one record and returns the tags that remain. The record itself is not touched.

The tags to remove go in a JSON body, not in the query string. Some HTTP clients send no body with DELETE unless told to.

FieldTypeRequiredDescription
tagsstring[]yes1 to 50 tags to remove.
Terminal window
curl -X DELETE https://api.pathbound.ai/v1/contacts/665f1c2ab3d4e5f6012345ab/tags \
-H "Authorization: Bearer $PATHBOUND_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "tags": ["vip"] }'
{
"status": "success",
"tags": ["customer", "campaign:oct-2026"],
"timestamp": "2026-10-13T09:23:02.560Z"
}

A tag the record does not carry is ignored, so repeating the request changes nothing.

POST /v1/contacts/tags/bulk
POST /v1/companies/tags/bulk
POST /v1/deals/tags/bulk

Required scope: contacts:write, companies:write or deals:write, for the record type in the path

Adds tags, removes tags, or does both, on many records in one request. You name the records in one of two ways: by a list of ids, or, for contacts and companies, by a saved segment.

FieldTypeRequiredDescription
contact_ids, company_ids or deal_idsstring[]yes, unless segment is sent1 to 1,000 ids of the record type in the path. For more, send several requests.
segmentstringnoThe id or handle of a segment, static or dynamic, up to 128 characters. The change applies to every record that is in the segment at that moment. Contacts and companies only: /v1/deals/tags/bulk has no segment and always needs deal_ids.
addstring[]at least one of the twoUp to 50 tags to add.
removestring[]at least one of the twoUp to 50 tags to remove.
  • When you send both segment and a list of ids, the segment is used and the ids are ignored.
  • A tag that appears in both add and remove is added.
  • To rename a tag, send the new name in add and the old one in remove.

By ids:

Terminal window
curl -X POST https://api.pathbound.ai/v1/contacts/tags/bulk \
-H "Authorization: Bearer $PATHBOUND_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"contact_ids": [
"665f1c2ab3d4e5f6012345ab",
"665f1c2ab3d4e5f6012345ac",
"665f1c2ab3d4e5f6012345ad"
],
"add": ["campaign:oct-2026"],
"remove": ["campaign:sep-2026"]
}'

By segment:

Terminal window
curl -X POST https://api.pathbound.ai/v1/companies/tags/bulk \
-H "Authorization: Bearer $PATHBOUND_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "segment": "enterprise-accounts", "add": ["tier:enterprise"] }'
{
"status": "success",
"matched": 3,
"added": ["campaign:oct-2026"],
"removed": ["campaign:sep-2026"],
"timestamp": "2026-10-13T09:25:48.903Z"
}
FieldDescription
matchedWith ids: how many of them are records of that type in your workspace. An id that is not is skipped without an error. With a segment: how many records are in the segment. It is not the number of records that changed.
addedThe tags that were added, as stored.
removedThe tags that were removed, as stored.

The response does not list records. A record that already holds 50 tags takes no new one, as in Add Tags to One Record, and the response does not say which records those were.

A segment must hold the record type in the path. A dynamic segment of the other type is refused with 400 segment_entity_mismatch. See Use a Segment on Other Endpoints.

GET /v1/contacts, GET /v1/companies and GET /v1/deals take two tag filters.

ParameterTypeDescription
tags_anystringComma-separated tags. Returns records that carry at least one of them.
tags_allstringComma-separated tags. Returns records that carry every one of them.
Terminal window
# How many contacts are tagged vip or customer
curl "https://api.pathbound.ai/v1/contacts?tags_any=vip,customer&count_only=true" \
-H "Authorization: Bearer $PATHBOUND_API_KEY"
# The ids of the companies tagged both tier:enterprise and renewal
curl "https://api.pathbound.ai/v1/companies?tags_all=tier:enterprise,renewal&fields=ids" \
-H "Authorization: Bearer $PATHBOUND_API_KEY"
  • Each tag in the value goes through the tag rules first, so VIP matches vip. A tag matches as a whole, never as a part of another tag.
  • When both parameters are sent, tags_all is applied and tags_any is ignored.
  • A parameter uses its first 50 tags and ignores the rest. A value longer than 8,000 characters is a 400.
  • The tag filters combine with every other filter of the list with AND.

Filtering costs nothing. What the list then returns is metered as on any list call, so add count_only=true or fields=ids to count or select records for free.

  • When records are written. POST /v1/deals takes tags. So does each item of POST /v1/contacts/bulk, POST /v1/companies/bulk and POST /v1/deals/bulk. The tags go on the records the request creates, and are added to a record an item updates through its own id (external_id, external_deal_id). An item reported existing (a contact’s email or a company’s domain that was already there) gets none. POST /v1/contacts and POST /v1/companies take no tags: add them with a second request.
  • In aggregations. POST /v1/aggregate takes tags_any and tags_all as filters and can group contacts, companies or deals by tag.
  • In streaming. A tag automation adds or removes tags on every contact or company that matches conditions you set, and a stream’s conditions can test a record’s tags. See Automations.
  • In integration settings. On the Apollo and Emailable integration pages in the dashboard you can name tags to exclude. The integration’s sync then leaves a record that carries one of them out of paid Apollo enrichment, or out of Emailable verification.

list_tags calls GET /v1/tags.

The six add_*_tags and remove_*_tags tools call the bulk endpoints, for one record as for many, and send either add or remove, never both. Given more than 1,000 ids, a tool sends one request per 1,000 and reports the sum of matched. The contact and company tools take a segment in place of ids. The deal tools do not.

The error format and the errors every endpoint shares (a missing or invalid key, rate limits) are described in the API overview. These are the ones specific to tags.

StatuscodeMeaning
400Validation failed: an id that is not an id, more than 1,000 ids, more than 50 tags in one list, a tag that is not a string, or a missing tags or deal_ids. details lists each problem.
400Provide at least one tag in add or remove: a bulk request with neither.
400Provide contact_ids or a segment (or company_ids): a bulk request that names no records.
400No valid tags after normalization: every tag to add, or every tag of a bulk request, was blank.
400segment_entity_mismatchA dynamic segment holds the other record type. A static segment, which holds contacts, on /v1/companies/tags/bulk is answered 200 with matched: 0.
403The API key lacks the scope for the record type in the path. For GET /v1/tags, it holds none of the three read scopes.
404Contact not found, Company not found or Deal not found: the record in the path is not in your workspace.
404segment_not_foundNo segment in your workspace has this id or handle.
422segment_invalidThe segment’s definition holds no usable filter, so it selects nothing.