Skip to content
Pathbound DOCS

Segments

A segment is a named group of contacts or companies in your workspace. You create one from a list of filters, and from then on you refer to it by its id or its handle wherever an endpoint takes a segment parameter: to list its members, count them, aggregate over them, tag them, or read their meetings.

A segment can also be the target of an action that runs over every member as a background job: enrich them with Apollo, add them to a Resend segment, or enroll them in an Apollo sequence. You start the job, get a job id back, and poll it.

The MCP tools create_segment, list_segments, get_segment, enrich_segment, broadcast_to_segment, apollo_add_segment_to_sequence and get_job call the same endpoints. See the MCP tools reference.

EndpointScopeWhat it does
POST /v1/segmentscontacts:write or companies:write, by what the segment holdsCreate a dynamic segment from filters.
GET /v1/segmentscontacts:read or companies:read, either oneList the workspace’s segments.
GET /v1/segments/:refcontacts:read or companies:read, by what the segment holdsOne segment with its current member count.
POST /v1/segments/:ref/actionsenrichment:write or actions:write, by actionStart a background job over every member.
GET /v1/jobs/:idany valid keyStatus, progress and result of a job.

Creating, listing and counting a segment never counts against your plan. Reading its members counts like any other read of contacts or companies. See Usage and metering.

Every segment has a type.

  • dynamic: a saved list of filters over contacts or over companies. It stores no member list. Its members are whichever records match the filters at the moment you use it, so a contact that starts to match is in the segment on the next request.
  • static: a fixed list of contacts that someone picked by hand on the Segments page of the dashboard. It holds contacts only.

POST /v1/segments creates dynamic segments. Static segments are created in the dashboard. Both kinds are listed, read and used through the API in the same way.

A segment is addressed by a ref: its id, or its handle if you gave it one. A handle is a short name you choose (us-customers), which is easier to keep in a script than an id.

The API has no endpoint that changes or deletes a segment. Its name, description and color are edited, and the segment is deleted, on the Segments page of the dashboard. The filters of a dynamic segment cannot be changed: create a new segment instead.

POST /v1/segments

Required scope: contacts:write for a contacts segment, companies:write for a companies segment.

Creates a dynamic segment. The filters are checked before anything is stored: a filter that is not valid is refused with 400 invalid_filter, and no segment is created.

FieldTypeRequiredDescription
namestringyesUp to 100 characters. Unique in the workspace.
entitystringyescontacts or companies.
filtersarrayyes1 to 10 filters, each { "field", "op", "value" }. A record is a member when it matches all of them. See Filters.
handlestringnoA short name to address the segment by: 1 to 64 lowercase letters, digits or hyphens, starting with a letter or a digit. Unique in the workspace.
descriptionstringnoUp to 500 characters.
colorstringnoA color for the segment in the dashboard. Up to 32 characters.
Terminal window
curl -X POST https://api.pathbound.ai/v1/segments \
-H "Authorization: Bearer $PATHBOUND_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "US customers",
"handle": "us-customers",
"entity": "contacts",
"filters": [
{ "field": "lifecyclestage", "op": "eq", "value": "customer" },
{ "field": "country", "op": "eq", "value": "United States" }
]
}'
{
"status": "success",
"segment": {
"id": "670b8f2e4c1a9d3e5f607182",
"name": "US customers",
"handle": "us-customers",
"type": "dynamic",
"entity": "contact",
"filters": [
{ "field": "lifecyclestage", "op": "eq", "value": "customer" },
{ "field": "country", "op": "eq", "value": "United States" }
],
"description": null,
"color": null,
"contact_count": 0,
"created_at": "2026-10-13T09:02:11.000Z"
}
}
FieldDescription
idThe segment’s id.
handleThe handle, or null.
typedynamic or static.
entitycontact or company. The request takes the plural (contacts), the response answers in the singular. A static segment is always contact.
filtersThe stored filters. Only on a dynamic segment.
contact_countFor a static segment, the number of contacts on its list. A dynamic segment keeps no count here: read count from Get a Segment.

A name or a handle that is already taken is answered with 409 and code conflict.

A segment’s filters are the API’s typed filters: each is { "field": "...", "op": "...", "value": ... }, with the fields, operators and values described under Filtering. They are the same as the filters parameter of GET /v1/contacts and GET /v1/companies, so you can try a set of filters on a list first and save it as a segment once it returns what you want.

GET /v1/schema lists the fields your workspace can filter on, with the type of each. Take field names from there: a source.field that is not on that list is either refused with 400 invalid_filter or matches nothing.

Which operators a field takes depends on its type. A property with no known type takes all of them.

TypeOperators
Texteq, neq, in, nin, contains, exists
Number, dateeq, neq, in, nin, gt, gte, lt, lte, exists
Booleaneq, neq, exists
List of textcontains, in, exists

