Pipelines

The REST API endpoints for managing pipelines and their stages within your organization.

What a pipeline is

A pipeline is an ordered set of stages a deal moves through. Stages are configuration, not a fixed list: your organization names them and orders them itself, and may keep several pipelines side by side.

Every new organization is created with one pipeline, Sales, holding four stages in order — New, Qualified, Proposal and Negotiation. The names follow the language of whoever created the organization, so an organization created in Portuguese is born with Vendas and Novo, Qualificado, Proposta, Negociação. They are ordinary names once written: changing your language later renames nothing that already exists. Rename them, add your own, or delete the pipeline outright once you have built the one you want.

Every pipeline has a currency — an uppercase ISO 4217 code such as "USD" or "BRL" — that every deal on it is expressed in. A deal has no currency of its own: its responses report the pipeline's. A new pipeline starts in your organization's default currency — USD until an owner or admin changes it (see Currency) — unless you name another when creating it. Nothing converts between currencies, and no total ever adds amounts held in different ones.

A pipeline's currency can change at any time, even while it holds deals. The change relabels those deals rather than converting them: a deal worth USD 1,000 becomes EUR 1,000, its stored value untouched. Change it only to correct the currency a pipeline was set up in, not to move money between currencies.

A stage's position is presentation only. Reordering the stages of a pipeline never touches a deal: a deal stays in the stage it was in, which simply appears somewhere else in the order. Reordering renumbers the whole pipeline in one operation — see Reorder Pipeline Stages.

Deleting a stage that still holds deals requires saying where those deals should go. Pass moveToStageId on the delete request, naming another stage of the same pipeline; without it the request is refused and tells you how many deals are in the way.

Configuring pipelines in the app

Everything below is also available without the API, under Settings → Pipelines. Like the rest of the settings area it is open to organization owners and admins only; a member never sees this screen.

The screen is a list, one row per pipeline: its name, how many stages it has, and when it was created. Sort it by name or by creation date and page through it. There is no search field and no filters — an organization keeps a handful of pipelines, not a directory of them.

New pipeline, at the top right, creates one straight away: there is no form to fill in first. It arrives with a default name and the same four default stages, named in your language, and its panel opens on it, so you land where the stages are edited rather than being sent there afterwards. It is a real pipeline from that moment — closing the panel without touching anything leaves it in the list, where you delete it like any other. Names are not numbered and need not be unique, so clicking twice leaves you with two pipelines of the same name; renaming is the first thing the open panel invites.

Clicking a pipeline's name opens it in a side panel: the name and the currency at the top, the stages in order below. Save changes in the panel's footer saves the name and the currency and closes the panel; Cancel closes it and discards what you typed. Everything you do to the stages saves as you do it, so it is kept either way. From the panel you can:

  • Add a stage — type its name in the field under the list. New stages append at the end.
  • Rename the pipeline, in the field at the top, or any of its stages.
  • Change the currency — a new pipeline arrives in your default currency and is changed here. When the pipeline already holds deals, saving a new currency first asks you to confirm, naming how many deals will be relabeled and that their values are not converted.
  • Reorder stages by dragging the handle on the left of a row. The list keeps the new order immediately and saves it in the background; if the save fails the stages go back where they were and a message says so. The handle is also reachable from the keyboard: focus it, press space, move with the arrow keys, and press space again to drop.
  • Delete a stage. A stage that still holds deals asks which of the pipeline's other stages they should move to, then deletes it — the same rule the API enforces with moveToStageId. The moved deals count their time in the destination stage from the moment they arrive, and the deleted stage leaves their funnels along with the rest of the pipeline's history of it.

A whole pipeline is deleted from its row's ⋯ menu in the list, not from inside the panel, and after a confirmation; deleting it takes its stages with it. A pipeline that still holds deals anywhere in it is kept instead: the delete reports a failure rather than taking those deals down with the stages. Empty it first.

Endpoints

All endpoints require a valid API key in the Authorization header. See Authentication for details.

Base URL: /api/v1

List Pipelines

GET /api/v1/pipelines

Returns every pipeline in your organization, each with its stages ordered by position.

Returns { "data": [...], "total": number }.


Get Pipeline

GET /api/v1/pipelines/:id

Create Pipeline

