VitrinaAPI

Embudos

Los tableros por los que avanza el trabajo, y las plantillas versionadas con las que se arma uno.

Beta
Puede cambiar en cualquier momento, con una entrada en el changelog y aviso a quienes la llamaron recientemente — ver versionado.

Descarga la proyección completa de la API pública: openapi.json.

Mover leads por el embudo explica este recurso en prosa, con ejemplos ejecutables.

MétodoRutaQué hace
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-CLes, 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.

ParámetroEnTipoObligatorioRestricciones
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"

Ejemplo de respuesta (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
  }
}

Responde: 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.

ParámetroEnTipoObligatorioRestricciones
keypathstringmín. 1, máx. 80

Cuerpo

CampoTipoObligatorioRestricciones
localestringmín. 1, máx. 35
versioninteger> 0
name_overridestringmín. 1, máx. 120
is_fallbackbooleanpor 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
  }'

Ejemplo de respuesta (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"
      }
    ]
  }
}

Responde: 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.

ParámetroEnTipoObligatorioRestricciones
keypathstringmí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"

Ejemplo de respuesta (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"
        ]
      }
    ]
  }
}

Responde: 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.

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

Ejemplo de respuesta (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
  }
}

Responde: 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).

Cuerpo

CampoTipoObligatorioRestricciones
namestringmín. 1, máx. 120
descriptionstringmáx. 4000, por defecto ""
slugstringmín. 1, máx. 120
kindticket \sales \vehicle
is_fallbackbooleanpor 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
  }'

Ejemplo de respuesta (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"
  }
}

Responde: 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.

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

Ejemplo de respuesta (200)

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

Responde: 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.

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

Ejemplo de respuesta (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
      }
    ]
  }
}

Responde: 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).

ParámetroEnTipoObligatorioRestricciones
idpathstringmín. 1

Cuerpo

CampoTipoObligatorioRestricciones
namestringmín. 1, máx. 120
descriptionstringmáx. 4000
slugstringmí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 %."
  }'

Ejemplo de respuesta (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"
  }
}

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

En esta página