VitrinaAPI

Pipelines

The boards work moves across, and the versioned templates one is built from.

Beta
This may change at any time, with a changelog entry and a notice to recent callers — see versioning.

Download the full API pública projection: openapi.json.

Move leads across the board explains this resource in prose, with runnable examples.

MethodPathWhat it does
GET/pipeline-templatesList pipeline templates
POST/pipeline-templates/{key}/applyApply a pipeline template to this workspace
GET/pipeline-templates/{key}/previewPreview a localized pipeline template
GET/pipelinesList pipelines
POST/pipelinesCreate pipeline
DELETE/pipelines/{id}Delete pipeline
GET/pipelines/{id}Get pipeline
PUT/pipelines/{id}Update pipeline

GET /pipeline-templates

List pipeline templates

The shipped, versioned board templates, localized to the resolved locale (es-CL → es, anything unshipped → en). Filter by kind and by vertical — the platform ships generic templates plus ones built for a particular vertical (automotive, healthcare), and a workspace may apply any of them. locales_available says which languages a template can be materialised in.

ParameterInTypeRequiredConstraints
kindquerysales \ticket \vehicle
verticalquerygeneric \automotive \healthcare
localequerystringnomín. 1, máx. 35
curl https://api.vitrinadev.com/api/v1/pipeline-templates \
  -H "Authorization: Bearer $VITRINA_KEY"

Example response (200)

{
  "data": [
    {
      "key": "generic_sales",
      "version": 1,
      "kind": "sales",
      "vertical": "generic",
      "name": "Ventas",
      "description": "Pipeline de ventas general: sigue una oportunidad comercial desde la primera consulta concreta hasta un cierre ganado, perdido o no calificado.",
      "stage_count": 9,
      "locale": "es",
      "locales_available": [
        "en",
        "es"
      ]
    },
    {
      "key": "clinic_treatments",
      "version": 1,
      "kind": "sales",
      "vertical": "healthcare",
      "name": "Tratamientos",
      "description": "Pipeline comercial de la clínica: desde una consulta de precio hasta un tratamiento presupuestado, aceptado e iniciado.",
      "stage_count": 7,
      "locale": "es",
      "locales_available": [
        "en",
        "es"
      ]
    }
  ],
  "meta": {
    "total": 2
  }
}

Answers: 200 · 400 · 401 · 403 · 404 · 409 · 429

POST /pipeline-templates/{key}/apply

Apply a pipeline template to this workspace

Materialises the template into tenant-owned, fully editable pipeline + stage rows — after this the board is yours, and editing it never diverges from anything. IDEMPOTENT per (workspace, template key): a second call returns the existing board with already_applied: true and changes nothing. A workspace holds exactly one FALLBACK board per kind — the board a card lands on when nobody named one. is_fallback: true promotes this board and demotes the current holder of that kind; false is ignored (promote the other board instead, so there is never a moment with no fallback). 400 when is_fallback is asked for and another board of that kind already holds the flag.

ParameterInTypeRequiredConstraints
keypathstringyesmín. 1, máx. 80

Body

FieldTypeRequiredConstraints
localestring—mín. 1, máx. 35
versioninteger—> 0
name_overridestring—mín. 1, máx. 120
is_fallbackboolean—por defecto false
curl -X POST https://api.vitrinadev.com/api/v1/pipeline-templates/<id>/apply \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "locale": "es",
    "name_override": "Ventas usados",
    "is_fallback": false
  }'

Example response (200)

