Pipeline
5 endpoints
/v1/pipeline/dealsList pipeline deals
Returns the paginated list of the account's deals (opportunities) (cursor v2).
Stable sort created_at DESC, id DESC.
A deal is NOT a separate entity in the database: it is a
client_contacts row with pipeline_stage_id IS NOT NULL. The set
of exposed fields is intentionally limited to what matters for
the pipeline view (contact details + stage + value + score). For the full
contact (custom_fields, tags, notes), use GET /v1/contacts/{id}.
Filters
?stage_id(uuid) — exact match on the pipeline stage.
Merged contacts
A contact absorbed by a merge no longer appears in the list: its history lives in the surviving contact, which is the only one listed.
No single GET
No GET /v1/pipeline/deals/{id} endpoint is exposed — to
retrieve an individual deal, use GET /v1/contacts/{id} or
filter this list by ?stage_id.
pipeline:readQuery parameters
| Name | Type | Required | Description |
|---|---|---|---|
stage_id | string | optional | Exact match on the pipeline stage. |
Responses
| Status | Description |
|---|---|
200 | Paginated list of deals. |
400 | The request is malformed (missing header, invalid JSON, invalid parameter). |
401 | Unknown API key or invalid format. |
403 | Authorization denied. Two causes, distinguished by |
429 | The per-window request quota has been exceeded for this API key or endpoint. |
500 | Unexpected server error. Always include the |
Example request
curl -X GET 'https://api.capturia.io/v1/pipeline/deals' \
-H 'Authorization: Bearer cap_live_<your-key>' \
-H 'Content-Type: application/json'/v1/pipeline/dealsPlace a contact in the pipeline
Places an existing contact in a pipeline stage (sets
pipeline_stage_id). Does not create a contact — contact_id must
already exist and belong to the calling account (otherwise 404 not_found).
The same applies to stage_id (404 not_found if the stage does not belong
to the account).
A contact_id that points to a contact absorbed by a merge returns
404 not_found: place the surviving contact instead. If the merge is
undone from the dashboard, the contact becomes accessible again under
its ID.
If the contact is already in a stage of the same pipeline, it is moved there. The move goes through the same engine as the dashboard: it appears in the contact's timeline, triggers the stage automations and the webhook, and counts as a human decision (the AI agent stops following up with this lead).
A contact filed in a different pipeline than the target stage's
is not moved: 422 business_rule_violation with
details.reason = other_pipeline (moving a contact between pipelines
is done from the dashboard). An archived contact is not moved
either (details.reason = contact_archived).
A disqualified lead does not change stage until its
disqualification is lifted: the request returns
409 lead_disqualified. Moving it to a lost stage, or to the
disqualification stage, is still accepted. You reopen the lead from
its contact in the dashboard.
pipeline:writeRequest body
| Property | Type | Required | Description |
|---|---|---|---|
contact_id | string | required | Contact to place in the pipeline. Must belong to the calling account. |
stage_id | string | required | Destination stage. Must belong to the calling account. |
Responses
| Status | Description |
|---|---|
201 | Deal placed in the pipeline. |
400 | Parameter or body validation failed. |
401 | Unknown API key or invalid format. |
403 | Authorization denied. Two causes, distinguished by |
404 | The requested resource doesn't exist (or isn't within the tenant's scope). |
409 | The targeted lead is disqualified: its stage can't change until the disqualification is lifted. Moving it to a lost stage, or to the disqualification stage, is still accepted. |
415 | The |
422 | The request is syntactically valid but violates a business rule. |
429 | The per-window request quota has been exceeded for this API key or endpoint. |
500 | Unexpected server error. Always include the |
Example request
curl -X POST 'https://api.capturia.io/v1/pipeline/deals' \
-H 'Authorization: Bearer cap_live_<your-key>' \
-H 'Content-Type: application/json' \
-d '{
"contact_id": "8f3b1a2c-9d4e-4f5a-b6c7-d8e9f0a1b2c3",
"stage_id": "1a2b3c4d-5e6f-7a8b-9c0d-1e2f3a4b5c6d"
}'/v1/pipeline/deals/{id}Move a deal to another stage
Moves a deal (pipeline_stage_id) to a new stage. Only the
stage move is exposed through this endpoint — to change the
deal's other fields (email, deal_value, etc.), use
PATCH /v1/contacts/{id}.
Preconditions
- The target deal must exist, belong to the calling account, and have
pipeline_stage_id IS NOT NULL, otherwise404 not_found. The ID of a contact absorbed by a merge also returns404 not_found: move the surviving contact. - The target stage must belong to the calling account, otherwise
404 not_found. - The lead must not be disqualified, otherwise
409 lead_disqualified. A lost stage or the disqualification stage is still an accepted target; otherwise, reopen the lead from its contact in the dashboard before retrying the request. - The target stage must belong to the deal's pipeline, and the contact
must not be archived, otherwise
422 business_rule_violation(details.reason=other_pipelineorcontact_archived).
The move goes through the same engine as the dashboard: it appears in the contact's timeline, triggers the stage automations and the webhook, and counts as a human decision (the AI agent stops following up with this lead).
pipeline:writeRequest body
| Property | Type | Required | Description |
|---|---|---|---|
stage_id | string | required | Destination stage. Must belong to the calling account. |
Responses
| Status | Description |
|---|---|
200 | Deal moved to the new stage. |
400 | Parameter or body validation failed. |
401 | Unknown API key or invalid format. |
403 | Authorization denied. Two causes, distinguished by |
404 | The requested resource doesn't exist (or isn't within the tenant's scope). |
409 | The targeted lead is disqualified: its stage can't change until the disqualification is lifted. Moving it to a lost stage, or to the disqualification stage, is still accepted. |
415 | The |
422 | The request is syntactically valid but violates a business rule. |
429 | The per-window request quota has been exceeded for this API key or endpoint. |
500 | Unexpected server error. Always include the |
Example request
curl -X PATCH 'https://api.capturia.io/v1/pipeline/deals/{id}' \
-H 'Authorization: Bearer cap_live_<your-key>' \
-H 'Content-Type: application/json' \
-d '{
"stage_id": "2b3c4d5e-6f7a-8b9c-0d1e-2f3a4b5c6d7e"
}'/v1/pipeline/deals/{id}Remove a deal from the pipeline
Removes the deal from the pipeline by setting pipeline_stage_id = NULL. The
contact is NOT deleted — it remains accessible via
GET /v1/contacts/{id}. To permanently delete the contact,
use DELETE /v1/contacts/{id}.
The target deal must exist, belong to the calling account, and have
pipeline_stage_id IS NOT NULL — otherwise 404 not_found. The ID of a
contact absorbed by a merge also returns 404 not_found: remove the
surviving contact. If the merge is undone from the dashboard, the
contact becomes accessible again under its ID. An archived contact stays in
its pipeline: 422 business_rule_violation (details.reason =
contact_archived).
pipeline:writeResponses
| Status | Description |
|---|---|
200 | Deal removed from the pipeline (the contact is not deleted). |
400 | Parameter or body validation failed. |
401 | Unknown API key or invalid format. |
403 | Authorization denied. Two causes, distinguished by |
404 | The requested resource doesn't exist (or isn't within the tenant's scope). |
422 | The request is syntactically valid but violates a business rule. |
429 | The per-window request quota has been exceeded for this API key or endpoint. |
500 | Unexpected server error. Always include the |
Example request
curl -X DELETE 'https://api.capturia.io/v1/pipeline/deals/{id}' \
-H 'Authorization: Bearer cap_live_<your-key>' \
-H 'Content-Type: application/json'/v1/pipeline/stagesList pipeline stages
Returns the full list of stages across the account's pipelines, sorted
by pipeline (in dashboard order), then by stage_order ASC.
Each stage names its pipeline (pipeline.name,
pipeline.is_default): an account with several pipelines can have
two stages with the same name. Each stage includes a
contact_count computed in real time (the number of the account's contacts
currently in that stage, excluding archived
and merged contacts — useful for pipeline KPIs). A stage hidden in
the dashboard is still listed, flagged is_hidden.
Non-paginated response
The endpoint returns all of the account's stages in a single call
(usually < 20 per account, max 50). The response format stays
list V2-compatible for integration consistency
(pagination.next_cursor=null, pagination.has_more=false,
pagination.limit and pagination.total_count equal the number of
stages returned).
Read-only
Creating, editing, and reordering stages is done in the
dashboard — no POST/PATCH/DELETE /v1/pipeline/stages endpoint
is exposed.
pipeline:readResponses
| Status | Description |
|---|---|
200 | Full list of pipeline stages (non-paginated). |
401 | Unknown API key or invalid format. |
403 | Authorization denied. Two causes, distinguished by |
429 | The per-window request quota has been exceeded for this API key or endpoint. |
500 | Unexpected server error. Always include the |
Example request
curl -X GET 'https://api.capturia.io/v1/pipeline/stages' \
-H 'Authorization: Bearer cap_live_<your-key>' \
-H 'Content-Type: application/json'