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.
| Endpoint | Scope | What it does |
|---|---|---|
GET /v1/tags | one of contacts:read, companies:read, deals:read | Lists every tag in the workspace with how many records carry it. |
POST /v1/contacts/:contact_id/tags | contacts:write | Adds tags to one contact. |
DELETE /v1/contacts/:contact_id/tags | contacts:write | Removes tags from one contact. |
POST /v1/contacts/tags/bulk | contacts:write | Adds and removes tags on up to 1,000 contacts, or on every contact in a segment. |
POST /v1/companies/:company_id/tags | companies:write | Adds tags to one company. |
DELETE /v1/companies/:company_id/tags | companies:write | Removes tags from one company. |
POST /v1/companies/tags/bulk | companies:write | Adds and removes tags on up to 1,000 companies, or on every company in a segment. |
POST /v1/deals/:deal_id/tags | deals:write | Adds tags to one deal. |
DELETE /v1/deals/:deal_id/tags | deals:write | Removes tags from one deal. |
POST /v1/deals/tags/bulk | deals:write | Adds 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.
Tag Rules
Section titled “Tag Rules”Every tag is rewritten the same way before it is stored, and before it is compared in a filter or a removal:
- Whitespace at the start and the end is removed.
- Letters are lowercased.
- Each run of whitespace inside the tag becomes one space.
- 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.
List Tags
Section titled “List Tags”GET /v1/tagsRequired 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.
curl https://api.pathbound.ai/v1/tags \ -H "Authorization: Bearer $PATHBOUND_API_KEY"Response — 200 OK
Section titled “Response — 200 OK”{ "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.
Add Tags to One Record
Section titled “Add Tags to One Record”POST /v1/contacts/:contact_id/tagsPOST /v1/companies/:company_id/tagsPOST /v1/deals/:deal_id/tagsRequired 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.
Path Parameters
Section titled “Path Parameters”| Parameter | Type | Description |
|---|---|---|
contact_id, company_id or deal_id | string | The record’s id (24 hex characters). Any other value is a 400. |
Request Body
Section titled “Request Body”| Field | Type | Required | Description |
|---|---|---|---|
tags | string[] | yes | 1 to 50 tags to add. |
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"] }'Response — 200 OK
Section titled “Response — 200 OK”{ "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.
Remove Tags from One Record
Section titled “Remove Tags from One Record”DELETE /v1/contacts/:contact_id/tagsDELETE /v1/companies/:company_id/tagsDELETE /v1/deals/:deal_id/tagsRequired 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.
Request Body
Section titled “Request Body”| Field | Type | Required | Description |
|---|---|---|---|
tags | string[] | yes | 1 to 50 tags to remove. |
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"] }'Response — 200 OK
Section titled “Response — 200 OK”{ "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.
Add or Remove Tags in Bulk
Section titled “Add or Remove Tags in Bulk”POST /v1/contacts/tags/bulkPOST /v1/companies/tags/bulkPOST /v1/deals/tags/bulkRequired 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.
Request Body
Section titled “Request Body”| Field | Type | Required | Description |
|---|---|---|---|
contact_ids, company_ids or deal_ids | string[] | yes, unless segment is sent | 1 to 1,000 ids of the record type in the path. For more, send several requests. |
segment | string | no | The 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. |
add | string[] | at least one of the two | Up to 50 tags to add. |
remove | string[] | at least one of the two | Up to 50 tags to remove. |
- When you send both
segmentand a list of ids, the segment is used and the ids are ignored. - A tag that appears in both
addandremoveis added. - To rename a tag, send the new name in
addand the old one inremove.
By ids:
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:
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"] }'Response — 200 OK
Section titled “Response — 200 OK”{ "status": "success", "matched": 3, "added": ["campaign:oct-2026"], "removed": ["campaign:sep-2026"], "timestamp": "2026-10-13T09:25:48.903Z"}| Field | Description |
|---|---|
matched | With 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. |
added | The tags that were added, as stored. |
removed | The 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.
Filter Lists by Tag
Section titled “Filter Lists by Tag”GET /v1/contacts, GET /v1/companies and GET /v1/deals take two tag filters.
| Parameter | Type | Description |
|---|---|---|
tags_any | string | Comma-separated tags. Returns records that carry at least one of them. |
tags_all | string | Comma-separated tags. Returns records that carry every one of them. |
# How many contacts are tagged vip or customercurl "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 renewalcurl "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
VIPmatchesvip. A tag matches as a whole, never as a part of another tag. - When both parameters are sent,
tags_allis applied andtags_anyis 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.
Where Else Tags Appear
Section titled “Where Else Tags Appear”- When records are written.
POST /v1/dealstakestags. So does each item ofPOST /v1/contacts/bulk,POST /v1/companies/bulkandPOST /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 reportedexisting(a contact’s email or a company’s domain that was already there) gets none.POST /v1/contactsandPOST /v1/companiestake no tags: add them with a second request. - In aggregations.
POST /v1/aggregatetakestags_anyandtags_allas filters and can group contacts, companies or deals bytag. - 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.
MCP Tools
Section titled “MCP Tools”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.
Errors
Section titled “Errors”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.
| Status | code | Meaning |
|---|---|---|
400 | Validation 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. | |
400 | Provide at least one tag in add or remove: a bulk request with neither. | |
400 | Provide contact_ids or a segment (or company_ids): a bulk request that names no records. | |
400 | No valid tags after normalization: every tag to add, or every tag of a bulk request, was blank. | |
400 | segment_entity_mismatch | A dynamic segment holds the other record type. A static segment, which holds contacts, on /v1/companies/tags/bulk is answered 200 with matched: 0. |
403 | The API key lacks the scope for the record type in the path. For GET /v1/tags, it holds none of the three read scopes. | |
404 | Contact not found, Company not found or Deal not found: the record in the path is not in your workspace. | |
404 | segment_not_found | No segment in your workspace has this id or handle. |
422 | segment_invalid | The segment’s definition holds no usable filter, so it selects nothing. |