{
  "data": {
    "already_applied": false,
    "template": {
      "key": "generic_sales",
      "version": 1,
      "locale": "es"
    },
    "pipeline": {
      "id": "44444444-0000-4000-8000-000000000001",
      "tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
      "name": "Ventas usados",
      "description": "Sigue una oportunidad comercial desde la primera consulta concreta hasta un cierre ganado, perdido o no calificado.",
      "slug": "generic-sales",
      "kind": "sales",
      "is_fallback": true,
      "template_key": "generic_sales",
      "template_version": 1,
      "template_locale": "es",
      "created_at": "2026-09-04T19:07:05.226Z",
      "updated_at": "2026-09-04T19:07:05.226Z"
    },
    "stages": [
      {
        "id": "55555555-0000-4000-8000-000000000001",
        "tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
        "pipeline_id": "44444444-0000-4000-8000-000000000001",
        "name": "Nuevo",
        "description": "Existe una consulta concreta, pero todavía no hubo contacto sustantivo ni calificación.",
        "slug": "new",
        "position": 0,
        "category": "open",
        "is_terminal": false,
        "won_state": null,
        "sla_days": null,
        "allowed_transitions_to": [
          "55555555-0000-4000-8000-000000000002",
          "55555555-0000-4000-8000-000000000006",
          "55555555-0000-4000-8000-000000000007"
        ],
        "ai_agent_id": null,
        "ai_agent_graph_id": null,
        "is_human": false,
        "is_marketing": false,
        "template_stage_key": "new",
        "created_at": "2026-09-04T19:07:05.226Z"
      },
      {
        "id": "55555555-0000-4000-8000-000000000002",
        "tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
        "pipeline_id": "44444444-0000-4000-8000-000000000001",
        "name": "Contactado",
        "description": "Alguien del equipo hizo contacto real y está entendiendo la necesidad.",
        "slug": "contacted",
        "position": 1,
        "category": "open",
        "is_terminal": false,
        "won_state": null,
        "sla_days": 3,
        "allowed_transitions_to": [
          "55555555-0000-4000-8000-000000000003",
          "55555555-0000-4000-8000-000000000006",
          "55555555-0000-4000-8000-000000000007"
        ],
        "ai_agent_id": null,
        "ai_agent_graph_id": null,
        "is_human": false,
        "is_marketing": false,
        "template_stage_key": "contacted",
        "created_at": "2026-09-04T19:07:05.226Z"
      },
      {
        "id": "55555555-0000-4000-8000-000000000006",
        "tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
        "pipeline_id": "44444444-0000-4000-8000-000000000001",
        "name": "Ganado",
        "description": "La oportunidad se cerró a favor.",
        "slug": "won",
        "position": 6,
        "category": "won",
        "is_terminal": true,
        "won_state": "won",
        "sla_days": null,
        "allowed_transitions_to": [],
        "ai_agent_id": null,
        "ai_agent_graph_id": null,
        "is_human": false,
        "is_marketing": false,
        "template_stage_key": "won",
        "created_at": "2026-09-04T19:07:05.226Z"
      }
    ]
  }
}

Answers: 200 · 400 · 401 · 403 · 404 · 409 · 429

GET /pipeline-templates/{key}/preview

Preview a localized pipeline template

The exact board an apply would create: the pipeline name, description and kind, plus every stage with its slug, position, category, derived is_terminal, localized name/description and its allowed transitions (as stage KEYS — the uuids only exist once the board does). Read-only: previewing never writes.

ParameterInTypeRequiredConstraints
keypathstringyesmín. 1, máx. 80
localequerystringnomín. 1, máx. 35
versionqueryintegerno> 0
curl https://api.vitrinadev.com/api/v1/pipeline-templates/<id>/preview \
  -H "Authorization: Bearer $VITRINA_KEY"

Example response (200)

{
  "data": {
    "key": "generic_sales",
    "version": 1,
    "kind": "sales",
    "vertical": "generic",
    "locale": "es",
    "locales_available": [
      "en",
      "es"
    ],
    "pipeline": {
      "name": "Ventas",
      "description": "Pipeline de ventas general: sigue una oportunidad comercial desde la primera consulta concreta hasta un cierre ganado, perdido o no calificado.",
      "kind": "sales"
    },
    "stages": [
      {
        "key": "new",
        "slug": "new",
        "position": 0,
        "category": "open",
        "is_terminal": false,
        "name": "Nuevo",
        "description": "Existe una consulta concreta, pero todavía no hubo contacto sustantivo ni calificación.",
        "transitions": [
          "contacted",
          "won",
          "lost",
          "unqualified"
        ]
      },
      {
        "key": "contacted",
        "slug": "contacted",
        "position": 1,
        "category": "open",
        "is_terminal": false,
        "name": "Contactado",
        "description": "Alguien del equipo hizo contacto real y está entendiendo la necesidad.",
        "transitions": [
          "new",
          "qualified",
          "won",
          "lost",
          "unqualified"
        ]
      }
    ]
  }
}

Answers: 200 · 400 · 401 · 403 · 404 · 409 · 429

GET /pipelines

List pipelines

Every board of the workspace. ?include=counts adds card_count to each row: THE WORK IN FLIGHT on that board — "how many cards would I see if I opened it" — per kind in that kind's own vocabulary: OPEN leads on a sales board, tickets that are neither resolved nor closed on a ticket board (pending and snoozed still count — deferred work is work), and currently-PLACED units on a vehicle board. It is NOT a historical total, so a long-finished funnel reads 0. Without the parameter the response is byte-identical to what it has always been; an unrecognised include value is a 400 rather than a silently ignored filter.

