> ## Documentation Index
> Fetch the complete documentation index at: https://docs.connie.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Lifecycle stages

> The HubSpot/Salesforce vocabulary your reps already know — Lead, MQL, SQL, Opportunity, Customer, Disqualified — wired in by convention so handoffs don't need translation.

## What lifecycle stages are

A lifecycle stage describes where a Contact sits in your sales funnel as a person — independent of any specific deal they're part of. The same vocabulary every B2B sales team uses: **Lead → MQL → SQL → Opportunity → Customer**, with **Disqualified** as the terminal off-ramp.

Connie speaks this vocabulary as a **convention on the Contacts `Stage` column**, not a separate entity. There's no lifecycle picker UI, no new table, no special migration. Just a workspace config field plus a helper that downstream analytics read.

## The default seed

Every new workspace gets this list out of the box. Override it any time without touching code:

| Value | Label | Terminal? | Order |
| - | - | - | - |
| Lead | Lead | | 1 |
| MQL | Marketing-qualified | | 2 |
| SQL | Sales-accepted | | 3 |
| Opportunity | Opportunity | | 4 |
| Customer | Customer | ✓ | 5 |
| Disqualified | Disqualified | ✓ | 99 |

**Terminal stages** exit the funnel. They're how analytics distinguish "won" / "lost" from "still in flight" — for example, the rep performance breakdown (G.4) computes win-rate by counting `is_terminal: true && value == 'Customer'` against the total terminal count.

## Where it lives

`workspaces.settings.lifecycle_stages` — a JSON array on the existing settings blob. No new table.

When the field is unset, the API returns the default seed above with `is_default: true`. As soon as you PATCH a custom list, that's what reads back.

## Configuring it

<CodeGroup>
  ```bash GET theme={null}
  curl -X GET \
    https://app.conigma.io/api/workspaces/<workspaceId>/lifecycle-stages \
    -H "Authorization: Bearer <token>"
  ```

  ```bash PATCH (custom list) theme={null}
  curl -X PATCH \
    https://app.conigma.io/api/workspaces/<workspaceId>/lifecycle-stages \
    -H "Authorization: Bearer <token>" \
    -H "Content-Type: application/json" \
    -d '{
      "lifecycle_stages": [
        { "value": "New",       "label": "New",       "is_terminal": false, "order": 1 },
        { "value": "Active",    "label": "Active",    "is_terminal": false, "order": 2 },
        { "value": "Churned",   "label": "Churned",   "is_terminal": true,  "order": 99 }
      ]
    }'
  ```

  ```bash PATCH (reset to default) theme={null}
  curl -X PATCH \
    https://app.conigma.io/api/workspaces/<workspaceId>/lifecycle-stages \
    -H "Authorization: Bearer <token>" \
    -H "Content-Type: application/json" \
    -d '{ "lifecycle_stages": null }'
  ```
</CodeGroup>

## Validation rules

The PATCH endpoint enforces two invariants that the schema alone can't:

* **Distinct values.** Two stages with the same `value` (case-insensitive) would make the Stage cell ambiguous.
* **At least one terminal.** Without a terminal stage, the win-rate analytics in G.4 divide by zero and emit a meaningless number — so the endpoint rejects a list where every entry has `is_terminal: false`.

If you need a funnel without an explicit Customer step (e.g., consumer onboarding), still mark the last step `is_terminal: true` so analytics treat it as the end.

## How analytics read it

The `isTerminalLifecycleStage(stages, value)` helper in `@conigma/shared/lifecycle-stages` resolves a stage value against the configured list — case-insensitively — and returns whether it's a terminal stage. G.4 (rep performance) calls this to split deals into "completed" (terminal) and "in-flight" buckets before computing win-rate and cycle-time.

This is the only thing reading the config today. Future analytics (G.1 funnel conversion, G.2 stage velocity) can plug into the same helper.

## What this is NOT

* ❌ A lifecycle picker UI. The Contacts `Stage` column's existing select-options dropdown is the picker.
* ❌ A new entity / lookup table. It's a JSON array on `workspaces.settings`.
* ❌ A migration that touches existing Stage column data. Existing workspaces keep whatever Stage values they already configured.
* ❌ A soft-sync warning system between this config and the Stage column's actual select-options. They drift independently by design; the config informs analytics, the column drives data entry.

## When in doubt

Treat lifecycle stages as **vocabulary**, not as a feature. The shape exists so cross-tool handoffs between Connie and a customer's existing HubSpot/Salesforce instance don't require translating "Connie's Stage" into "their Lifecycle Stage." If you find yourself building UI around it, you've probably scoped past the design.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.