Pipelines
Read, create, rename and delete Deal pipelines and their stages.
A pipeline is an ordered set of stages that Deals move through. Every workspace has one default pipeline.
The pipeline object#
| Field | Type | Notes |
|---|---|---|
id |
string | UUID. |
name |
string | |
description |
string or null | |
is_default |
boolean | true for the workspace's default pipeline. |
stages |
array | The pipeline's stages, in order. See below. |
created_at |
string | ISO 8601 timestamp. |
updated_at |
string | ISO 8601 timestamp. |
Each stage:
| Field | Type | Notes |
|---|---|---|
id |
string | UUID of the pipeline stage. This is not a Deal's stage_id; see Stage IDs. |
name |
string | |
code |
string | Links the stage to its deal stage ID. |
sort_order |
integer | Position in the pipeline, lowest first. |
is_won |
boolean | Deals at this stage count as won. |
is_lost |
boolean | Deals at this stage count as lost. |
{
"id": "0d9b7f5e-3c1a-4e8f-a6b4-2d0c8e6a4f92",
"name": "New business",
"description": null,
"is_default": true,
"created_at": "2026-03-02T18:21:07.000Z",
"updated_at": "2026-03-02T18:21:07.000Z",
"stages": [
{ "id": "e2c4a6b8-0d1f-4a3c-9e5b-7d9f1b3d5f70", "name": "Lead In", "code": "lead_in", "sort_order": 0, "is_won": false, "is_lost": false },
{ "id": "f3d5b7c9-1e2a-4b4d-8f6c-8eaf2c4e6a81", "name": "Discovery", "code": "discovery", "sort_order": 1, "is_won": false, "is_lost": false },
{ "id": "a4e6c8d0-2f3b-4c5e-9a7d-9fb03d5f7b92", "name": "Proposal", "code": "proposal", "sort_order": 2, "is_won": false, "is_lost": false },
{ "id": "b5f7d9e1-3a4c-4d6f-8b8e-a0c14e6a8ca3", "name": "Negotiation", "code": "negotiation", "sort_order": 3, "is_won": false, "is_lost": false },
{ "id": "c6a8e0f2-4b5d-4e7a-9c9f-b1d25f7b9db4", "name": "Closed Won", "code": "closed_won", "sort_order": 4, "is_won": true, "is_lost": false },
{ "id": "d7b9f1a3-5c6e-4f8b-8da0-c2e36a8caec5", "name": "Closed Lost", "code": "closed_lost", "sort_order": 5, "is_won": false, "is_lost": true }
]
}
List pipelines#
GET /pipelines · Scope: pipeline:read
Returns pipelines newest first, each with its stages.
Query parameters#
| Parameter | Type | Required | Notes |
|---|---|---|---|
limit |
integer | No | 1 to 100. Default 20. |
cursor |
string | No | meta.cursor from the previous page. |
Example#
curl https://crm.empiraa.app/api/v1/pipelines \
-H "Authorization: Bearer YOUR_API_KEY"
const res = await fetch("https://crm.empiraa.app/api/v1/pipelines", {
headers: { Authorization: "Bearer YOUR_API_KEY" },
});
const { data } = await res.json();
const defaultPipeline = data.find((p) => p.is_default);
Response 200#
An array of pipeline objects in data; meta has cursor and hasMore.
Create a pipeline#
POST /pipelines · Scope: pipeline:write
Creates a pipeline with the stages you send. New pipelines are never the default.
Request body#
| Field | Type | Required | Notes |
|---|---|---|---|
name |
string | Yes | |
description |
string or null | No | |
stages |
array | No | The stages, in order. If you leave this out, the pipeline has no stages, and Deals can't be placed in it. |
stages[].name |
string | Yes, per stage | |
stages[].code |
string | Yes, per stage | A short identifier such as demo_booked. Reuse a standard code (such as proposal) to share its deal stage ID. |
stages[].sort_order |
integer | No | Defaults to the stage's position in the array. |
stages[].is_won |
boolean | No | Default false. |
stages[].is_lost |
boolean | No | Default false. |
Example#
curl -X POST https://crm.empiraa.app/api/v1/pipelines \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Partnerships",
"stages": [
{ "name": "Discovery", "code": "discovery" },
{ "name": "Proposal", "code": "proposal" },
{ "name": "Signed", "code": "closed_won", "is_won": true },
{ "name": "Lost", "code": "closed_lost", "is_lost": true }
]
}'
const res = await fetch("https://crm.empiraa.app/api/v1/pipelines", {
method: "POST",
headers: { Authorization: "Bearer YOUR_API_KEY", "Content-Type": "application/json" },
body: JSON.stringify({
name: "Partnerships",
stages: [
{ name: "Discovery", code: "discovery" },
{ name: "Proposal", code: "proposal" },
{ name: "Signed", code: "closed_won", is_won: true },
{ name: "Lost", code: "closed_lost", is_lost: true },
],
}),
});
Response 201#
The new pipeline object, with its stages, in data.
Errors#
| Status | Code | When |
|---|---|---|
| 400 | INVALID_REQUEST |
name is missing or not a string. |
Get a pipeline#
GET /pipelines/{id} · Scope: pipeline:read
Path parameters#
| Parameter | Type | Notes |
|---|---|---|
id |
string | Pipeline ID. |
Example#
curl https://crm.empiraa.app/api/v1/pipelines/0d9b7f5e-3c1a-4e8f-a6b4-2d0c8e6a4f92 \
-H "Authorization: Bearer YOUR_API_KEY"
await fetch("https://crm.empiraa.app/api/v1/pipelines/0d9b7f5e-3c1a-4e8f-a6b4-2d0c8e6a4f92", {
headers: { Authorization: "Bearer YOUR_API_KEY" },
});
Response 200#
The pipeline object in data.
Errors#
| Status | Code | When |
|---|---|---|
| 404 | NOT_FOUND |
No pipeline with that ID in this workspace. |
Update a pipeline#
PATCH /pipelines/{id} · Scope: pipeline:write
Renames a pipeline or changes its description. Stages can't be changed through the API; edit them in Signal.
Request body#
| Field | Type | Notes |
|---|---|---|
name |
string | |
description |
string or null |
Example#
curl -X PATCH https://crm.empiraa.app/api/v1/pipelines/0d9b7f5e-3c1a-4e8f-a6b4-2d0c8e6a4f92 \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"name": "Direct sales"}'
await fetch("https://crm.empiraa.app/api/v1/pipelines/0d9b7f5e-3c1a-4e8f-a6b4-2d0c8e6a4f92", {
method: "PATCH",
headers: { Authorization: "Bearer YOUR_API_KEY", "Content-Type": "application/json" },
body: JSON.stringify({ name: "Direct sales" }),
});
Response 200#
The updated pipeline object, with its stages, in data.
Errors#
| Status | Code | When |
|---|---|---|
| 404 | NOT_FOUND |
No pipeline with that ID in this workspace. |
Delete a pipeline#
DELETE /pipelines/{id} · Scope: pipeline:write
Deletes a pipeline and its stages. You can't delete the default pipeline, or a pipeline that still has Deals in it or Deals using its stages. Move those Deals to another pipeline first.
Example#
curl -X DELETE https://crm.empiraa.app/api/v1/pipelines/0d9b7f5e-3c1a-4e8f-a6b4-2d0c8e6a4f92 \
-H "Authorization: Bearer YOUR_API_KEY"
await fetch("https://crm.empiraa.app/api/v1/pipelines/0d9b7f5e-3c1a-4e8f-a6b4-2d0c8e6a4f92", {
method: "DELETE",
headers: { Authorization: "Bearer YOUR_API_KEY" },
});
Response 204#
No body.
Errors#
| Status | Code | When |
|---|---|---|
| 400 | INVALID_REQUEST |
The pipeline is the workspace's default ("Cannot delete the default pipeline"). |
| 404 | NOT_FOUND |
No pipeline with that ID in this workspace. |
| 500 | SERVER_ERROR |
The pipeline still has Deals. |