POST /api/v1/pipelines
Content-Type: application/json
FieldTypeRequiredDescription
namestringNoPipeline name (1-200 characters). Defaults to "New pipeline"
localestringNoen or pt-BR. The language of the default names. Defaults to en
currencystringNoAn uppercase ISO 4217 code from the supported list, such as EUR. Defaults to the organization's default currency

A pipeline is created with the four default stages already in it — New, Qualified, Proposal and Negotiation — at positions 0 to 3, and the response carries them in stages. Add more with Create Pipeline Stage, or delete the ones you do not want.

Pipeline names are neither numbered nor required to be unique, so creating two pipelines without a name leaves you with two called "New pipeline".

locale decides the language of the default pipeline and stage names, and nothing else. The Accept-Language header is ignored: name the language in the body if you want anything other than English.


Update Pipeline

PATCH /api/v1/pipelines/:id
Content-Type: application/json
FieldTypeRequiredDescription
namestringNoPipeline name (1-200 characters)
currencystringNoAn uppercase ISO 4217 code from the supported list

A new currency relabels every deal on the pipeline — their values are kept as they are, never converted. A code outside the supported list, or one written in lowercase, is refused with 422.


Delete Pipeline

DELETE /api/v1/pipelines/:id

Returns { "success": true } on success. Deleting a pipeline deletes its stages with it.

A pipeline that still holds deals — in any of its stages, whatever their status — is not deleted. Its stages would cascade away and take the deals with them, so the request is refused rather than destroying them silently, and unlike a stage delete there is nowhere on the same board to move them to. The request responds 409 with the number of deals that are in the way, counted across every stage of the pipeline, so you can report it without a second request:

{
  "error": "Pipeline still has deals in it",
  "dealCount": 12
}

Empty the pipeline first. A deal can only be moved between stages of its own pipeline, so there is no way to carry it across to another one: delete the deals, then delete the pipeline. See Deals.


Reorder Pipeline Stages

PATCH /api/v1/pipelines/:id/stage-order
Content-Type: application/json
FieldTypeRequiredDescription
stageIdsstring[]YesEvery stage of the pipeline, exactly once, in the new order

Returns { "data": [...] } — the stages renumbered from 0 with no gaps. An order that omits a stage, repeats one, or names a stage from another pipeline is refused with 400 and nothing is renumbered. Reordering never moves a deal.


List Pipeline Stages

GET /api/v1/pipeline-stages?pipelineId=UUID_HERE
ParameterTypeRequiredDescription
pipelineIdstringYesThe pipeline whose stages to list

Returns { "data": [...], "total": number }, ordered by position.


Create Pipeline Stage

POST /api/v1/pipeline-stages
Content-Type: application/json
FieldTypeRequiredDescription
pipelineIdstringYesThe pipeline the stage belongs to
namestringYesStage name (1-200 characters)

A new stage is always appended at the end of the pipeline. Move it with Reorder Pipeline Stages.


Get Pipeline Stage

GET /api/v1/pipeline-stages/:id

Update Pipeline Stage

PATCH /api/v1/pipeline-stages/:id
Content-Type: application/json

Body: name. Order is not editable here — reordering renumbers every stage of the pipeline at once, through Reorder Pipeline Stages.


Delete Pipeline Stage

DELETE /api/v1/pipeline-stages/:id
Content-Type: application/json
FieldTypeRequiredDescription
moveToStageIdstringWhen the stage holds dealsAnother stage of the same pipeline to move them to

Returns { "success": true } on success.

A stage that still holds deals cannot be deleted on its own. The request responds 409 with the number of deals that are in the way, so you can ask where to move them without a second request:

{
  "error": "Pipeline stage still has deals in it",
  "dealCount": 12
}

Repeat the request with moveToStageId set, and the deals move to that stage as the deleted one goes away. Each moved deal is recorded as entering the destination at that moment, so its stage funnel keeps counting; the entries it had for the deleted stage go with the stage.


Response shape

A pipeline:

