Leads
Las oportunidades que el workspace está trabajando: abrirlas, moverlas por el tablero, cerrarlas ganadas, perdidas o no calificadas, y leer el embudo.
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étodo | Ruta | Qué hace |
|---|---|---|
GET | /leads | List leads |
POST | /leads | Create a lead |
DELETE | /leads/{id} | Delete a lead |
GET | /leads/{id} | Get a lead |
PUT | /leads/{id} | Update a lead |
GET | /leads/{id}/activity | List a lead's activity timeline |
PUT | /leads/{id}/assign | Assign a lead to an owner or team |
GET | /leads/{id}/conversations | List the conversations this opportunity spans |
POST | /leads/{id}/conversations | Link a conversation to a lead |
DELETE | /leads/{id}/conversations/{conversationId} | Unlink a conversation from a lead |
GET | /leads/{id}/interests | List what a lead concretely wants |
POST | /leads/{id}/interests | Add an interest to a lead |
DELETE | /leads/{id}/interests/{interestId} | Remove a lead interest |
PATCH | /leads/{id}/interests/{interestId} | Update a lead interest |
PUT | /leads/{id}/lost | Mark a lead as lost |
PUT | /leads/{id}/pipeline | Move a lead to another board |
PUT | /leads/{id}/reopen | Reopen a closed lead |
PUT | /leads/{id}/stage | Move a lead to another stage |
PUT | /leads/{id}/unqualify | Mark a lead as unqualified |
PUT | /leads/{id}/won | Mark a lead as won |
POST | /leads/from-conversation | Convert a conversation into a lead |
GET | /leads/funnel | Lead funnel by stage |
POST | /leads/import | Bulk-import leads |
GET | /leads/kanban | Leads grouped by stage (board view) |
GET | /leads/stats | Lead score bands |
GET | /leads/summary | Lead pipeline summary |
GET | /leads/win-rate/{dimension} | Win rate by owner, team or source |
GET /leads
List leads
The flat list, paged (page / page_size, max 200) and filterable by board, stage, status, source, intent, owner, team, contact, value band, score band and last activity; q matches the title and the contact. temperature is a SHORTCUT over score (cold 0–39 · warm 40–69 · hot 70–100 · unscored score IS NULL) and explicit min_score / max_score win over it — do not confuse it with the temperature FIELD on a lead, which is a judgement somebody recorded about momentum and is not derived from the score. resource_type and resource_id must be supplied together and narrow to the leads interested in one record. meta.pagination.total is the match count, not the page size. A member whose role restricts record visibility sees only the leads they own.
| Parámetro | En | Tipo | Obligatorio | Restricciones |
|---|---|---|---|---|
pipeline_id | query | string | no | mín. 1 |
stage_id | query | string | no | mín. 1 |
status | query | open \ | won \ | lost \ |
source | query | conversation \ | marketplace \ | manual \ |
intent | query | buy \ | sell \ | financing \ |
owner_user_id | query | uuid | no | |
team_id | query | uuid | no | |
contact_id | query | string | no | mín. 1 |
min_value | query | number | null | no | ≥ 0 |
max_value | query | number | null | no | ≥ 0 |
min_score | query | integer | null | no | ≥ 0, ≤ 100 |
max_score | query | integer | null | no | ≥ 0, ≤ 100 |
temperature | query | cold \ | warm \ | hot \ |
last_activity_after | query | string | no | date-time |
q | query | string | no | mín. 1, máx. 200 |
sort | query | last_activity_desc \ | last_activity_asc \ | created_desc \ |
resource_type | query | vehicle | no | |
resource_id | query | uuid | no | |
card_kind | query | all \ | leads \ | solicitudes |
page | query | integer | no | ≥ 1, por defecto 1 |
page_size | query | integer | no | ≥ 1, ≤ 200, por defecto 25 |
curl https://api.vitrinadev.com/api/v1/leads \
-H "Authorization: Bearer $VITRINA_KEY"Ejemplo de respuesta (200)
{
"data": [
{
"id": "99999999-0000-4000-8000-000000000001",
"tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
"display_id": "L-89",
"display_seq": 89,
"title": "María — prueba de manejo Corolla 2022",
"notes": null,
"status": "open",
"source": "conversation",
"source_ref": null,
"intent": "buy",
"contact_id": "22222222-0000-4000-8000-000000000001",
"pipeline_id": "44444444-0000-4000-8000-000000000001",
"stage_id": "55555555-0000-4000-8000-000000000001",
"conversation_id": "bbbbbbbb-0000-4000-8000-000000000001",
"origin_conversation_id": "bbbbbbbb-0000-4000-8000-000000000001",
"owner_user_id": "11111111-0000-4000-8000-000000000001",
"team_id": null,
"value_amount": 8990000,
"value_currency": "CLP",
"value_amount_close_confirmed_at": null,
"expected_close_at": "2026-10-15T00:00:00.000Z",
"score": 72,
"score_reason": "pidió hora para probar el auto y confirmó día",
"scored_at": "2026-09-21T14:02:11.000Z",
"temperature": "warm",
"temperature_reason": "respondió pidiendo confirmar la hora y no ha vuelto a escribir",
"temperature_at": "2026-09-21T14:02:11.000Z",
"won_lost_reason": null,
"closed_at": null,
"created_by": "11111111-0000-4000-8000-000000000001",
"created_at": "2026-09-18T13:44:02.115Z",
"updated_at": "2026-09-21T14:02:11.000Z",
"last_activity_at": "2026-09-21T14:02:11.000Z",
"contact": {
"id": "22222222-0000-4000-8000-000000000001",
"name": "María González",
"email": "[email protected]",
"phone": "+56912345001"
},
"stage": {
"id": "55555555-0000-4000-8000-000000000001",
"name": "Nuevo",
"slug": "new",
"position": 0,
"won_state": null,
"pipeline_id": "44444444-0000-4000-8000-000000000001"
},
"pipeline": {
"id": "44444444-0000-4000-8000-000000000001",
"kind": "sales",
"name": "Ventas"
}
},
{
"id": "99999999-0000-4000-8000-000000000002",
"tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
"display_id": "L-90",
"display_seq": 90,
"title": "Consulta por financiamiento — Ana Soto",
"notes": null,
"status": "open",
"source": "website",
"source_ref": null,
"intent": "financing",
"contact_id": "22222222-0000-4000-8000-000000000001",
"pipeline_id": "44444444-0000-4000-8000-000000000001",
"stage_id": "55555555-0000-4000-8000-000000000002",
"conversation_id": null,
"origin_conversation_id": null,
"owner_user_id": null,
"team_id": null,
"value_amount": null,
"value_currency": "CLP",
"value_amount_close_confirmed_at": null,
"expected_close_at": null,
"score": null,
"score_reason": null,
"scored_at": null,
"temperature": null,
"temperature_reason": null,
"temperature_at": null,
"won_lost_reason": null,
"closed_at": null,
"created_by": null,
"created_at": "2026-09-19T09:12:44.000Z",
"updated_at": "2026-09-19T09:12:44.000Z",
"last_activity_at": "2026-09-19T09:12:44.000Z",
"contact": {
"id": "22222222-0000-4000-8000-000000000001",
"name": "Ana Soto",
"email": null,
"phone": "+56912345001"
},
"stage": {
"id": "55555555-0000-4000-8000-000000000002",
"name": "Contactado",
"slug": "contacted",
"position": 1,
"won_state": null,
"pipeline_id": "44444444-0000-4000-8000-000000000001"
},
"pipeline": {
"id": "44444444-0000-4000-8000-000000000001",
"kind": "sales",
"name": "Ventas"
}
}
],
"meta": {
"pagination": {
"total": 16,
"limit": 25
}
}
}Responde: 200 · 400 · 401 · 403 · 404 · 409 · 429
POST /leads
Create a lead
Opens an opportunity for an existing contact. pipeline_id defaults to the workspace's fallback SALES board (a lead cannot live on a ticket or vehicle board — 400 if you name one) and stage_id to that board's first column. source says WHERE it came from, intent says WHY (buy · sell · financing · trade_in, default buy); both are set at creation and are not part of the update body. With no owner_user_id but a team_id, the team's rotation picks the owner. Emits lead.created. Money is value_amount + value_currency and the pair travels together: a workspace may work in more than one currency, so the analytics endpoints break their totals down by_currency and flag is_mixed_currency rather than summing across them.
Cuerpo
| Campo | Tipo | Obligatorio | Restricciones |
|---|---|---|---|
contact_id | uuid | sí | |
pipeline_id | uuid | — | |
stage_id | uuid | — | |
conversation_id | uuid | — | |
title | string | sí | mín. 1, máx. 500 |
notes | string | null | — | máx. 20000 |
source | conversation \ | marketplace \ | manual \ |
intent | buy \ | sell \ | financing \ |
source_ref | string | null | — | máx. 500 |
value_amount | number | null | — | ≥ 0 |
value_currency | string | null | — | mín. 1, máx. 8 |
expected_close_at | string | null | — | date-time |
owner_user_id | string | null | — | |
team_id | string | null | — | |
score | integer | null | — | ≥ 0, ≤ 100 |
curl -X POST https://api.vitrinadev.com/api/v1/leads \
-H "Authorization: Bearer $VITRINA_KEY" \
-H "Content-Type: application/json" \
-d '{
"contact_id": "22222222-0000-4000-8000-000000000001",
"pipeline_id": "44444444-0000-4000-8000-000000000001",
"title": "María — prueba de manejo Corolla 2022",
"source": "manual",
"intent": "buy",
"value_amount": 8990000,
"value_currency": "CLP",
"expected_close_at": "2026-10-15T00:00:00.000Z",
"owner_user_id": "11111111-0000-4000-8000-000000000001"
}'Ejemplo de respuesta (201)
{
"data": {
"id": "99999999-0000-4000-8000-000000000001",
"tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
"display_id": "L-89",
"display_seq": 89,
"title": "María — prueba de manejo Corolla 2022",
"notes": null,
"status": "open",
"source": "conversation",
"source_ref": null,
"intent": "buy",
"contact_id": "22222222-0000-4000-8000-000000000001",
"pipeline_id": "44444444-0000-4000-8000-000000000001",
"stage_id": "55555555-0000-4000-8000-000000000001",
"conversation_id": "bbbbbbbb-0000-4000-8000-000000000001",
"origin_conversation_id": "bbbbbbbb-0000-4000-8000-000000000001",
"owner_user_id": "11111111-0000-4000-8000-000000000001",
"team_id": null,
"value_amount": 8990000,
"value_currency": "CLP",
"value_amount_close_confirmed_at": null,
"expected_close_at": "2026-10-15T00:00:00.000Z",
"score": 72,
"score_reason": "pidió hora para probar el auto y confirmó día",
"scored_at": "2026-09-21T14:02:11.000Z",
"temperature": "warm",
"temperature_reason": "respondió pidiendo confirmar la hora y no ha vuelto a escribir",
"temperature_at": "2026-09-21T14:02:11.000Z",
"won_lost_reason": null,
"closed_at": null,
"created_by": "11111111-0000-4000-8000-000000000001",
"created_at": "2026-09-18T13:44:02.115Z",
"updated_at": "2026-09-21T14:02:11.000Z",
"last_activity_at": "2026-09-21T14:02:11.000Z",
"contact": {
"id": "22222222-0000-4000-8000-000000000001",
"name": "María González",
"email": "[email protected]",
"phone": "+56912345001"
},
"stage": {
"id": "55555555-0000-4000-8000-000000000001",
"name": "Nuevo",
"slug": "new",
"position": 0,
"won_state": null,
"pipeline_id": "44444444-0000-4000-8000-000000000001"
},
"pipeline": {
"id": "44444444-0000-4000-8000-000000000001",
"kind": "sales",
"name": "Ventas"
}
}
}Responde: 201 · 400 · 401 · 403 · 404 · 409 · 429
DELETE /leads/{id}
Delete a lead
Removes the opportunity and its history. The CONTACT is untouched — a deleted lead is an opportunity that should never have been opened, not a person who should be forgotten. A lead that really ended belongs on /lost or /unqualify, which keep the record and the reason.
| Parámetro | En | Tipo | Obligatorio | Restricciones |
|---|---|---|---|---|
id | path | string | sí | patrón `^(?:[0-9a-f]8-[0-9a-f]4-[0-9a-f]4-[0-9a-f]4-[0-9a-f]12\ |
curl -X DELETE https://api.vitrinadev.com/api/v1/leads/<id> \
-H "Authorization: Bearer $VITRINA_KEY"Responde: 204 · 400 · 401 · 403 · 404 · 409 · 429
GET /leads/{id}
Get a lead
One opportunity, with its contact, stage and pipeline embedded. \{id\} accepts the lead uuid or its ID visible (L-89) — the label is a lookup shortcut on the path only, never a value to store.
| Parámetro | En | Tipo | Obligatorio | Restricciones |
|---|---|---|---|---|
id | path | string | sí | patrón `^(?:[0-9a-f]8-[0-9a-f]4-[0-9a-f]4-[0-9a-f]4-[0-9a-f]12\ |
curl https://api.vitrinadev.com/api/v1/leads/<id> \
-H "Authorization: Bearer $VITRINA_KEY"Ejemplo de respuesta (200)
{
"data": {
"id": "99999999-0000-4000-8000-000000000001",
"tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
"display_id": "L-89",
"display_seq": 89,
"title": "María — prueba de manejo Corolla 2022",
"notes": null,
"status": "open",
"source": "conversation",
"source_ref": null,
"intent": "buy",
"contact_id": "22222222-0000-4000-8000-000000000001",
"pipeline_id": "44444444-0000-4000-8000-000000000001",
"stage_id": "55555555-0000-4000-8000-000000000001",
"conversation_id": "bbbbbbbb-0000-4000-8000-000000000001",
"origin_conversation_id": "bbbbbbbb-0000-4000-8000-000000000001",
"owner_user_id": "11111111-0000-4000-8000-000000000001",
"team_id": null,
"value_amount": 8990000,
"value_currency": "CLP",
"value_amount_close_confirmed_at": null,
"expected_close_at": "2026-10-15T00:00:00.000Z",
"score": 72,
"score_reason": "pidió hora para probar el auto y confirmó día",
"scored_at": "2026-09-21T14:02:11.000Z",
"temperature": "warm",
"temperature_reason": "respondió pidiendo confirmar la hora y no ha vuelto a escribir",
"temperature_at": "2026-09-21T14:02:11.000Z",
"won_lost_reason": null,
"closed_at": null,
"created_by": "11111111-0000-4000-8000-000000000001",
"created_at": "2026-09-18T13:44:02.115Z",
"updated_at": "2026-09-21T14:02:11.000Z",
"last_activity_at": "2026-09-21T14:02:11.000Z",
"contact": {
"id": "22222222-0000-4000-8000-000000000001",
"name": "María González",
"email": "[email protected]",
"phone": "+56912345001"
},
"stage": {
"id": "55555555-0000-4000-8000-000000000001",
"name": "Nuevo",
"slug": "new",
"position": 0,
"won_state": null,
"pipeline_id": "44444444-0000-4000-8000-000000000001"
},
"pipeline": {
"id": "44444444-0000-4000-8000-000000000001",
"kind": "sales",
"name": "Ventas"
}
}
}Responde: 200 · 400 · 401 · 403 · 404 · 409 · 429
PUT /leads/{id}
Update a lead
Only the fields present in the body are written; at least one is required. source and intent are set at creation and are not editable, and the stage is moved with PUT /leads/\{id\}/stage rather than here. temperature is the recorded JUDGEMENT about momentum (hot · warm · cold): omitting it leaves it alone, sending null RETRACTS it — which is how a person undoes a wrong verdict. Setting owner_user_id reassigns the lead's conversations with it. Money is value_amount + value_currency and the pair travels together: a workspace may work in more than one currency, so the analytics endpoints break their totals down by_currency and flag is_mixed_currency rather than summing across them.
| Parámetro | En | Tipo | Obligatorio | Restricciones |
|---|---|---|---|---|
id | path | string | sí | patrón `^(?:[0-9a-f]8-[0-9a-f]4-[0-9a-f]4-[0-9a-f]4-[0-9a-f]12\ |
Cuerpo
| Campo | Tipo | Obligatorio | Restricciones |
|---|---|---|---|
title | string | — | mín. 1, máx. 500 |
notes | string | null | — | máx. 20000 |
value_amount | number | null | — | ≥ 0 |
value_currency | string | null | — | mín. 1, máx. 8 |
expected_close_at | string | null | — | date-time |
owner_user_id | string | null | — | |
team_id | string | null | — | |
score | integer | null | — | ≥ 0, ≤ 100 |
temperature | hot \ | warm \ | cold |
temperature_reason | string | null | — | máx. 500 |
curl -X PUT https://api.vitrinadev.com/api/v1/leads/<id> \
-H "Authorization: Bearer $VITRINA_KEY" \
-H "Content-Type: application/json" \
-d '{
"value_amount": 8750000,
"expected_close_at": "2026-10-31T00:00:00.000Z",
"temperature": "hot",
"temperature_reason": "volvió a escribir pidiendo la reserva"
}'Ejemplo de respuesta (200)
{
"data": {
"id": "99999999-0000-4000-8000-000000000001",
"tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
"display_id": "L-89",
"display_seq": 89,
"title": "María — prueba de manejo Corolla 2022",
"notes": null,
"status": "open",
"source": "conversation",
"source_ref": null,
"intent": "buy",
"contact_id": "22222222-0000-4000-8000-000000000001",
"pipeline_id": "44444444-0000-4000-8000-000000000001",
"stage_id": "55555555-0000-4000-8000-000000000001",
"conversation_id": "bbbbbbbb-0000-4000-8000-000000000001",
"origin_conversation_id": "bbbbbbbb-0000-4000-8000-000000000001",
"owner_user_id": "11111111-0000-4000-8000-000000000001",
"team_id": null,
"value_amount": 8750000,
"value_currency": "CLP",
"value_amount_close_confirmed_at": null,
"expected_close_at": "2026-10-31T00:00:00.000Z",
"score": 72,
"score_reason": "pidió hora para probar el auto y confirmó día",
"scored_at": "2026-09-21T14:02:11.000Z",
"temperature": "hot",
"temperature_reason": "volvió a escribir pidiendo la reserva",
"temperature_at": "2026-09-21T14:02:11.000Z",
"won_lost_reason": null,
"closed_at": null,
"created_by": "11111111-0000-4000-8000-000000000001",
"created_at": "2026-09-18T13:44:02.115Z",
"updated_at": "2026-09-22T08:39:20.206Z",
"last_activity_at": "2026-09-22T08:39:20.206Z",
"contact": {
"id": "22222222-0000-4000-8000-000000000001",
"name": "María González",
"email": "[email protected]",
"phone": "+56912345001"
},
"stage": {
"id": "55555555-0000-4000-8000-000000000001",
"name": "Nuevo",
"slug": "new",
"position": 0,
"won_state": null,
"pipeline_id": "44444444-0000-4000-8000-000000000001"
},
"pipeline": {
"id": "44444444-0000-4000-8000-000000000001",
"kind": "sales",
"name": "Ventas"
}
}
}Responde: 200 · 400 · 401 · 403 · 404 · 409 · 429
GET /leads/{id}/activity
List a lead's activity timeline
Newest first. Every line carries its author (ADR 0106 §3.1): the member who acted (with via when they acted through a connected app or a personal token — «Camila vía Claude»), the API key that acted («CRM propio (API)», named as it was then), the AI agent, or the system. kind says what happened and data is kind-specific; id is the timeline row's own legacy sequence number, not a uuid — lead_id beside it is the reference.
| Parámetro | En | Tipo | Obligatorio | Restricciones |
|---|---|---|---|---|
id | path | string | sí | patrón `^(?:[0-9a-f]8-[0-9a-f]4-[0-9a-f]4-[0-9a-f]4-[0-9a-f]12\ |
curl https://api.vitrinadev.com/api/v1/leads/<id>/activity \
-H "Authorization: Bearer $VITRINA_KEY"Ejemplo de respuesta (200)
{
"data": [
{
"id": 474,
"tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
"lead_id": "99999999-0000-4000-8000-000000000001",
"kind": "stage_changed",
"actor_user_id": "11111111-0000-4000-8000-000000000001",
"conversation_id": "bbbbbbbb-0000-4000-8000-000000000001",
"data": {
"reason": "la llamamos y confirmó la hora",
"from_stage_id": "55555555-0000-4000-8000-000000000001",
"to_stage_id": "55555555-0000-4000-8000-000000000002",
"from_stage_slug": "new",
"to_stage_slug": "contacted",
"from_stage_name": "Nuevo",
"to_stage_name": "Contactado",
"to_stage_category": "open"
},
"author": {
"kind": "member",
"id": "11111111-0000-4000-8000-000000000001",
"name": "Camila Rojas",
"via": {
"kind": "connected_app",
"name": "Claude"
}
},
"created_at": "2026-09-22T08:39:25.338Z"
},
{
"id": 473,
"tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
"lead_id": "99999999-0000-4000-8000-000000000001",
"kind": "assigned",
"actor_user_id": null,
"conversation_id": null,
"data": {
"from_owner_user_id": null,
"to_owner_user_id": "11111111-0000-4000-8000-000000000001"
},
"author": {
"kind": "api_key",
"id": "c1c1c1c1-0000-4000-8000-000000000001",
"name": "CRM propio"
},
"created_at": "2026-09-21T14:02:11.000Z"
},
{
"id": 470,
"tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
"lead_id": "99999999-0000-4000-8000-000000000001",
"kind": "created",
"actor_user_id": null,
"conversation_id": "bbbbbbbb-0000-4000-8000-000000000001",
"data": {
"source": "conversation",
"title": "María — prueba de manejo Corolla 2022"
},
"author": {
"kind": "system",
"id": null,
"name": null
},
"created_at": "2026-09-18T13:44:02.115Z"
}
]
}Responde: 200 · 400 · 401 · 403 · 404 · 409 · 429
PUT /leads/{id}/assign
Assign a lead to an owner or team
ONE ASSIGNEE OWNS THE WHOLE RELATIONSHIP: reassigning a lead moves every conversation linked to it, and their tickets, to the same person. Send owner_user_id, team_id or both; an omitted key leaves that side alone, and null clears it. A team with no owner draws from the team's rotation. Emits lead.assigned.
| Parámetro | En | Tipo | Obligatorio | Restricciones |
|---|---|---|---|---|
id | path | string | sí | patrón `^(?:[0-9a-f]8-[0-9a-f]4-[0-9a-f]4-[0-9a-f]4-[0-9a-f]12\ |
Cuerpo
| Campo | Tipo | Obligatorio | Restricciones |
|---|---|---|---|
owner_user_id | string | null | — | |
team_id | string | null | — |
curl -X PUT https://api.vitrinadev.com/api/v1/leads/<id>/assign \
-H "Authorization: Bearer $VITRINA_KEY" \
-H "Content-Type: application/json" \
-d '{
"owner_user_id": "11111111-0000-4000-8000-000000000001",
"team_id": "cccccccc-0000-4000-8000-000000000001"
}'Ejemplo de respuesta (200)
{
"data": {
"id": "99999999-0000-4000-8000-000000000001",
"tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
"display_id": "L-89",
"display_seq": 89,
"title": "María — prueba de manejo Corolla 2022",
"notes": null,
"status": "open",
"source": "conversation",
"source_ref": null,
"intent": "buy",
"contact_id": "22222222-0000-4000-8000-000000000001",
"pipeline_id": "44444444-0000-4000-8000-000000000001",
"stage_id": "55555555-0000-4000-8000-000000000001",
"conversation_id": "bbbbbbbb-0000-4000-8000-000000000001",
"origin_conversation_id": "bbbbbbbb-0000-4000-8000-000000000001",
"owner_user_id": "11111111-0000-4000-8000-000000000001",
"team_id": "cccccccc-0000-4000-8000-000000000001",
"value_amount": 8990000,
"value_currency": "CLP",
"value_amount_close_confirmed_at": null,
"expected_close_at": "2026-10-15T00:00:00.000Z",
"score": 72,
"score_reason": "pidió hora para probar el auto y confirmó día",
"scored_at": "2026-09-21T14:02:11.000Z",
"temperature": "warm",
"temperature_reason": "respondió pidiendo confirmar la hora y no ha vuelto a escribir",
"temperature_at": "2026-09-21T14:02:11.000Z",
"won_lost_reason": null,
"closed_at": null,
"created_by": "11111111-0000-4000-8000-000000000001",
"created_at": "2026-09-18T13:44:02.115Z",
"updated_at": "2026-09-21T14:02:11.000Z",
"last_activity_at": "2026-09-21T14:02:11.000Z",
"contact": {
"id": "22222222-0000-4000-8000-000000000001",
"name": "María González",
"email": "[email protected]",
"phone": "+56912345001"
},
"stage": {
"id": "55555555-0000-4000-8000-000000000001",
"name": "Nuevo",
"slug": "new",
"position": 0,
"won_state": null,
"pipeline_id": "44444444-0000-4000-8000-000000000001"
},
"pipeline": {
"id": "44444444-0000-4000-8000-000000000001",
"kind": "sales",
"name": "Ventas"
}
}
}Responde: 200 · 400 · 401 · 403 · 404 · 409 · 429
GET /leads/{id}/conversations
List the conversations this opportunity spans
A lead is ONE closable opportunity; a conversation is a temporary session. The customer comes back next week on another channel and it is still the same opportunity, so this relation — not a single conversation_id — is the answer to "which conversations belong to this lead". is_origin marks where the opportunity STARTED (a historical fact) and is_primary the thread it is worked on now (editable).
| Parámetro | En | Tipo | Obligatorio | Restricciones |
|---|---|---|---|---|
id | path | string | sí | patrón `^(?:[0-9a-f]8-[0-9a-f]4-[0-9a-f]4-[0-9a-f]4-[0-9a-f]12\ |
curl https://api.vitrinadev.com/api/v1/leads/<id>/conversations \
-H "Authorization: Bearer $VITRINA_KEY"Ejemplo de respuesta (200)
{
"data": [
{
"conversation_id": "bbbbbbbb-0000-4000-8000-000000000001",
"display_id": "C-122",
"channel": "whatsapp",
"status": "open",
"is_primary": true,
"is_origin": true,
"linked_at": "2026-09-18T13:44:02.115Z",
"linked_by_type": "system",
"last_message_date": "2026-09-21T14:01:58.000Z"
}
],
"meta": {
"total": 1
}
}Responde: 200 · 400 · 401 · 403 · 404 · 409 · 429
POST /leads/{id}/conversations
Link a conversation to a lead
IDEMPOTENT: re-linking an existing pair answers linked: false and writes no second timeline entry. make_primary moves the primary flag onto this thread, demoting whichever held it. 404 for a lead or a conversation this credential cannot see — BOTH sides are checked, so a member restricted to their own records cannot attach a colleague's thread to a lead they own.
| Parámetro | En | Tipo | Obligatorio | Restricciones |
|---|---|---|---|---|
id | path | string | sí | patrón `^(?:[0-9a-f]8-[0-9a-f]4-[0-9a-f]4-[0-9a-f]4-[0-9a-f]12\ |
Cuerpo
| Campo | Tipo | Obligatorio | Restricciones |
|---|---|---|---|
conversation_id | uuid | sí | |
make_primary | boolean | — |
curl -X POST https://api.vitrinadev.com/api/v1/leads/<id>/conversations \
-H "Authorization: Bearer $VITRINA_KEY" \
-H "Content-Type: application/json" \
-d '{
"conversation_id": "bbbbbbbb-0000-4000-8000-000000000001",
"make_primary": true
}'Ejemplo de respuesta (200)
{
"data": {
"linked": true,
"is_primary": true
}
}Responde: 200 · 400 · 401 · 403 · 404 · 409 · 429
DELETE /leads/{id}/conversations/{conversationId}
Unlink a conversation from a lead
Detaching the ORIGIN conversation is allowed: the association is editable, but origin_conversation_id is a historical fact ("where this opportunity started") and stays put, because attribution keeps reading it. Answers \{ unlinked \} — false when there was no link to remove.
| Parámetro | En | Tipo | Obligatorio | Restricciones |
|---|---|---|---|---|
id | path | string | sí | patrón `^(?:[0-9a-f]8-[0-9a-f]4-[0-9a-f]4-[0-9a-f]4-[0-9a-f]12\ |
conversationId | path | uuid | sí |
curl -X DELETE https://api.vitrinadev.com/api/v1/leads/<id>/conversations/<id> \
-H "Authorization: Bearer $VITRINA_KEY"Ejemplo de respuesta (200)
{
"data": {
"unlinked": true
}
}Responde: 200 · 400 · 401 · 403 · 404 · 409 · 429
GET /leads/{id}/interests
List what a lead concretely wants
A lead is the opportunity; an INTEREST is the thing being asked for, and there can be several. resource_type is vehicle · property · product · service · repair_order · custom, with resource_id pointing at the record when it exists in Vitrina and title carrying it in words when it does not ("un SUV gris, 2020 o más nuevo").
| Parámetro | En | Tipo | Obligatorio | Restricciones |
|---|---|---|---|---|
id | path | string | sí | patrón `^(?:[0-9a-f]8-[0-9a-f]4-[0-9a-f]4-[0-9a-f]4-[0-9a-f]12\ |
curl https://api.vitrinadev.com/api/v1/leads/<id>/interests \
-H "Authorization: Bearer $VITRINA_KEY"Ejemplo de respuesta (200)
{
"data": [
{
"id": "aaaaaaaa-0000-4000-8000-000000000001",
"tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
"lead_id": "99999999-0000-4000-8000-000000000001",
"resource_type": "vehicle",
"resource_id": "e1e1e1e1-0000-4000-8000-000000000001",
"title": "Toyota Corolla 2022",
"quantity": 1,
"priority": "high",
"notes": "lo quiere gris, con mantenimiento al día",
"metadata": {},
"created_at": "2026-09-22T08:39:26.884Z",
"updated_at": "2026-09-22T08:39:26.884Z",
"superseded_at": null
}
],
"meta": {
"total": 1
}
}Responde: 200 · 400 · 401 · 403 · 404 · 409 · 429
POST /leads/{id}/interests
Add an interest to a lead
Provide resource_id, title, or both — one of the two is required, because an interest that names neither a record nor a description says nothing. 400 otherwise.
| Parámetro | En | Tipo | Obligatorio | Restricciones |
|---|---|---|---|---|
id | path | string | sí | patrón `^(?:[0-9a-f]8-[0-9a-f]4-[0-9a-f]4-[0-9a-f]4-[0-9a-f]12\ |
Cuerpo
| Campo | Tipo | Obligatorio | Restricciones |
|---|---|---|---|
resource_type | vehicle \ | property \ | product \ |
resource_id | string | null | — | |
title | string | null | — | mín. 1, máx. 500 |
quantity | number | null | — | ≥ 0 |
priority | low \ | medium \ | high |
notes | string | null | — | máx. 20000 |
metadata | object | — |
curl -X POST https://api.vitrinadev.com/api/v1/leads/<id>/interests \
-H "Authorization: Bearer $VITRINA_KEY" \
-H "Content-Type: application/json" \
-d '{
"resource_type": "vehicle",
"resource_id": "e1e1e1e1-0000-4000-8000-000000000001",
"quantity": 1,
"priority": "high",
"notes": "lo quiere gris, con mantenimiento al día"
}'Ejemplo de respuesta (201)
{
"data": {
"id": "aaaaaaaa-0000-4000-8000-000000000001",
"tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
"lead_id": "99999999-0000-4000-8000-000000000001",
"resource_type": "vehicle",
"resource_id": "e1e1e1e1-0000-4000-8000-000000000001",
"title": "Toyota Corolla 2022",
"quantity": 1,
"priority": "high",
"notes": "lo quiere gris, con mantenimiento al día",
"metadata": {},
"created_at": "2026-09-22T08:39:26.884Z",
"updated_at": "2026-09-22T08:39:26.884Z",
"superseded_at": null
}
}Responde: 201 · 400 · 401 · 403 · 404 · 409 · 429
DELETE /leads/{id}/interests/{interestId}
Remove a lead interest
Deletes the row and answers \{ id, deleted: true \} (200, not 204). Removes it from the record entirely — an interest the customer moved ON from is history worth keeping, so prefer adding the new one.
| Parámetro | En | Tipo | Obligatorio | Restricciones |
|---|---|---|---|---|
id | path | string | sí | patrón `^(?:[0-9a-f]8-[0-9a-f]4-[0-9a-f]4-[0-9a-f]4-[0-9a-f]12\ |
interestId | path | uuid | sí |
curl -X DELETE https://api.vitrinadev.com/api/v1/leads/<id>/interests/<id> \
-H "Authorization: Bearer $VITRINA_KEY"Ejemplo de respuesta (200)
{
"data": {
"id": "aaaaaaaa-0000-4000-8000-000000000001",
"deleted": true
}
}Responde: 200 · 400 · 401 · 403 · 404 · 409 · 429
PATCH /leads/{id}/interests/{interestId}
Update a lead interest
Only the fields present in the body are written; at least one is required. Use this when the ask itself changed — a different unit, a different quantity, a different priority.
| Parámetro | En | Tipo | Obligatorio | Restricciones |
|---|---|---|---|---|
id | path | string | sí | patrón `^(?:[0-9a-f]8-[0-9a-f]4-[0-9a-f]4-[0-9a-f]4-[0-9a-f]12\ |
interestId | path | uuid | sí |
Cuerpo
| Campo | Tipo | Obligatorio | Restricciones |
|---|---|---|---|
resource_type | vehicle \ | property \ | product \ |
resource_id | string | null | — | |
title | string | null | — | mín. 1, máx. 500 |
quantity | number | null | — | ≥ 0 |
priority | low \ | medium \ | high |
notes | string | null | — | máx. 20000 |
metadata | object | — |
curl -X PATCH https://api.vitrinadev.com/api/v1/leads/<id>/interests/<id> \
-H "Authorization: Bearer $VITRINA_KEY" \
-H "Content-Type: application/json" \
-d '{
"priority": "medium"
}'Ejemplo de respuesta (200)
{
"data": {
"id": "aaaaaaaa-0000-4000-8000-000000000001",
"tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
"lead_id": "99999999-0000-4000-8000-000000000001",
"resource_type": "vehicle",
"resource_id": "e1e1e1e1-0000-4000-8000-000000000001",
"title": "Toyota Corolla 2022",
"quantity": 1,
"priority": "medium",
"notes": "lo quiere gris, con mantenimiento al día",
"metadata": {},
"created_at": "2026-09-22T08:39:26.884Z",
"updated_at": "2026-09-22T08:39:26.884Z",
"superseded_at": null
}
}Responde: 200 · 400 · 401 · 403 · 404 · 409 · 429
PUT /leads/{id}/lost
Mark a lead as lost
The opportunity was real and did not close. The lead MOVES to the lowest-position stage of that category on its board, so the outcome and the column can never disagree — 400 when the board has no stage of that category, which is a board a person has to fix rather than a request to retry. status, closed_at and won_lost_reason are derived from the landing stage, and a lead.stage_changed Evento fires alongside the outcome one. reason is the one field worth filling: it is what the win-rate and funnel reads are read against. Emits lead.lost.
| Parámetro | En | Tipo | Obligatorio | Restricciones |
|---|---|---|---|---|
id | path | string | sí | patrón `^(?:[0-9a-f]8-[0-9a-f]4-[0-9a-f]4-[0-9a-f]4-[0-9a-f]12\ |
Cuerpo
| Campo | Tipo | Obligatorio | Restricciones |
|---|---|---|---|
status | won \ | lost \ | unqualified \ |
reason | string | null | — | máx. 500 |
value_amount | number | null | — | ≥ 0 |
value_currency | string | null | — | mín. 1, máx. 8 |
curl -X PUT https://api.vitrinadev.com/api/v1/leads/<id>/lost \
-H "Authorization: Bearer $VITRINA_KEY" \
-H "Content-Type: application/json" \
-d '{
"reason": "compró en otra parte"
}'Ejemplo de respuesta (200)
{
"data": {
"id": "99999999-0000-4000-8000-000000000001",
"tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
"display_id": "L-89",
"display_seq": 89,
"title": "María — prueba de manejo Corolla 2022",
"notes": null,
"status": "lost",
"source": "conversation",
"source_ref": null,
"intent": "buy",
"contact_id": "22222222-0000-4000-8000-000000000001",
"pipeline_id": "44444444-0000-4000-8000-000000000001",
"stage_id": "55555555-0000-4000-8000-000000000007",
"conversation_id": "bbbbbbbb-0000-4000-8000-000000000001",
"origin_conversation_id": "bbbbbbbb-0000-4000-8000-000000000001",
"owner_user_id": "11111111-0000-4000-8000-000000000001",
"team_id": null,
"value_amount": 8990000,
"value_currency": "CLP",
"value_amount_close_confirmed_at": null,
"expected_close_at": "2026-10-15T00:00:00.000Z",
"score": 72,
"score_reason": "pidió hora para probar el auto y confirmó día",
"scored_at": "2026-09-21T14:02:11.000Z",
"temperature": "warm",
"temperature_reason": "respondió pidiendo confirmar la hora y no ha vuelto a escribir",
"temperature_at": "2026-09-21T14:02:11.000Z",
"won_lost_reason": "compró en otra parte",
"closed_at": "2026-09-22T08:44:10.331Z",
"created_by": "11111111-0000-4000-8000-000000000001",
"created_at": "2026-09-18T13:44:02.115Z",
"updated_at": "2026-09-21T14:02:11.000Z",
"last_activity_at": "2026-09-21T14:02:11.000Z",
"contact": {
"id": "22222222-0000-4000-8000-000000000001",
"name": "María González",
"email": "[email protected]",
"phone": "+56912345001"
},
"stage": {
"id": "55555555-0000-4000-8000-000000000007",
"name": "Perdido",
"slug": "lost",
"position": 7,
"won_state": "lost",
"pipeline_id": "44444444-0000-4000-8000-000000000001"
},
"pipeline": {
"id": "44444444-0000-4000-8000-000000000001",
"kind": "sales",
"name": "Ventas"
}
}
}Responde: 200 · 400 · 401 · 403 · 404 · 409 · 429
PUT /leads/{id}/pipeline
Move a lead to another board
Reclassifies the opportunity onto a different SALES board (400 for a ticket or vehicle board) and lands it on that board's first stage, re-deriving the lifecycle from the landing stage rather than leaving a closed lead parked in an open column. Emits lead.pipeline_changed, whose changes carries BOTH the board and the stage that moved with it; no separate lead.stage_changed fires, so a receiver never sees one board change twice.
| Parámetro | En | Tipo | Obligatorio | Restricciones |
|---|---|---|---|---|
id | path | string | sí | patrón `^(?:[0-9a-f]8-[0-9a-f]4-[0-9a-f]4-[0-9a-f]4-[0-9a-f]12\ |
Cuerpo
| Campo | Tipo | Obligatorio | Restricciones |
|---|---|---|---|
pipeline_id | uuid | sí | |
reason | string | null | — | máx. 500 |
curl -X PUT https://api.vitrinadev.com/api/v1/leads/<id>/pipeline \
-H "Authorization: Bearer $VITRINA_KEY" \
-H "Content-Type: application/json" \
-d '{
"pipeline_id": "44444444-0000-4000-8000-000000000002",
"reason": "es una venta mayorista"
}'Ejemplo de respuesta (200)
{
"data": {
"id": "99999999-0000-4000-8000-000000000001",
"tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
"display_id": "L-89",
"display_seq": 89,
"title": "María — prueba de manejo Corolla 2022",
"notes": null,
"status": "open",
"source": "conversation",
"source_ref": null,
"intent": "buy",
"contact_id": "22222222-0000-4000-8000-000000000001",
"pipeline_id": "44444444-0000-4000-8000-000000000002",
"stage_id": "55555555-0000-4000-8000-000000000003",
"conversation_id": "bbbbbbbb-0000-4000-8000-000000000001",
"origin_conversation_id": "bbbbbbbb-0000-4000-8000-000000000001",
"owner_user_id": "11111111-0000-4000-8000-000000000001",
"team_id": null,
"value_amount": 8990000,
"value_currency": "CLP",
"value_amount_close_confirmed_at": null,
"expected_close_at": "2026-10-15T00:00:00.000Z",
"score": 72,
"score_reason": "pidió hora para probar el auto y confirmó día",
"scored_at": "2026-09-21T14:02:11.000Z",
"temperature": "warm",
"temperature_reason": "respondió pidiendo confirmar la hora y no ha vuelto a escribir",
"temperature_at": "2026-09-21T14:02:11.000Z",
"won_lost_reason": null,
"closed_at": null,
"created_by": "11111111-0000-4000-8000-000000000001",
"created_at": "2026-09-18T13:44:02.115Z",
"updated_at": "2026-09-21T14:02:11.000Z",
"last_activity_at": "2026-09-21T14:02:11.000Z",
"contact": {
"id": "22222222-0000-4000-8000-000000000001",
"name": "María González",
"email": "[email protected]",
"phone": "+56912345001"
},
"stage": {
"id": "55555555-0000-4000-8000-000000000003",
"name": "Nuevo",
"slug": "new",
"position": 0,
"won_state": null,
"pipeline_id": "44444444-0000-4000-8000-000000000002"
},
"pipeline": {
"id": "44444444-0000-4000-8000-000000000002",
"kind": "sales",
"name": "Ventas mayoristas"
}
}
}Responde: 200 · 400 · 401 · 403 · 404 · 409 · 429
PUT /leads/{id}/reopen
Reopen a closed lead
Puts a won, lost or unqualified lead back in play on the first OPEN stage of its board, clearing closed_at and won_lost_reason. No body. The close is not erased — the timeline and the transition log keep it — so a reopened-and-re-won lead reads as the two events it was.
| Parámetro | En | Tipo | Obligatorio | Restricciones |
|---|---|---|---|---|
id | path | string | sí | patrón `^(?:[0-9a-f]8-[0-9a-f]4-[0-9a-f]4-[0-9a-f]4-[0-9a-f]12\ |
curl -X PUT https://api.vitrinadev.com/api/v1/leads/<id>/reopen \
-H "Authorization: Bearer $VITRINA_KEY"Ejemplo de respuesta (200)
{
"data": {
"id": "99999999-0000-4000-8000-000000000001",
"tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
"display_id": "L-89",
"display_seq": 89,
"title": "María — prueba de manejo Corolla 2022",
"notes": null,
"status": "open",
"source": "conversation",
"source_ref": null,
"intent": "buy",
"contact_id": "22222222-0000-4000-8000-000000000001",
"pipeline_id": "44444444-0000-4000-8000-000000000001",
"stage_id": "55555555-0000-4000-8000-000000000001",
"conversation_id": "bbbbbbbb-0000-4000-8000-000000000001",
"origin_conversation_id": "bbbbbbbb-0000-4000-8000-000000000001",
"owner_user_id": "11111111-0000-4000-8000-000000000001",
"team_id": null,
"value_amount": 8990000,
"value_currency": "CLP",
"value_amount_close_confirmed_at": null,
"expected_close_at": "2026-10-15T00:00:00.000Z",
"score": 72,
"score_reason": "pidió hora para probar el auto y confirmó día",
"scored_at": "2026-09-21T14:02:11.000Z",
"temperature": "warm",
"temperature_reason": "respondió pidiendo confirmar la hora y no ha vuelto a escribir",
"temperature_at": "2026-09-21T14:02:11.000Z",
"won_lost_reason": null,
"closed_at": null,
"created_by": "11111111-0000-4000-8000-000000000001",
"created_at": "2026-09-18T13:44:02.115Z",
"updated_at": "2026-09-21T14:02:11.000Z",
"last_activity_at": "2026-09-21T14:02:11.000Z",
"contact": {
"id": "22222222-0000-4000-8000-000000000001",
"name": "María González",
"email": "[email protected]",
"phone": "+56912345001"
},
"stage": {
"id": "55555555-0000-4000-8000-000000000001",
"name": "Nuevo",
"slug": "new",
"position": 0,
"won_state": null,
"pipeline_id": "44444444-0000-4000-8000-000000000001"
},
"pipeline": {
"id": "44444444-0000-4000-8000-000000000001",
"kind": "sales",
"name": "Ventas"
}
}
}Responde: 200 · 400 · 401 · 403 · 404 · 409 · 429
PUT /leads/{id}/stage
Move a lead to another stage
THE move. The target must be a stage of the lead's own board and must be reachable from the current one — a stage graph that forbids the hop is a 400. Landing on a terminal column re-derives the lifecycle from that stage's category, so dragging a card to «Ganado» closes the lead exactly as PUT /leads/\{id\}/won does. Emits lead.stage_changed with changes.stage = \{ from, to \} and the author, plus the outcome Evento when the status actually flipped. A no-op move (already on that stage) emits nothing.
| Parámetro | En | Tipo | Obligatorio | Restricciones |
|---|---|---|---|---|
id | path | string | sí | patrón `^(?:[0-9a-f]8-[0-9a-f]4-[0-9a-f]4-[0-9a-f]4-[0-9a-f]12\ |
Cuerpo
| Campo | Tipo | Obligatorio | Restricciones |
|---|---|---|---|
stage_id | uuid | sí | |
reason | string | null | — | máx. 500 |
suggestion_id | uuid | — |
curl -X PUT https://api.vitrinadev.com/api/v1/leads/<id>/stage \
-H "Authorization: Bearer $VITRINA_KEY" \
-H "Content-Type: application/json" \
-d '{
"stage_id": "55555555-0000-4000-8000-000000000002",
"reason": "la llamamos y confirmó la hora"
}'Ejemplo de respuesta (200)
{
"data": {
"id": "99999999-0000-4000-8000-000000000001",
"tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
"display_id": "L-89",
"display_seq": 89,
"title": "María — prueba de manejo Corolla 2022",
"notes": null,
"status": "open",
"source": "conversation",
"source_ref": null,
"intent": "buy",
"contact_id": "22222222-0000-4000-8000-000000000001",
"pipeline_id": "44444444-0000-4000-8000-000000000001",
"stage_id": "55555555-0000-4000-8000-000000000002",
"conversation_id": "bbbbbbbb-0000-4000-8000-000000000001",
"origin_conversation_id": "bbbbbbbb-0000-4000-8000-000000000001",
"owner_user_id": "11111111-0000-4000-8000-000000000001",
"team_id": null,
"value_amount": 8990000,
"value_currency": "CLP",
"value_amount_close_confirmed_at": null,
"expected_close_at": "2026-10-15T00:00:00.000Z",
"score": 72,
"score_reason": "pidió hora para probar el auto y confirmó día",
"scored_at": "2026-09-21T14:02:11.000Z",
"temperature": "warm",
"temperature_reason": "respondió pidiendo confirmar la hora y no ha vuelto a escribir",
"temperature_at": "2026-09-21T14:02:11.000Z",
"won_lost_reason": null,
"closed_at": null,
"created_by": "11111111-0000-4000-8000-000000000001",
"created_at": "2026-09-18T13:44:02.115Z",
"updated_at": "2026-09-22T08:39:25.338Z",
"last_activity_at": "2026-09-22T08:39:25.338Z",
"contact": {
"id": "22222222-0000-4000-8000-000000000001",
"name": "María González",
"email": "[email protected]",
"phone": "+56912345001"
},
"stage": {
"id": "55555555-0000-4000-8000-000000000002",
"name": "Contactado",
"slug": "contacted",
"position": 1,
"won_state": null,
"pipeline_id": "44444444-0000-4000-8000-000000000001"
},
"pipeline": {
"id": "44444444-0000-4000-8000-000000000001",
"kind": "sales",
"name": "Ventas"
}
}
}Responde: 200 · 400 · 401 · 403 · 404 · 409 · 429
PUT /leads/{id}/unqualify
Mark a lead as unqualified
There was never an opportunity here — the wrong product, the wrong workspace, a duplicate, a test. A DISTINCT outcome from lost, and the distinction is the point: unqualified leads are excluded from win rate on both sides, so qualifying early never looks like losing. The lead MOVES to the lowest-position stage of that category on its board, so the outcome and the column can never disagree — 400 when the board has no stage of that category, which is a board a person has to fix rather than a request to retry. status, closed_at and won_lost_reason are derived from the landing stage, and a lead.stage_changed Evento fires alongside the outcome one. Emits lead.unqualified.
| Parámetro | En | Tipo | Obligatorio | Restricciones |
|---|---|---|---|---|
id | path | string | sí | patrón `^(?:[0-9a-f]8-[0-9a-f]4-[0-9a-f]4-[0-9a-f]4-[0-9a-f]12\ |
Cuerpo
| Campo | Tipo | Obligatorio | Restricciones |
|---|---|---|---|
status | won \ | lost \ | unqualified \ |
reason | string | null | — | máx. 500 |
value_amount | number | null | — | ≥ 0 |
value_currency | string | null | — | mín. 1, máx. 8 |
curl -X PUT https://api.vitrinadev.com/api/v1/leads/<id>/unqualify \
-H "Authorization: Bearer $VITRINA_KEY" \
-H "Content-Type: application/json" \
-d '{
"reason": "buscaba un servicio que no ofrecemos"
}'Ejemplo de respuesta (200)
{
"data": {
"id": "99999999-0000-4000-8000-000000000001",
"tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
"display_id": "L-89",
"display_seq": 89,
"title": "María — prueba de manejo Corolla 2022",
"notes": null,
"status": "unqualified",
"source": "conversation",
"source_ref": null,
"intent": "buy",
"contact_id": "22222222-0000-4000-8000-000000000001",
"pipeline_id": "44444444-0000-4000-8000-000000000001",
"stage_id": "55555555-0000-4000-8000-000000000003",
"conversation_id": "bbbbbbbb-0000-4000-8000-000000000001",
"origin_conversation_id": "bbbbbbbb-0000-4000-8000-000000000001",
"owner_user_id": "11111111-0000-4000-8000-000000000001",
"team_id": null,
"value_amount": 8990000,
"value_currency": "CLP",
"value_amount_close_confirmed_at": null,
"expected_close_at": "2026-10-15T00:00:00.000Z",
"score": 72,
"score_reason": "pidió hora para probar el auto y confirmó día",
"scored_at": "2026-09-21T14:02:11.000Z",
"temperature": "warm",
"temperature_reason": "respondió pidiendo confirmar la hora y no ha vuelto a escribir",
"temperature_at": "2026-09-21T14:02:11.000Z",
"won_lost_reason": "buscaba un servicio que no ofrecemos",
"closed_at": "2026-09-22T08:44:10.331Z",
"created_by": "11111111-0000-4000-8000-000000000001",
"created_at": "2026-09-18T13:44:02.115Z",
"updated_at": "2026-09-21T14:02:11.000Z",
"last_activity_at": "2026-09-21T14:02:11.000Z",
"contact": {
"id": "22222222-0000-4000-8000-000000000001",
"name": "María González",
"email": "[email protected]",
"phone": "+56912345001"
},
"stage": {
"id": "55555555-0000-4000-8000-000000000003",
"name": "No calificado",
"slug": "unqualified",
"position": 8,
"won_state": null,
"pipeline_id": "44444444-0000-4000-8000-000000000001"
},
"pipeline": {
"id": "44444444-0000-4000-8000-000000000001",
"kind": "sales",
"name": "Ventas"
}
}
}Responde: 200 · 400 · 401 · 403 · 404 · 409 · 429
PUT /leads/{id}/won
Mark a lead as won
The opportunity closed in the workspace's favour. The lead MOVES to the lowest-position stage of that category on its board, so the outcome and the column can never disagree — 400 when the board has no stage of that category, which is a board a person has to fix rather than a request to retry. status, closed_at and won_lost_reason are derived from the landing stage, and a lead.stage_changed Evento fires alongside the outcome one. value_amount here is the REAL closing figure — distinct from any earlier estimate on the lead — and is the only status endpoint that acts on it: it overwrites the value and stamps value_amount_close_confirmed_at. Omit it to leave the estimate untouched. Emits lead.won.
| Parámetro | En | Tipo | Obligatorio | Restricciones |
|---|---|---|---|---|
id | path | string | sí | patrón `^(?:[0-9a-f]8-[0-9a-f]4-[0-9a-f]4-[0-9a-f]4-[0-9a-f]12\ |
Cuerpo
| Campo | Tipo | Obligatorio | Restricciones |
|---|---|---|---|
status | won \ | lost \ | unqualified \ |
reason | string | null | — | máx. 500 |
value_amount | number | null | — | ≥ 0 |
value_currency | string | null | — | mín. 1, máx. 8 |
curl -X PUT https://api.vitrinadev.com/api/v1/leads/<id>/won \
-H "Authorization: Bearer $VITRINA_KEY" \
-H "Content-Type: application/json" \
-d '{
"reason": "cerró con financiamiento propio",
"value_amount": 8750000,
"value_currency": "CLP"
}'Ejemplo de respuesta (200)
{
"data": {
"id": "99999999-0000-4000-8000-000000000001",
"tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
"display_id": "L-89",
"display_seq": 89,
"title": "María — prueba de manejo Corolla 2022",
"notes": null,
"status": "won",
"source": "conversation",
"source_ref": null,
"intent": "buy",
"contact_id": "22222222-0000-4000-8000-000000000001",
"pipeline_id": "44444444-0000-4000-8000-000000000001",
"stage_id": "55555555-0000-4000-8000-000000000006",
"conversation_id": "bbbbbbbb-0000-4000-8000-000000000001",
"origin_conversation_id": "bbbbbbbb-0000-4000-8000-000000000001",
"owner_user_id": "11111111-0000-4000-8000-000000000001",
"team_id": null,
"value_amount": 8750000,
"value_currency": "CLP",
"value_amount_close_confirmed_at": "2026-09-22T08:41:32.967Z",
"expected_close_at": "2026-10-15T00:00:00.000Z",
"score": 72,
"score_reason": "pidió hora para probar el auto y confirmó día",
"scored_at": "2026-09-21T14:02:11.000Z",
"temperature": "warm",
"temperature_reason": "respondió pidiendo confirmar la hora y no ha vuelto a escribir",
"temperature_at": "2026-09-21T14:02:11.000Z",
"won_lost_reason": "cerró con financiamiento propio",
"closed_at": "2026-09-22T08:41:32.977Z",
"created_by": "11111111-0000-4000-8000-000000000001",
"created_at": "2026-09-18T13:44:02.115Z",
"updated_at": "2026-09-21T14:02:11.000Z",
"last_activity_at": "2026-09-21T14:02:11.000Z",
"contact": {
"id": "22222222-0000-4000-8000-000000000001",
"name": "María González",
"email": "[email protected]",
"phone": "+56912345001"
},
"stage": {
"id": "55555555-0000-4000-8000-000000000006",
"name": "Ganado",
"slug": "won",
"position": 6,
"won_state": "won",
"pipeline_id": "44444444-0000-4000-8000-000000000001"
},
"pipeline": {
"id": "44444444-0000-4000-8000-000000000001",
"kind": "sales",
"name": "Ventas"
}
}
}Responde: 200 · 400 · 401 · 403 · 404 · 409 · 429
POST /leads/from-conversation
Convert a conversation into a lead
Opens an opportunity out of a conversation that already has a contact: the contact is resolved from the thread (404 when it has none), the conversation becomes the lead's ORIGIN and its primary link, and a lead opened on a thread somebody already owns inherits that owner unless you name one. Emits lead.created with source: conversation.
Cuerpo
| Campo | Tipo | Obligatorio | Restricciones |
|---|---|---|---|
conversation_id | uuid | sí | |
title | string | — | mín. 1, máx. 500 |
notes | string | — | máx. 20000 |
pipeline_id | uuid | — | |
stage_id | uuid | — | |
owner_user_id | string | null | — | |
value_amount | number | null | — | ≥ 0 |
curl -X POST https://api.vitrinadev.com/api/v1/leads/from-conversation \
-H "Authorization: Bearer $VITRINA_KEY" \
-H "Content-Type: application/json" \
-d '{
"conversation_id": "bbbbbbbb-0000-4000-8000-000000000001",
"title": "María — prueba de manejo Corolla 2022",
"pipeline_id": "44444444-0000-4000-8000-000000000001",
"owner_user_id": "11111111-0000-4000-8000-000000000001"
}'Ejemplo de respuesta (201)
{
"data": {
"id": "99999999-0000-4000-8000-000000000001",
"tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
"display_id": "L-89",
"display_seq": 89,
"title": "María — prueba de manejo Corolla 2022",
"notes": null,
"status": "open",
"source": "conversation",
"source_ref": null,
"intent": "buy",
"contact_id": "22222222-0000-4000-8000-000000000001",
"pipeline_id": "44444444-0000-4000-8000-000000000001",
"stage_id": "55555555-0000-4000-8000-000000000001",
"conversation_id": "bbbbbbbb-0000-4000-8000-000000000001",
"origin_conversation_id": "bbbbbbbb-0000-4000-8000-000000000001",
"owner_user_id": "11111111-0000-4000-8000-000000000001",
"team_id": null,
"value_amount": 8990000,
"value_currency": "CLP",
"value_amount_close_confirmed_at": null,
"expected_close_at": "2026-10-15T00:00:00.000Z",
"score": 72,
"score_reason": "pidió hora para probar el auto y confirmó día",
"scored_at": "2026-09-21T14:02:11.000Z",
"temperature": "warm",
"temperature_reason": "respondió pidiendo confirmar la hora y no ha vuelto a escribir",
"temperature_at": "2026-09-21T14:02:11.000Z",
"won_lost_reason": null,
"closed_at": null,
"created_by": "11111111-0000-4000-8000-000000000001",
"created_at": "2026-09-18T13:44:02.115Z",
"updated_at": "2026-09-21T14:02:11.000Z",
"last_activity_at": "2026-09-21T14:02:11.000Z",
"contact": {
"id": "22222222-0000-4000-8000-000000000001",
"name": "María González",
"email": "[email protected]",
"phone": "+56912345001"
},
"stage": {
"id": "55555555-0000-4000-8000-000000000001",
"name": "Nuevo",
"slug": "new",
"position": 0,
"won_state": null,
"pipeline_id": "44444444-0000-4000-8000-000000000001"
},
"pipeline": {
"id": "44444444-0000-4000-8000-000000000001",
"kind": "sales",
"name": "Ventas"
}
}
}Responde: 201 · 400 · 401 · 403 · 404 · 409 · 429
GET /leads/funnel
Lead funnel by stage
One row per stage of a board, in position order: how many OPEN leads sit there, what they are worth, and the MEDIAN hours a lead has been sitting on that stage — the number that says where the funnel is stuck. Money is value_amount + value_currency and the pair travels together: a workspace may work in more than one currency, so the analytics endpoints break their totals down by_currency and flag is_mixed_currency rather than summing across them.
| Parámetro | En | Tipo | Obligatorio | Restricciones |
|---|---|---|---|---|
pipeline_id | query | string | sí | mín. 1 |
card_kind | query | all \ | leads \ | solicitudes |
curl https://api.vitrinadev.com/api/v1/leads/funnel \
-H "Authorization: Bearer $VITRINA_KEY"Ejemplo de respuesta (200)
{
"data": [
{
"stage_id": "55555555-0000-4000-8000-000000000001",
"stage_name": "Nuevo",
"stage_slug": "new",
"position": 0,
"open_count": 6,
"total_value": 193990000,
"total_value_by_currency": {
"CLP": 193990000
},
"is_mixed_currency": false,
"median_time_in_stage_hours": 350.91
},
{
"stage_id": "55555555-0000-4000-8000-000000000002",
"stage_name": "Contactado",
"stage_slug": "contacted",
"position": 1,
"open_count": 2,
"total_value": 15450000,
"total_value_by_currency": {
"CLP": 15450000
},
"is_mixed_currency": false,
"median_time_in_stage_hours": 427.53
}
]
}Responde: 200 · 400 · 401 · 403 · 404 · 409 · 429
POST /leads/import
Bulk-import leads
Up to 2 000 rows in one call — a trade-show list, a spreadsheet, a migration from another system. Each row is matched to an existing contact by contact_email or contact_phone, and to a stage by stage_slug / an owner by owner_email. PARTIAL SUCCESS IS THE NORMAL OUTCOME and the status is 200 either way: the report says how many rows landed and gives a per-row ok with the lead_id or the reason it failed. Imported leads skip owner rotation — the file decides the owner.
Cuerpo
| Campo | Tipo | Obligatorio | Restricciones |
|---|---|---|---|
rows | object[] | sí |
curl -X POST https://api.vitrinadev.com/api/v1/leads/import \
-H "Authorization: Bearer $VITRINA_KEY" \
-H "Content-Type: application/json" \
-d '{
"rows": [
{
"title": "Consulta feria de autos — stand 12",
"contact_email": "[email protected]",
"value_amount": 7500000,
"currency": "CLP",
"stage_slug": "new",
"source": "import"
},
{
"title": "Consulta feria de autos — stand 14",
"contact_phone": "+56988887777",
"owner_email": "[email protected]"
}
]
}'Ejemplo de respuesta (200)
{
"data": {
"total": 2,
"inserted": 1,
"failed": 1,
"rows": [
{
"row": 1,
"ok": true,
"lead_id": "99999999-0000-4000-8000-000000000001"
},
{
"row": 2,
"ok": false,
"error": "No contact matched +56988887777"
}
]
}
}Responde: 200 · 400 · 401 · 403 · 404 · 409 · 429
GET /leads/kanban
Leads grouped by stage (board view)
The board itself: every stage with its leads already nested, up to 200 per column — one request instead of one per column. The cards carry the same embeds and the same scope gating as GET /leads.
| Parámetro | En | Tipo | Obligatorio | Restricciones |
|---|---|---|---|---|
pipeline_id | query | string | sí | mín. 1 |
card_kind | query | all \ | leads \ | solicitudes |
curl https://api.vitrinadev.com/api/v1/leads/kanban \
-H "Authorization: Bearer $VITRINA_KEY"Ejemplo de respuesta (200)
{
"data": {
"stages": [
{
"id": "55555555-0000-4000-8000-000000000001",
"name": "Nuevo",
"slug": "new",
"position": 0,
"won_state": null,
"is_terminal": false,
"leads": [
{
"id": "99999999-0000-4000-8000-000000000001",
"tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
"display_id": "L-89",
"display_seq": 89,
"title": "María — prueba de manejo Corolla 2022",
"notes": null,
"status": "open",
"source": "conversation",
"source_ref": null,
"intent": "buy",
"contact_id": "22222222-0000-4000-8000-000000000001",
"pipeline_id": "44444444-0000-4000-8000-000000000001",
"stage_id": "55555555-0000-4000-8000-000000000001",
"conversation_id": "bbbbbbbb-0000-4000-8000-000000000001",
"origin_conversation_id": "bbbbbbbb-0000-4000-8000-000000000001",
"owner_user_id": "11111111-0000-4000-8000-000000000001",
"team_id": null,
"value_amount": 8990000,
"value_currency": "CLP",
"value_amount_close_confirmed_at": null,
"expected_close_at": "2026-10-15T00:00:00.000Z",
"score": 72,
"score_reason": "pidió hora para probar el auto y confirmó día",
"scored_at": "2026-09-21T14:02:11.000Z",
"temperature": "warm",
"temperature_reason": "respondió pidiendo confirmar la hora y no ha vuelto a escribir",
"temperature_at": "2026-09-21T14:02:11.000Z",
"won_lost_reason": null,
"closed_at": null,
"created_by": "11111111-0000-4000-8000-000000000001",
"created_at": "2026-09-18T13:44:02.115Z",
"updated_at": "2026-09-21T14:02:11.000Z",
"last_activity_at": "2026-09-21T14:02:11.000Z",
"contact": {
"id": "22222222-0000-4000-8000-000000000001",
"name": "María González",
"email": "[email protected]",
"phone": "+56912345001"
},
"stage": {
"id": "55555555-0000-4000-8000-000000000001",
"name": "Nuevo",
"slug": "new",
"position": 0,
"won_state": null,
"pipeline_id": "44444444-0000-4000-8000-000000000001"
},
"pipeline": {
"id": "44444444-0000-4000-8000-000000000001",
"kind": "sales",
"name": "Ventas"
}
}
]
},
{
"id": "55555555-0000-4000-8000-000000000002",
"name": "Contactado",
"slug": "contacted",
"position": 1,
"won_state": null,
"is_terminal": false,
"leads": [
{
"id": "99999999-0000-4000-8000-000000000002",
"tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
"display_id": "L-90",
"display_seq": 90,
"title": "Consulta por financiamiento — Ana Soto",
"notes": null,
"status": "open",
"source": "website",
"source_ref": null,
"intent": "financing",
"contact_id": "22222222-0000-4000-8000-000000000001",
"pipeline_id": "44444444-0000-4000-8000-000000000001",
"stage_id": "55555555-0000-4000-8000-000000000002",
"conversation_id": null,
"origin_conversation_id": null,
"owner_user_id": null,
"team_id": null,
"value_amount": null,
"value_currency": "CLP",
"value_amount_close_confirmed_at": null,
"expected_close_at": null,
"score": null,
"score_reason": null,
"scored_at": null,
"temperature": null,
"temperature_reason": null,
"temperature_at": null,
"won_lost_reason": null,
"closed_at": null,
"created_by": null,
"created_at": "2026-09-19T09:12:44.000Z",
"updated_at": "2026-09-19T09:12:44.000Z",
"last_activity_at": "2026-09-19T09:12:44.000Z",
"contact": {
"id": "22222222-0000-4000-8000-000000000001",
"name": "Ana Soto",
"email": null,
"phone": "+56912345001"
},
"stage": {
"id": "55555555-0000-4000-8000-000000000002",
"name": "Contactado",
"slug": "contacted",
"position": 1,
"won_state": null,
"pipeline_id": "44444444-0000-4000-8000-000000000001"
},
"pipeline": {
"id": "44444444-0000-4000-8000-000000000001",
"kind": "sales",
"name": "Ventas"
}
}
]
}
]
}
}Responde: 200 · 400 · 401 · 403 · 404 · 409 · 429
GET /leads/stats
Lead score bands
The count in each SCORE band — cold 0–39, warm 40–69, hot 70–100, unscored — plus the average score. Bands over lead.score, not over the recorded temperature judgement; the two are different facts about a lead and neither is derived from the other.
| Parámetro | En | Tipo | Obligatorio | Restricciones |
|---|---|---|---|---|
pipeline_id | query | string | no | mín. 1 |
curl https://api.vitrinadev.com/api/v1/leads/stats \
-H "Authorization: Bearer $VITRINA_KEY"Ejemplo de respuesta (200)
{
"data": {
"total": 16,
"hot": 2,
"warm": 0,
"cold": 8,
"unscored": 6,
"avg_score": 28.5
}
}Responde: 200 · 400 · 401 · 403 · 404 · 409 · 429
GET /leads/summary
Lead pipeline summary
How many leads are open, won, lost and unqualified, and what they are worth. Optionally narrowed to one board with pipeline_id. Money is value_amount + value_currency and the pair travels together: a workspace may work in more than one currency, so the analytics endpoints break their totals down by_currency and flag is_mixed_currency rather than summing across them.
| Parámetro | En | Tipo | Obligatorio | Restricciones |
|---|---|---|---|---|
pipeline_id | query | string | no | mín. 1 |
curl https://api.vitrinadev.com/api/v1/leads/summary \
-H "Authorization: Bearer $VITRINA_KEY"Ejemplo de respuesta (200)
{
"data": {
"open": 13,
"won": 2,
"lost": 1,
"unqualified": 0,
"total_value_open": 194077750,
"total_value_won": 2499000,
"total_value_open_by_currency": {
"CLP": 193990000,
"USD": 42750
},
"total_value_won_by_currency": {
"CLP": 2499000
},
"is_mixed_currency": true,
"is_mixed_currency_open": true
}
}Responde: 200 · 400 · 401 · 403 · 404 · 409 · 429
GET /leads/win-rate/{dimension}
Win rate by owner, team or source
Won / (won + lost) grouped by owner_user_id, team_id or source. UNQUALIFIED leads are excluded from both sides on purpose: they were never a sale to win or lose, and counting them as losses would punish whoever is best at qualifying early. key is the owner or team uuid, or the source name — and the literal string unassigned for the bucket of closed leads that carried no owner or team at all.
| Parámetro | En | Tipo | Obligatorio | Restricciones |
|---|---|---|---|---|
dimension | path | owner_user_id \ | team_id \ | source |
pipeline_id | query | string | no | mín. 1 |
curl https://api.vitrinadev.com/api/v1/leads/win-rate/<id> \
-H "Authorization: Bearer $VITRINA_KEY"Ejemplo de respuesta (200)
{
"data": [
{
"dimension": "owner_user_id",
"key": "11111111-0000-4000-8000-000000000001",
"won": 2,
"lost": 1,
"win_rate": 0.6666666666666666
}
]
}Responde: 200 · 400 · 401 · 403 · 404 · 409 · 429