A segment’s filters are stored as you send them and applied on every later read, so three details are worth getting right:

  • Case. eq, neq, in and nin compare the whole stored value, including case: "United States" does not match united states. contains ignores case.
  • Values. A value is converted to the field’s type, so "50" is accepted for a number and "2026-10-01" for a date. A value that cannot be converted is refused with 400 invalid_filter, and details names the field.
  • exists. With false it matches the records where the field is missing or null.
GET /v1/segments

Required scope: contacts:read or companies:read. Either one is enough.

Returns the workspace’s segments, static and dynamic, sorted by name. The list holds at most 200 segments and is not paged.

{
"status": "success",
"segments": [
{
"id": "670b8f2e4c1a9d3e5f607182",
"name": "US customers",
"handle": "us-customers",
"type": "dynamic",
"entity": "contact",
"filters": [
{ "field": "lifecyclestage", "op": "eq", "value": "customer" },
{ "field": "country", "op": "eq", "value": "United States" }
],
"description": null,
"color": null,
"contact_count": 0,
"created_at": "2026-10-13T09:02:11.000Z"
},
{
"id": "66f41d7a2b9c8e0f1a2b3c4d",
"name": "Webinar attendees",
"handle": null,
"type": "static",
"entity": "contact",
"description": "Picked after the October webinar",
"color": "#4f46e5",
"contact_count": 38,
"created_at": "2026-10-02T14:20:45.000Z"
}
]
}

The fields are those of Create a Segment.

GET /v1/segments/:ref

Required scope: companies:read for a companies segment, contacts:read for any other.

Returns one segment and how many records are in it right now. The members themselves are not in the response: list them with GET /v1/contacts?segment=.

ParameterTypeDescription
refstringThe segment’s id or its handle.
Terminal window
curl https://api.pathbound.ai/v1/segments/us-customers \
-H "Authorization: Bearer $PATHBOUND_API_KEY"
{
"status": "success",
"segment": {
"id": "670b8f2e4c1a9d3e5f607182",
"name": "US customers",
"handle": "us-customers",
"type": "dynamic",
"entity": "contact",
"filters": [
{ "field": "lifecyclestage", "op": "eq", "value": "customer" },
{ "field": "country", "op": "eq", "value": "United States" }
],
"description": null,
"color": null,
"contact_count": 0,
"created_at": "2026-10-13T09:02:11.000Z"
},
"count": 412
}

count is counted when you ask. For a dynamic segment it is the number of records that match the filters now.

The filters of a dynamic segment are checked again every time the segment is used, here and on every endpoint that takes a segment. If one of them names a source field that no longer exists, the request is answered with 400 invalid_filter.

EndpointParameterWhat it does
GET /v1/contacts, GET /v1/companiessegment (query)Only the members of one segment. Combines with every other filter, with count_only and with paging.
The same twosegments_any (query)Records that are in at least one of several segments: a comma-separated list of ids or handles. The first 50 are read.
POST /v1/aggregatesegment (body), filters.segments_anyGroup and count inside a segment. For entity contacts or companies only.
POST /v1/contacts/tags/bulk, POST /v1/companies/tags/bulksegment (body)Add or remove tags on every member, in place of a list of ids. When both are sent, segment is used.
POST /v1/meetings/querysegment (body)Meetings with the segment’s contacts. A companies segment matches anyone at its companies, and needs companies:read as well.
Terminal window
curl "https://api.pathbound.ai/v1/contacts?segment=us-customers&limit=50" \
-H "Authorization: Bearer $PATHBOUND_API_KEY"

Three rules hold on all of them:

  • The segment has to be for the records you ask for. A dynamic contacts segment on GET /v1/companies, or a companies segment on GET /v1/contacts, is refused with 400 segment_entity_mismatch. A static segment is not checked this way: it holds contacts, and on an endpoint that reads companies it is answered 200 and matches nothing. Use it where contacts are read.
  • An unknown segment is an error. It is answered with 404 segment_not_found, never with every record in the workspace.
  • An unknown entry in segments_any adds no members. If none of the entries names a segment, nothing matches.
POST /v1/segments/:ref/actions

Required scope: enrichment:write for apollo_enrich, actions:write for resend_add_to_segment and apollo_add_to_sequence. Both scopes are opt-in: a key has them only if they were selected when it was created. See Scopes.

Starts a background job that works through every member of the segment. The request returns at once with a job. Poll it with Get a Job.

ParameterTypeDescription
refstringThe segment’s id or its handle.
HeaderRequiredDescription
Idempotency-KeyyesAny non-empty string that is unique to this job. See Idempotency.

action is required and selects one of three jobs. The other fields belong to one action each.

