Pipeline Stages

Deals move through pipelines made up of ordered stages. Understanding the stage model is required for any feature that reads or moves deals.

Tables

Table Purpose
pipelines Pipeline definitions (name, type, tenant)
pipeline_stages Ordered stages belonging to a pipeline
deals Each deal has a stage_id pointing to a stage

Pipeline types

The pipeline_type column on the pipelines table controls behavior. Valid values:

pipeline_type Description
sales Standard sales pipeline — default type
onboarding Post-sale onboarding flow
support Support ticket pipeline
custom Freeform — no enforced stage semantics

Always use one of these exact strings. An unknown pipeline_type will not cause a DB error but will break pipeline-specific UI rendering and automation triggers.

Stage schema

SELECT id, id_pipeline, id_company, name, sort_order,
       is_won, is_lost, color
FROM pipeline_stages
WHERE id_company = :id_company
  AND id_pipeline = :id_pipeline
ORDER BY sort_order ASC;

Key columns:

Column Notes
sort_order Visual order in the pipeline board
is_won Boolean — marks this as the "won" terminal stage
is_lost Boolean — marks this as the "lost" terminal stage
color Hex string for the stage label

Only one stage per pipeline should have is_won = true and one should have is_lost = true. These drive the deal.won and deal.lost automation triggers.

Moving a deal

UPDATE deals
SET stage_id = :new_stage_id,
    updated_at = NOW()
WHERE id = :id
  AND id_company = :id_company
  AND deleted_at IS NULL;

Always verify the target stage belongs to the same id_company before updating — never trust a stage_id from user input directly.

-- Verify stage ownership before moving
SELECT id FROM pipeline_stages
WHERE id = :stage_id AND id_company = :id_company;
-- If 0 rows returned, reject the move

Creating a pipeline and stages

-- 1. Create the pipeline
INSERT INTO pipelines (id_company, name, pipeline_type)
VALUES (:id_company, 'New Sales Pipeline', 'sales');

-- 2. Add stages in order
INSERT INTO pipeline_stages
  (id_pipeline, id_company, name, sort_order, is_won, is_lost, color)
VALUES
  (:pipeline_id, :id_company, 'New Lead',    1, false, false, '#94a3b8'),
  (:pipeline_id, :id_company, 'Contacted',   2, false, false, '#60a5fa'),
  (:pipeline_id, :id_company, 'Proposal',    3, false, false, '#f59e0b'),
  (:pipeline_id, :id_company, 'Won',         4, true,  false, '#22c55e'),
  (:pipeline_id, :id_company, 'Lost',        5, false, true,  '#ef4444');

Querying deals by stage

SELECT d.*, ps.name AS stage_name, ps.color AS stage_color
FROM deals d
JOIN pipeline_stages ps ON ps.id = d.stage_id
WHERE d.id_company = :id_company
  AND d.deleted_at IS NULL
  AND d.stage_id = :stage_id;