{
  "id": "0c1f5b8e-...",
  "organizationId": "9a2d...",
  "name": "Sales",
  "currency": "USD",
  "createdAt": "2026-09-01T14:54:01.000Z",
  "updatedAt": "2026-09-01T14:54:01.000Z",
  "stages": [
    {
      "id": "4b7e...",
      "organizationId": "9a2d...",
      "pipelineId": "0c1f5b8e-...",
      "name": "New",
      "position": 0,
      "createdAt": "2026-09-01T14:54:01.000Z",
      "updatedAt": "2026-09-01T14:54:01.000Z"
    }
  ]
}

stages holds every stage of the pipeline, in position order — four of them on a pipeline that has just been created. currency is the code every deal's value on this pipeline is expressed in; a pipeline created through the API without one starts in the organization's default currency.

The stage endpoints return the stage on its own, without the surrounding pipeline.

createdAt and updatedAt are ISO 8601 in UTC, and position is a raw number. The API is not localized, whatever language the app itself is showing you — see Data Formats.


Examples

cURL

# List pipelines
curl https://your-app.com/api/v1/pipelines \
  -H "Authorization: Bearer ba_a1b2c3d4e5f6..."

# Create a pipeline — comes back with the four default stages
curl -X POST https://your-app.com/api/v1/pipelines \
  -H "Authorization: Bearer ba_a1b2c3d4e5f6..." \
  -H "Content-Type: application/json" \
  -d '{"name":"Renewals"}'

# Every field is optional: this one is called "Novo funil", with Portuguese stages
curl -X POST https://your-app.com/api/v1/pipelines \
  -H "Authorization: Bearer ba_a1b2c3d4e5f6..." \
  -H "Content-Type: application/json" \
  -d '{"locale":"pt-BR"}'

# A pipeline negotiated in euros
curl -X POST https://your-app.com/api/v1/pipelines \
  -H "Authorization: Bearer ba_a1b2c3d4e5f6..." \
  -H "Content-Type: application/json" \
  -d '{"name":"EMEA","currency":"EUR"}'

# Change a pipeline's currency — its deals are relabeled, not converted
curl -X PATCH https://your-app.com/api/v1/pipelines/UUID_HERE \
  -H "Authorization: Bearer ba_a1b2c3d4e5f6..." \
  -H "Content-Type: application/json" \
  -d '{"currency":"BRL"}'

# Add a stage to it
curl -X POST https://your-app.com/api/v1/pipeline-stages \
  -H "Authorization: Bearer ba_a1b2c3d4e5f6..." \
  -H "Content-Type: application/json" \
  -d '{"pipelineId":"UUID_HERE","name":"Contacted"}'

# Rename a stage
curl -X PATCH https://your-app.com/api/v1/pipeline-stages/UUID_HERE \
  -H "Authorization: Bearer ba_a1b2c3d4e5f6..." \
  -H "Content-Type: application/json" \
  -d '{"name":"Qualified"}'

# Delete a stage, moving its deals elsewhere
curl -X DELETE https://your-app.com/api/v1/pipeline-stages/UUID_HERE \
  -H "Authorization: Bearer ba_a1b2c3d4e5f6..." \
  -H "Content-Type: application/json" \
  -d '{"moveToStageId":"OTHER_UUID_HERE"}'

JavaScript / TypeScript

const API_KEY = "ba_a1b2c3d4e5f6...";
const BASE_URL = "https://your-app.com/api/v1";

// List pipelines
const response = await fetch(`${BASE_URL}/pipelines`, {
  headers: { Authorization: `Bearer ${API_KEY}` },
});
const { data, total } = await response.json();

// Add a stage to the first pipeline
const createRes = await fetch(`${BASE_URL}/pipeline-stages`, {
  method: "POST",
  headers: {
    Authorization: `Bearer ${API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    pipelineId: data[0].id,
    name: "Contacted",
  }),
});
const { data: newStage } = await createRes.json();

Python

import requests

API_KEY = "ba_a1b2c3d4e5f6..."
BASE_URL = "https://your-app.com/api/v1"

# List pipelines
response = requests.get(
    f"{BASE_URL}/pipelines",
    headers={"Authorization": f"Bearer {API_KEY}"},
)
data = response.json()

# Add a stage to the first pipeline
response = requests.post(
    f"{BASE_URL}/pipeline-stages",
    headers={"Authorization": f"Bearer {API_KEY}"},
    json={"pipelineId": data["data"][0]["id"], "name": "Contacted"},
)
new_stage = response.json()

On this page