ParameterInTypeRequiredConstraints
includequerycountsno
curl https://api.vitrinadev.com/api/v1/pipelines \
  -H "Authorization: Bearer $VITRINA_KEY"

Example response (200)

{
  "data": [
    {
      "id": "44444444-0000-4000-8000-000000000001",
      "tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
      "name": "Ventas",
      "description": "Sigue una oportunidad comercial desde la primera consulta concreta hasta un cierre ganado, perdido o no calificado.",
      "slug": "generic-sales",
      "kind": "sales",
      "is_fallback": true,
      "template_key": "generic_sales",
      "template_version": 1,
      "template_locale": "es",
      "created_at": "2026-09-04T19:07:05.226Z",
      "updated_at": "2026-09-04T19:07:05.226Z",
      "card_count": 13
    },
    {
      "id": "44444444-0000-4000-8000-000000000002",
      "tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
      "name": "Soporte",
      "description": "Sigue una solicitud resoluble desde la clasificación hasta la resolución, por todas las conversaciones que haga falta.",
      "slug": "generic-support",
      "kind": "ticket",
      "is_fallback": true,
      "template_key": "generic_support",
      "template_version": 1,
      "template_locale": "es",
      "created_at": "2026-09-04T19:07:05.226Z",
      "updated_at": "2026-09-04T19:07:05.226Z",
      "card_count": 4
    }
  ],
  "meta": {
    "total": 2
  }
}

Answers: 200 · 400 · 401 · 403 · 404 · 409 · 429

POST /pipelines

Create pipeline

Creates an EMPTY board — a board with no stages accepts no cards, so follow it with POST /stages/bulk, or start from a shipped template with POST /pipeline-templates/\{key\}/apply instead. A workspace holds exactly one FALLBACK board per kind — the board a card lands on when nobody named one. is_fallback: true promotes this board and demotes the current holder of that kind; false is ignored (promote the other board instead, so there is never a moment with no fallback).

Body

FieldTypeRequiredConstraints
namestringyesmín. 1, máx. 120
descriptionstring—máx. 4000, por defecto ""
slugstring—mín. 1, máx. 120
kindticket \sales \vehicle
is_fallbackboolean—por defecto false
curl -X POST https://api.vitrinadev.com/api/v1/pipelines \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Ventas mayoristas",
    "description": "Oportunidades con empresas, con aprobación de precio.",
    "kind": "sales",
    "is_fallback": false
  }'

Example response (201)

{
  "data": {
    "id": "44444444-0000-4000-8000-000000000002",
    "tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
    "name": "Ventas mayoristas",
    "description": "Oportunidades con empresas, con aprobación de precio.",
    "slug": "ventas-mayoristas",
    "kind": "sales",
    "is_fallback": false,
    "template_key": null,
    "template_version": null,
    "template_locale": null,
    "created_at": "2026-09-22T08:41:22.273Z",
    "updated_at": "2026-09-22T08:41:22.273Z"
  }
}

Answers: 201 · 400 · 401 · 403 · 404 · 409 · 429

DELETE /pipelines/{id}

Delete pipeline

DELETING A BOARD NEVER DELETES ITS CARDS. Every lead and ticket on it is MOVED first — to ?reassign_to=&lt;pipeline id&gt; (same kind), or to the kind's fallback board when it is omitted — and the response reports how many of each moved and where to. 409 when the board is the fallback of its kind (promote another one first) or the last board of its kind; 400 when reassign_to names a board of a different kind.

ParameterInTypeRequiredConstraints
idpathstringyesmín. 1
reassign_toqueryuuidno
curl -X DELETE https://api.vitrinadev.com/api/v1/pipelines/<id> \
  -H "Authorization: Bearer $VITRINA_KEY"

Example response (200)

{
  "data": {
    "tickets": 0,
    "leads": 13,
    "reassignedTo": "44444444-0000-4000-8000-000000000001"
  }
}

Answers: 200 · 400 · 401 · 403 · 404 · 409 · 429

GET /pipelines/{id}

Get pipeline

One board WITH its columns nested under stage[], in position order — the single call that renders a board. A stage's ai_agent embed is omitted for a caller without ai_agents:read.

ParameterInTypeRequiredConstraints
idpathstringyesmín. 1
curl https://api.vitrinadev.com/api/v1/pipelines/<id> \
  -H "Authorization: Bearer $VITRINA_KEY"

Example response (200)