Each action also needs its integration connected and one switch turned on by a workspace admin under MCP Tools in the dashboard. For these jobs the switches apply to API keys as well as to AI agents.

FieldTypeRequiredDescription
actionstringyesapollo_enrich, resend_add_to_segment or apollo_add_to_sequence.

Enriches every member with Apollo: contacts for a contacts segment, companies for a companies segment. Needs enrichment:write.

FieldTypeRequiredDescription
forcebooleannotrue enriches members that were enriched before, and pays for them again. Default false: they are skipped.

A member that Apollo finds costs about one Apollo credit. A member that was enriched before, or a company without a domain, is not sent to Apollo and costs nothing. The other skips can cost: a member that Apollo found and that could not be saved is counted in skipped, and its credit is in credits_consumed. The job draws on the workspace’s daily credit budget for enrichment, and stops when the budget is reached.

The switch is Enrich contacts & companies, for Apollo. This action always runs on Apollo, whichever data provider serves enrichment of single records.

Adds every contact of the segment that has an email address to a segment in your Resend account. A contact that is not in Resend yet is created there. This sends no email: it fills the audience you then send a broadcast to. Needs actions:write and a contacts segment.

FieldTypeRequiredDescription
resend_segment_idstringyesThe id of the Resend segment to add to. GET /v1/integrations/resend/segments lists them.

The switch is Manage segments, for Resend.

Enrolls every contact of the segment in an existing Apollo sequence. Apollo then emails them on the sequence’s schedule. Needs actions:write and a contacts segment.

FieldTypeRequiredDescription
sequence_idstringyesThe Apollo sequence id: 24 hexadecimal characters.
sender_account_idstringyesThe id of the Apollo mailbox the sequence sends from: 24 hexadecimal characters. There is no default. GET /v1/integrations/apollo/senders lists the mailboxes you may use.
start_atstringnoAn ISO 8601 time in the future. Contacts are enrolled paused and start then.
tagsstring[]noUp to 20 Pathbound tags, each up to 64 characters, added to every contact that was enrolled.

Contacts without an email address, contacts already in the sequence, and contacts that Apollo’s own safeguards refuse are skipped and counted by reason. Enrollments are capped per workspace per day, and a job that reaches the cap stops there.

The switch is Create and run sequences, for Apollo. This action is checked before a job is created: if Apollo is not connected, the switch is off, or the mailbox may not be used, the request is refused with the error codes described on the Apollo page, and nothing starts.

Terminal window
curl -X POST https://api.pathbound.ai/v1/segments/us-customers/actions \
-H "Authorization: Bearer $PATHBOUND_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: enrich-us-customers-2026-10-13" \
-d '{ "action": "apollo_enrich" }'
{
"status": "success",
"job_id": "670b91a74c1a9d3e5f6071a3",
"action": "apollo_enrich",
"job_status": "pending",
"entity": "contact",
"segment_ref": "us-customers",
"progress": { "processed": 0, "total": 412 },
"result": null,
"error": null,
"created_at": "2026-10-13T09:14:02.000Z",
"updated_at": "2026-10-13T09:14:02.000Z",
"match_count": 412,
"estimated_credits": 412
}
FieldDescription
job_idThe id to poll.
match_countHow many members the segment has now.
estimated_creditsOnly for apollo_enrich. An estimate of one credit per member, so it equals match_count. The job reports what was really spent in result.credits_consumed.

The other fields are those of Get a Job.

For apollo_enrich and resend_add_to_segment, whether the integration is connected and its switch is on is checked when the job runs, not when you start it. A job that cannot run ends with job_status: "failed" and the reason in error.

The job reads the segment’s members when it runs. For a dynamic segment that can be a slightly different set from the one match_count counted.

The Idempotency-Key identifies the job. A second request with the same key starts nothing: it is answered with 200 OK, "replay": true and the job as it stands now, without match_count or estimated_credits.

This endpoint differs from the convention across the API in three ways:

  • Only the key is compared. A request that reuses a key with a different segment or a different body still returns the first job. Use a new key for every job you mean to run.
  • A key is remembered for 30 days, as long as the job itself is kept, and not for 24 hours. To run the same action on the same segment again within that time, send a new key.
  • A replay returns the job in whatever state it is in, one that is still running or one that failed included. There is no 409 for a job in progress. To try a failed job again, send a new key.

A new key always starts a new job, and the job spends again.

GET /v1/jobs/:id

Required scope: none. Any valid API key of the workspace can read the workspace’s jobs.