{
  "data": {
    "id": "44444444-0000-4000-8000-000000000001",
    "tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
    "name": "Ventas",
    "description": "Sigue una oportunidad comercial desde la primera consulta concreta hasta un cierre ganado, perdido o no calificado.",
    "slug": "generic-sales",
    "kind": "sales",
    "is_fallback": true,
    "template_key": "generic_sales",
    "template_version": 1,
    "template_locale": "es",
    "created_at": "2026-09-04T19:07:05.226Z",
    "updated_at": "2026-09-04T19:07:05.226Z",
    "stage": [
      {
        "id": "55555555-0000-4000-8000-000000000001",
        "tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
        "pipeline_id": "44444444-0000-4000-8000-000000000001",
        "name": "Nuevo",
        "description": "Existe una consulta concreta, pero todavía no hubo contacto sustantivo ni calificación.",
        "slug": "new",
        "position": 0,
        "category": "open",
        "is_terminal": false,
        "won_state": null,
        "sla_days": null,
        "allowed_transitions_to": [
          "55555555-0000-4000-8000-000000000002",
          "55555555-0000-4000-8000-000000000006",
          "55555555-0000-4000-8000-000000000007"
        ],
        "ai_agent_id": null,
        "ai_agent_graph_id": null,
        "is_human": false,
        "is_marketing": false,
        "template_stage_key": "new",
        "created_at": "2026-09-04T19:07:05.226Z",
        "ai_agent": null
      },
      {
        "id": "55555555-0000-4000-8000-000000000002",
        "tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
        "pipeline_id": "44444444-0000-4000-8000-000000000001",
        "name": "Contactado",
        "description": "Alguien del equipo hizo contacto real y está entendiendo la necesidad.",
        "slug": "contacted",
        "position": 1,
        "category": "open",
        "is_terminal": false,
        "won_state": null,
        "sla_days": 3,
        "allowed_transitions_to": [
          "55555555-0000-4000-8000-000000000003",
          "55555555-0000-4000-8000-000000000006",
          "55555555-0000-4000-8000-000000000007"
        ],
        "ai_agent_id": null,
        "ai_agent_graph_id": null,
        "is_human": false,
        "is_marketing": false,
        "template_stage_key": "contacted",
        "created_at": "2026-09-04T19:07:05.226Z",
        "ai_agent": null
      },
      {
        "id": "55555555-0000-4000-8000-000000000006",
        "tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
        "pipeline_id": "44444444-0000-4000-8000-000000000001",
        "name": "Ganado",
        "description": "La oportunidad se cerró a favor.",
        "slug": "won",
        "position": 6,
        "category": "won",
        "is_terminal": true,
        "won_state": "won",
        "sla_days": null,
        "allowed_transitions_to": [],
        "ai_agent_id": null,
        "ai_agent_graph_id": null,
        "is_human": false,
        "is_marketing": false,
        "template_stage_key": "won",
        "created_at": "2026-09-04T19:07:05.226Z",
        "ai_agent": null
      }
    ]
  }
}

Answers: 200 · 400 · 401 · 403 · 404 · 409 · 429

PUT /pipelines/{id}

Update pipeline

Only the fields present in the body are written. kind is editable only for a non-fallback, EMPTY board that is not the last of its kind — otherwise 409. A workspace holds exactly one FALLBACK board per kind — the board a card lands on when nobody named one. is_fallback: true promotes this board and demotes the current holder of that kind; false is ignored (promote the other board instead, so there is never a moment with no fallback).

ParameterInTypeRequiredConstraints
idpathstringyesmín. 1

Body

FieldTypeRequiredConstraints
namestring—mín. 1, máx. 120
descriptionstring—máx. 4000
slugstring—mín. 1, máx. 120
kindticket \sales \vehicle
is_fallbackboolean—
curl -X PUT https://api.vitrinadev.com/api/v1/pipelines/<id> \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "description": "Oportunidades con empresas. Requiere aprobación de precio sobre 20 %."
  }'

Example response (200)

{
  "data": {
    "id": "44444444-0000-4000-8000-000000000002",
    "tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
    "name": "Ventas mayoristas",
    "description": "Oportunidades con empresas. Requiere aprobación de precio sobre 20 %.",
    "slug": "ventas-mayoristas",
    "kind": "sales",
    "is_fallback": false,
    "template_key": null,
    "template_version": null,
    "template_locale": null,
    "created_at": "2026-09-22T08:41:22.273Z",
    "updated_at": "2026-09-22T08:41:22.273Z"
  }
}

Answers: 200 · 400 · 401 · 403 · 404 · 409 · 429

On this page