ParameterTypeDescription
idstringThe job_id returned by Run an Action on a Segment.
Terminal window
curl https://api.pathbound.ai/v1/jobs/670b91a74c1a9d3e5f6071a3 \
-H "Authorization: Bearer $PATHBOUND_API_KEY"
{
"status": "success",
"job_id": "670b91a74c1a9d3e5f6071a3",
"action": "apollo_enrich",
"job_status": "running",
"entity": "contact",
"segment_ref": "us-customers",
"progress": { "processed": 150, "total": 412 },
"result": {
"requested": 150,
"enriched": 96,
"not_found": 21,
"skipped": 33,
"failed": 0,
"credits_consumed": 96,
"failures": []
},
"error": null,
"created_at": "2026-10-13T09:14:02.000Z",
"updated_at": "2026-10-13T09:15:40.000Z"
}

status says that this request succeeded. The state of the job is job_status.

FieldDescription
actionThe action the job runs.
job_statusSee Job statuses.
entitycontact or company: what the segment holds.
segment_refThe id or handle the job was started with.
progressprocessed members out of total.
resultCounters for the action, or null before the first batch. It is updated while the job runs.
errorWhy the job failed, or null.
job_statusMeaning
pendingAccepted and waiting to start.
runningWorking through the segment.
succeededFinished, and no member failed.
partialFinished or stopped early with some members not done: a batch failed, a daily cap was reached, or Apollo refused the rest. result says how many and why.
failedCould not run, or stopped on an error. error says why. result keeps what was done before that.

Poll until job_status is succeeded, partial or failed. A job has no fixed duration: it works in batches (25 members at a time for the two Apollo actions, 100 for Resend), paced to the provider’s rate limits, and progress moves once per batch. A job that fails is not retried.

A job can be read for 30 days after it was created. After that GET /v1/jobs/:id answers 404.

apollo_enrich

FieldDescription
requestedMembers in the batches that have completed so far.
enrichedMembers Apollo found and enriched.
not_foundMembers Apollo has no match for.
skippedMembers not enriched for another reason: enriched before (unless force is true), a company without a domain, or a request to Apollo that failed.
failedMembers in a batch that failed as a whole.
credits_consumedApollo credits spent.
failuresUp to 50 failed batches, each { "ids": [...], "error": "..." }.
stopped_reasonapollo_daily_credit_cap_reached when the job stopped at the daily cap. The job is then partial.

resend_add_to_segment

FieldDescription
addedContacts added to the Resend segment.
failedContacts Resend refused.
no_emailContacts without an email address, which were left out.
skippedAlways 0 for this action.
failuresAlways empty for this action.

apollo_add_to_sequence

FieldDescription
requestedContacts in the batches that have completed so far.
enrolledContacts now in the sequence.
skippedContacts not enrolled, with the count per reason in skip_reasons (for example no_email, already_enrolled, or the reason Apollo gave).
failedContacts in a batch that failed.
sequence_id, sequence_nameThe sequence.
sender_emailThe mailbox the sequence sends from.
failuresUp to 50 failed batches, each { "ids": [...], "error": "...", "code": "..." }.
stopped_reason, errorSet when the job stopped early: apollo_daily_enroll_cap_reached, or the code of the Apollo error that would have repeated for every batch. The job is then partial.

Errors use the common error format.

StatuscodeMeaning
400noneA field failed validation. details lists each one.
400invalid_filterA filter names a source field that does not exist, uses an operator the field’s type does not take, or carries a value of the wrong type. details names the field.
400IDEMPOTENCY_KEY_REQUIREDAn action was sent without the Idempotency-Key header.
400validation_errorThe action does not fit: resend_add_to_segment or apollo_add_to_sequence on a companies segment, a missing resend_segment_id, sequence_id or sender_account_id, or a start_at that is not in the future.
400segment_entity_mismatchA dynamic contacts segment was used where companies are read, or the reverse. A static segment used where companies are read is not an error: it matches nothing.
403auth_errorThe key lacks the scope the request needs. The message names it.
404not_foundNo segment has that id or handle, or no job has that id.
404segment_not_foundThe segment parameter of another endpoint names no segment.
409conflictA segment with that name or that handle already exists.
412field_redactedOnly for calls made through the MCP server in a workspace that masks personal data for AI agents: a filter names a masked contact field. A direct call with an API key never gets it.
412integration_missing, invalid_credentials, needs_reconsent, tool_disabledapollo_add_to_sequence only: Apollo is not connected, its connection has to be renewed, or Create and run sequences is off. No job was created.
400, 403sender_inactive, sender_not_allowedapollo_add_to_sequence only: the mailbox is inactive in Apollo, or is not one this workspace may send from. No job was created. The Apollo page lists the other errors Apollo can answer with.
422segment_invalidThe segment’s stored filters no longer resolve to any condition, so it is refused instead of matching every record.
429rate_limitedThe workspace has too many jobs waiting. Nothing was started: retry with the same Idempotency-Key after retry_after seconds.
500noneThe request could not be completed.