VitrinaAPI

Analytics

Los reportes del panel: volumen y resolución de conversaciones, desempeño humano y de IA, uso de herramientas y de tokens, y latencia.

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.

Medir el workspace explica este recurso en prosa, con ejemplos ejecutables.

MétodoRutaQué hace
GET/analytics/latencyp50/p95/p99 latency by kind over the time window
GET/analytics/overviewAggregate counts for a date range
GET/analytics/resolution-timesTicket resolution latency stats (avg/p50/p95 + by_resolved_by)
GET/analytics/timeseriesDay-bucketed series for one metric
GET/analytics/token-usageDaily token usage by model (prompt + completion + total)
GET/analytics/toolsTop tool calls in a date range
GET/insights/agentsHuman agent performance
GET/insights/ai-agentsPer-AI-agent performance breakdown
GET/insights/botsAggregate AI handling
GET/insights/callsVoice call statistics
GET/insights/channelsVolume by channel
GET/insights/conversationsConversation volume and resolution
GET/insights/csatEstimated satisfaction
POST/insights/generate-pdfRender a report as a PDF
GET/insights/leadsLead capture
GET/insights/overview/heatmapActivity heatmap by day and hour
GET/insights/overview/liveCurrent live activity
GET/insights/schedulesList report schedules
GET/insights/slaSLA compliance
GET/insights/sourcesVolume by origin
GET/insights/storefrontStorefront performance for a calendar month
GET/insights/storefront/cardStorefront summary for the CRM dashboard "Mi sitio" card
GET/insights/tagsVolume by tag
GET/insights/teamsVolume by team

GET /analytics/latency

p50/p95/p99 latency by kind over the time window

Operational timing, not business metrics: kind groups spans by name (agent_turn, tool_call, …) and each row carries count plus its p50_ms/p95_ms/p99_ms. since defaults to the last hour; omit kind to get every kind.

ParámetroEnTipoObligatorioRestricciones
sincequerystringnodate-time
kindquerystringnomín. 1, máx. 60
curl https://api.vitrinadev.com/api/v1/analytics/latency \
  -H "Authorization: Bearer $VITRINA_KEY"

Ejemplo de respuesta (200)

{
  "data": [
    {
      "kind": "agent_turn",
      "p50_ms": 1120,
      "p95_ms": 3400,
      "p99_ms": 5600,
      "count": 940
    },
    {
      "kind": "tool_call",
      "p50_ms": 340,
      "p95_ms": 980,
      "p99_ms": 1800,
      "count": 310
    }
  ]
}

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

GET /analytics/overview

Aggregate counts for a date range

Headline counts for fromto: conversations, messages, tickets resolved (split resolved_by_bot/_human/_auto), tool calls, distinct contacts, plus two derived rates — bot_resolution_rate (bot / every resolution) and tool_use_per_message. Optional ai_agent_id narrows every figure to one agent.

ParámetroEnTipoObligatorioRestricciones
fromquerystringdate-time
toquerystringdate-time
ai_agent_idqueryuuidno
curl https://api.vitrinadev.com/api/v1/analytics/overview \
  -H "Authorization: Bearer $VITRINA_KEY"

Ejemplo de respuesta (200)

{
  "data": {
    "conversations_count": 182,
    "messages_count": 940,
    "tickets_resolved_count": 140,
    "resolved_by_bot": 96,
    "resolved_by_human": 40,
    "resolved_by_auto": 4,
    "tool_calls_count": 310,
    "distinct_contacts_count": 165,
    "bot_resolution_rate": 0.6857,
    "tool_use_per_message": 0.3298
  }
}

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

GET /analytics/resolution-times

Ticket resolution latency stats (avg/p50/p95 + by_resolved_by)

Time-to-resolution over fromto: count/avg_seconds/p50_seconds/p95_seconds overall, then the same four figures again per resolved_by value (bot/human/auto) in by_resolved_by — so "how fast is the bot vs. a human" is one call, not three.

ParámetroEnTipoObligatorioRestricciones
fromquerystringdate-time
toquerystringdate-time
curl https://api.vitrinadev.com/api/v1/analytics/resolution-times \
  -H "Authorization: Bearer $VITRINA_KEY"

Ejemplo de respuesta (200)

{
  "data": {
    "count": 140,
    "avg_seconds": 512.4,
    "p50_seconds": 260,
    "p95_seconds": 1840,
    "by_resolved_by": {
      "bot": {
        "count": 96,
        "avg_seconds": 180.2,
        "p50_seconds": 120,
        "p95_seconds": 600
      },
      "human": {
        "count": 40,
        "avg_seconds": 1120.8,
        "p50_seconds": 900,
        "p95_seconds": 3200
      },
      "auto": {
        "count": 4,
        "avg_seconds": 30.1,
        "p50_seconds": 28,
        "p95_seconds": 60
      }
    }
  }
}

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

GET /analytics/timeseries

Day-bucketed series for one metric

metric selects one of conversations | messages | tickets_resolved | tool_calls; the response is one point per day in fromto — a day with no activity is a zero-valued point, not an absent one.

ParámetroEnTipoObligatorioRestricciones
fromquerystringdate-time
toquerystringdate-time
metricqueryconversations \messages \tickets_resolved \
curl https://api.vitrinadev.com/api/v1/analytics/timeseries \
  -H "Authorization: Bearer $VITRINA_KEY"

Ejemplo de respuesta (200)

{
  "data": [
    {
      "bucket": "2026-09-20",
      "value": 24
    },
    {
      "bucket": "2026-09-21",
      "value": 31
    },
    {
      "bucket": "2026-09-22",
      "value": 18
    }
  ]
}

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

GET /analytics/token-usage

Daily token usage by model (prompt + completion + total)

One row per (day, model) over fromto: prompt_tokens, completion_tokens, total_tokens. model is the OpenRouter model id the platform actually ran — every model this workspace’s agents run is flash-tier; the platform never routes a turn to a "pro"/reasoning-max model.

ParámetroEnTipoObligatorioRestricciones
fromquerystringdate-time
toquerystringdate-time
curl https://api.vitrinadev.com/api/v1/analytics/token-usage \
  -H "Authorization: Bearer $VITRINA_KEY"

Ejemplo de respuesta (200)

{
  "data": [
    {
      "bucket": "2026-09-22",
      "model": "xiaomi/mimo-v2.6-flash",
      "prompt_tokens": 812000,
      "completion_tokens": 96000,
      "total_tokens": 908000
    },
    {
      "bucket": "2026-09-22",
      "model": "google/gemini-2.5-flash",
      "prompt_tokens": 220000,
      "completion_tokens": 31000,
      "total_tokens": 251000
    }
  ]
}

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

GET /analytics/tools

Top tool calls in a date range

The most-called tools over fromto, ranked by call_count (default top 20, max 200 via limit). function_name is null for a call logged against a tool that has since been deleted.

ParámetroEnTipoObligatorioRestricciones
fromquerystringdate-time
toquerystringdate-time
limitqueryintegerno≥ 1, ≤ 200, por defecto 20
curl https://api.vitrinadev.com/api/v1/analytics/tools \
  -H "Authorization: Bearer $VITRINA_KEY"

Ejemplo de respuesta (200)

{
  "data": [
    {
      "function_id": "b6b6b6b6-1000-4000-8000-000000000001",
      "function_name": "search_vehicle_stock",
      "call_count": 214
    },
    {
      "function_id": "b6b6b6b6-1000-4000-8000-000000000002",
      "function_name": "book_appointment",
      "call_count": 58
    }
  ]
}

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

GET /insights/agents

Human agent performance

Per-human-agent throughput and response times. /insights/ai-agents is the machine-side counterpart; the two are not comparable row for row, because a human is assigned a conversation and an AI agent runs turns within one.

ParámetroEnTipoObligatorioRestricciones
windowquery24h \7d \30d \
monthquerystringnopatrón `^\d4-(0[1-9]\
yearquerystringnopatrón ^\d{4}$
fromquerystringno
toquerystringno
curl https://api.vitrinadev.com/api/v1/insights/agents \
  -H "Authorization: Bearer $VITRINA_KEY"

Ejemplo de respuesta (200)

{
  "data": [
    {
      "user_id": "11111111-0000-4000-8000-000000000001",
      "name": "Carolina Muñoz",
      "email": "[email protected]",
      "conversations": 8,
      "replies": 5,
      "avg_first_response_seconds": 733.76,
      "avg_response_seconds": 28197.49,
      "avg_resolution_seconds": 9891.67,
      "avg_customer_wait_seconds": 733.76,
      "resolutions": 4
    }
  ],
  "meta": {
    "window": {
      "kind": "rolling",
      "preset": "7d",
      "hours": 168,
      "from": "2026-09-15T16:38:51.000Z",
      "to": "2026-09-22T16:38:51.000Z",
      "label": "últimos 7d"
    }
  }
}

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

GET /insights/ai-agents

Per-AI-agent performance breakdown

One row per ai_agent. /insights/bots answers the same question in aggregate — use that for a single headline number and this to find which agent moved it.

ParámetroEnTipoObligatorioRestricciones
windowquery24h \7d \30d \
monthquerystringnopatrón `^\d4-(0[1-9]\
yearquerystringnopatrón ^\d{4}$
fromquerystringno
toquerystringno
curl https://api.vitrinadev.com/api/v1/insights/ai-agents \
  -H "Authorization: Bearer $VITRINA_KEY"

Ejemplo de respuesta (200)

{
  "data": [
    {
      "ai_agent_id": "a3a3a3a3-0000-4000-8000-000000000001",
      "name": "Asistente de ventas",
      "status": "active",
      "is_default": true,
      "conversations": 12,
      "responses": 30,
      "avg_response_seconds": 4.2,
      "automated_conversations": 8,
      "handoffs": 2,
      "resolutions": 4,
      "tool_calls": 21,
      "cost_usd": 0.0508,
      "tokens": 322522,
      "avg_gen_latency_ms": 7938,
      "handoffs_requested": 2,
      "handoffs_answered": 2
    }
  ],
  "meta": {
    "window": {
      "kind": "rolling",
      "preset": "7d",
      "hours": 168,
      "from": "2026-09-15T16:38:51.000Z",
      "to": "2026-09-22T16:38:51.000Z",
      "label": "últimos 7d"
    }
  }
}

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

GET /insights/bots

Aggregate AI handling

AI handling across the workspace as one figure, not split by agent. The aggregate counterpart to /insights/ai-agents.

ParámetroEnTipoObligatorioRestricciones
windowquery24h \7d \30d \
monthquerystringnopatrón `^\d4-(0[1-9]\
yearquerystringnopatrón ^\d{4}$
fromquerystringno
toquerystringno
curl https://api.vitrinadev.com/api/v1/insights/bots \
  -H "Authorization: Bearer $VITRINA_KEY"

Ejemplo de respuesta (200)

{
  "data": {
    "conversations": 12,
    "automated_conversations": 8,
    "total_conversations": 18,
    "engaged_conversations": 11,
    "deflection_rate": 0.44,
    "total_responses": 30,
    "avg_response_seconds": 4.2,
    "resolutions": 4,
    "handoff_rate": 0.17,
    "resolved_by_bot": 3,
    "resolved_by_human": 1,
    "resolved_by_auto": 0,
    "cost_usd": 0.0508,
    "cost_per_conversation": 0.0042,
    "tokens": 322522,
    "avg_gen_latency_ms": 7938,
    "tool_calls": 21,
    "resolutions_series": [
      {
        "bucket": "2026-09-22",
        "value": 3
      }
    ],
    "handoff_series": [
      {
        "bucket": "2026-09-22",
        "value": 1
      }
    ],
    "cost_series": [
      {
        "bucket": "2026-09-22",
        "value": 0.0064
      }
    ]
  },
  "meta": {
    "window": {
      "kind": "rolling",
      "preset": "7d",
      "hours": 168,
      "from": "2026-09-15T16:38:51.000Z",
      "to": "2026-09-22T16:38:51.000Z",
      "label": "últimos 7d"
    }
  }
}

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

GET /insights/calls

Voice call statistics

Voice-channel call stats for the window. Served by the voice insights service rather than the general reporting one, so its shape does not match the other reports.

ParámetroEnTipoObligatorioRestricciones
windowquery24h \7d \30d \
monthquerystringnopatrón `^\d4-(0[1-9]\
yearquerystringnopatrón ^\d{4}$
fromquerystringno
toquerystringno
curl https://api.vitrinadev.com/api/v1/insights/calls \
  -H "Authorization: Bearer $VITRINA_KEY"

Ejemplo de respuesta (200)

{
  "data": {
    "range": {
      "from": "2026-09-15T16:38:51.540Z",
      "to": "2026-09-22T16:38:51.540Z"
    },
    "calls": {
      "total": 6,
      "answered": 5,
      "avg_duration_s": 142,
      "by_outcome": {
        "completed": 5,
        "no_answer": 1
      },
      "by_direction": {
        "inbound": 4,
        "outbound": 2
      },
      "by_amd": {}
    },
    "latency": {
      "voice_to_voice": 820,
      "voice_llm_turn": 410,
      "voice_llm_first_token": 190,
      "tool_execution": 260
    }
  },
  "meta": {
    "window": {
      "kind": "rolling",
      "preset": "7d",
      "hours": 168,
      "from": "2026-09-15T16:38:51.000Z",
      "to": "2026-09-22T16:38:51.000Z",
      "label": "últimos 7d"
    }
  }
}

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

GET /insights/channels

Volume by channel

Grouped by messaging channel (WhatsApp, Instagram, email, voice, …), so a channel with no traffic in the window is simply absent rather than present with a zero.

ParámetroEnTipoObligatorioRestricciones
windowquery24h \7d \30d \
monthquerystringnopatrón `^\d4-(0[1-9]\
yearquerystringnopatrón ^\d{4}$
fromquerystringno
toquerystringno
curl https://api.vitrinadev.com/api/v1/insights/channels \
  -H "Authorization: Bearer $VITRINA_KEY"

Ejemplo de respuesta (200)

{
  "data": [
    {
      "key": "00000000-0000-4000-8000-000000005001",
      "label": "WhatsApp · Acme",
      "conversations": 5,
      "avg_first_response_seconds": 450.43,
      "avg_resolution_seconds": 101.4,
      "avg_customer_wait_seconds": 450.43,
      "resolutions": 1
    },
    {
      "key": "__none__",
      "label": "Directo / sin canal",
      "conversations": 4,
      "avg_first_response_seconds": 0,
      "avg_resolution_seconds": 0,
      "avg_customer_wait_seconds": 0,
      "resolutions": 0
    }
  ],
  "meta": {
    "window": {
      "kind": "rolling",
      "preset": "7d",
      "hours": 168,
      "from": "2026-09-15T16:38:51.000Z",
      "to": "2026-09-22T16:38:51.000Z",
      "label": "últimos 7d"
    }
  }
}

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

GET /insights/conversations

Conversation volume and resolution

Volume, resolution and handling time over the window — the top-level view the other reports break down.

ParámetroEnTipoObligatorioRestricciones
windowquery24h \7d \30d \
monthquerystringnopatrón `^\d4-(0[1-9]\
yearquerystringnopatrón ^\d{4}$
fromquerystringno
toquerystringno
curl https://api.vitrinadev.com/api/v1/insights/conversations \
  -H "Authorization: Bearer $VITRINA_KEY"

Ejemplo de respuesta (200)

{
  "data": {
    "conversations": {
      "total": 18,
      "delta_pct": 17,
      "series": [
        {
          "bucket": "2026-09-21",
          "value": 0
        },
        {
          "bucket": "2026-09-22",
          "value": 18
        }
      ]
    },
    "messages_in": {
      "total": 11,
      "delta_pct": null,
      "series": [
        {
          "bucket": "2026-09-22",
          "value": 10
        }
      ]
    },
    "messages_out": {
      "total": 25,
      "delta_pct": null,
      "series": [
        {
          "bucket": "2026-09-22",
          "value": 25
        }
      ]
    },
    "first_response_seconds": {
      "avg": 728.65,
      "delta_pct": null,
      "series": [
        {
          "bucket": "2026-09-22",
          "value": 728.65
        }
      ]
    },
    "first_response_p50_seconds": 388.36,
    "first_response_p90_seconds": 1411.41,
    "resolution_seconds": {
      "avg": 9891.67,
      "delta_pct": -91.3,
      "series": [
        {
          "bucket": "2026-09-22",
          "value": 9891.67
        }
      ]
    },
    "resolutions": {
      "total": 5,
      "delta_pct": -16.7,
      "series": [
        {
          "bucket": "2026-09-22",
          "value": 4
        }
      ]
    },
    "customer_wait_seconds": {
      "avg": 728.65,
      "delta_pct": null,
      "series": [
        {
          "bucket": "2026-09-22",
          "value": 728.65
        }
      ]
    },
    "facturables": 4,
    "solo_humano": 14
  },
  "meta": {
    "window": {
      "kind": "rolling",
      "preset": "7d",
      "hours": 168,
      "from": "2026-09-15T16:38:51.000Z",
      "to": "2026-09-22T16:38:51.000Z",
      "label": "últimos 7d"
    }
  }
}

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

GET /insights/csat

Estimated satisfaction

CSAT is AI-ESTIMATED from the conversation, not collected from a survey — no customer was asked to rate anything. Read it as a model judgement about tone and outcome, which is why it exists for every conversation rather than the small fraction that would answer a survey.

ParámetroEnTipoObligatorioRestricciones
windowquery24h \7d \30d \
monthquerystringnopatrón `^\d4-(0[1-9]\
yearquerystringnopatrón ^\d{4}$
fromquerystringno
toquerystringno
curl https://api.vitrinadev.com/api/v1/insights/csat \
  -H "Authorization: Bearer $VITRINA_KEY"

Ejemplo de respuesta (200)

{
  "data": {
    "total_responses": 9,
    "satisfaction_score": 0.82,
    "response_rate": 0.5,
    "positive": 7,
    "neutral": 1,
    "negative": 1,
    "recommend_rate": 0.78,
    "avg_confidence": 0.9,
    "review_requests_sent": 18,
    "resolved_tickets": 5,
    "distribution": {
      "positive": 7,
      "neutral": 1,
      "negative": 1
    },
    "resolution_quality": {
      "good": 8,
      "poor": 1
    },
    "agent_performance": {},
    "recent": []
  },
  "meta": {
    "window": {
      "kind": "rolling",
      "preset": "7d",
      "hours": 168,
      "from": "2026-09-15T16:38:51.000Z",
      "to": "2026-09-22T16:38:51.000Z",
      "label": "últimos 7d"
    }
  }
}

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

POST /insights/generate-pdf

Render a report as a PDF

The one endpoint on the tenant API that does not answer the \{ data \} envelope. It replies with raw application/pdf bytes and a Content-Disposition: attachment header, so a client that pipes every response through the usual JSON parse will fail here. On an error it reverts to the JSON error envelope, which means the response content-type is what tells the two apart.

Generation is SYNCHRONOUS: it queries the report, runs an LLM pass to write the narrative, then renders. The connection is held for all of that, there is no job id, and nothing is persisted — a client or proxy timeout loses the document with no way to retrieve it other than asking again. Treat a wide range as a slow request.

The LLM pass degrades rather than fails: if the narrative cannot be generated you still get a 200 and a complete PDF, with a Spanish note in place of the executive summary and every raw figure intact. So a successful response does not by itself mean the analysis is there.

range.preset is not a range selector — from/to alone decide the data. It is the window’s NAME: printed as the cover subtitle and passed into the summariser’s prompt, so a preset that disagrees with from/to mislabels the cover and misleads the narrative while the figures stay correct. Defaults to custom. Needs only analytics:read.

Cuerpo

CampoTipoObligatorioRestricciones
report_typeoverview \conversations \agents \
rangeobject
curl -X POST https://api.vitrinadev.com/api/v1/insights/generate-pdf \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "report": "conversations",
    "window": {
      "preset": "7d"
    },
    "range": {
      "preset": "7d"
    }
  }'

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

GET /insights/leads

Lead capture

Leads created over the window and where they came from.

ParámetroEnTipoObligatorioRestricciones
windowquery24h \7d \30d \
monthquerystringnopatrón `^\d4-(0[1-9]\
yearquerystringnopatrón ^\d{4}$
fromquerystringno
toquerystringno
curl https://api.vitrinadev.com/api/v1/insights/leads \
  -H "Authorization: Bearer $VITRINA_KEY"

Ejemplo de respuesta (200)

{
  "data": {
    "open": 16,
    "won": 2,
    "lost": 1,
    "unqualified": 0,
    "total_value_open": 19407775,
    "total_value_won": 4500000,
    "total_value_open_by_currency": {
      "CLP": 19399000
    },
    "total_value_won_by_currency": {
      "CLP": 4500000
    },
    "is_mixed_currency": false,
    "is_mixed_currency_open": false,
    "win_rate": 0.67,
    "avg_cycle_seconds": 432000,
    "created_series": [
      {
        "bucket": "2026-09-22",
        "value": 6
      }
    ],
    "won_series": [
      {
        "bucket": "2026-09-22",
        "value": 1
      }
    ],
    "revenue_series": [
      {
        "bucket": "2026-09-22",
        "value": 4500000,
        "value_by_currency": {
          "CLP": 4500000
        },
        "is_mixed_currency": false
      }
    ],
    "funnel": [
      {
        "stage_id": "55555555-0000-4000-8000-000000000001",
        "stage_name": "Nuevo",
        "position": 0,
        "open_count": 9,
        "total_value": 19401030,
        "total_value_by_currency": {
          "CLP": 19399000
        },
        "is_mixed_currency": false,
        "median_time_in_stage_hours": 6.5
      }
    ]
  },
  "meta": {
    "window": {
      "kind": "rolling",
      "preset": "7d",
      "hours": 168,
      "from": "2026-09-15T16:38:51.000Z",
      "to": "2026-09-22T16:38:51.000Z",
      "label": "últimos 7d"
    }
  }
}

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

GET /insights/overview/heatmap

Activity heatmap by day and hour

The only report that accepts tz. Pass the viewer’s IANA zone (America/Santiago) and the day/hour buckets follow their wall clock; omit it and the grid is UTC, which shifts a Chilean workspace’s busy hours by three or four columns depending on DST. The name is validated by Postgres, so an unknown zone is a 500 rather than a 400.

ParámetroEnTipoObligatorioRestricciones
windowquery24h \7d \30d \
monthquerystringnopatrón `^\d4-(0[1-9]\
yearquerystringnopatrón ^\d{4}$
fromquerystringno
toquerystringno
tzquerystringnomáx. 64, patrón ^[A-Za-z0-9_+\-/]+$
curl https://api.vitrinadev.com/api/v1/insights/overview/heatmap \
  -H "Authorization: Bearer $VITRINA_KEY"

Ejemplo de respuesta (200)

{
  "data": {
    "buckets": [
      {
        "day": 1,
        "hour": 10,
        "conversations": 3
      },
      {
        "day": 1,
        "hour": 11,
        "conversations": 5
      }
    ]
  },
  "meta": {
    "window": {
      "kind": "rolling",
      "preset": "7d",
      "hours": 168,
      "from": "2026-09-15T16:38:51.000Z",
      "to": "2026-09-22T16:38:51.000Z",
      "label": "últimos 7d"
    }
  }
}

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

GET /insights/overview/live

Current live activity

A now-snapshot: open conversations, who is waiting, what is in flight. The one report that takes NO range — it describes the present, so from/to would mean nothing and are not accepted.

curl https://api.vitrinadev.com/api/v1/insights/overview/live \
  -H "Authorization: Bearer $VITRINA_KEY"

Ejemplo de respuesta (200)

{
  "data": {
    "open": 14,
    "unattended": 0,
    "unassigned": 1,
    "pending": 0,
    "agents_online": 1,
    "agents_busy": 0,
    "agents_offline": 4
  },
  "meta": {
    "window": {
      "kind": "instant",
      "at": "2026-09-22T16:38:43.071Z",
      "label": "ahora"
    }
  }
}

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

GET /insights/schedules

List report schedules

The standing subscriptions that mail a rendered report out on a cadence. Unpaginated, and with no meta.total — this list is small by construction.

curl https://api.vitrinadev.com/api/v1/insights/schedules \
  -H "Authorization: Bearer $VITRINA_KEY"

Ejemplo de respuesta (200)

{
  "data": [
    {
      "id": "cccccccc-2000-4000-8000-000000000001",
      "report_type": "conversations",
      "range_preset": "7d",
      "cadence": "weekly",
      "recipients": [
        "[email protected]"
      ],
      "next_run_at": "2026-09-29T09:00:00.000Z",
      "enabled": true
    }
  ]
}

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

GET /insights/sla

SLA compliance

Breach and compliance counts against the workspace SLA policies. A window with no policy configured reports nothing to comply with, not full compliance.

ParámetroEnTipoObligatorioRestricciones
windowquery24h \7d \30d \
monthquerystringnopatrón `^\d4-(0[1-9]\
yearquerystringnopatrón ^\d{4}$
fromquerystringno
toquerystringno
curl https://api.vitrinadev.com/api/v1/insights/sla \
  -H "Authorization: Bearer $VITRINA_KEY"

Ejemplo de respuesta (200)

{
  "data": {
    "has_policies": true,
    "policy_count": 2,
    "target_first_response_minutes": 15,
    "target_resolution_minutes": 480,
    "tracked": 18,
    "breached": 2,
    "hits": 16,
    "hit_rate": 0.89,
    "first_response": {
      "evaluated": 18,
      "breached": 1,
      "hit_rate": 0.94
    },
    "resolution": {
      "evaluated": 5,
      "breached": 1,
      "hit_rate": 0.8
    },
    "by_policy": [
      {
        "policy_id": "cccccccc-1000-4000-8000-000000000001",
        "name": "SLA estándar",
        "hit_rate": 0.89
      }
    ],
    "breaches": []
  },
  "meta": {
    "window": {
      "kind": "rolling",
      "preset": "7d",
      "hours": 168,
      "from": "2026-09-15T16:38:51.000Z",
      "to": "2026-09-22T16:38:51.000Z",
      "label": "últimos 7d"
    }
  }
}

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

GET /insights/sources

Volume by origin

The same row shape as /insights/channels, grouped by where the conversation CAME FROM (mercadolibre, yapo, chileautos, ai_agent, website, …) rather than which messaging account it rode in on. The two are not interchangeable: marketplace leads all arrive on the same WhatsApp number, so /insights/channels collapses every paid origin into one row. Unlike channels there is no catalogue to zero-fill from, so an origin with no traffic is absent rather than present with a zero; __none__ is the no-origin bucket.

ParámetroEnTipoObligatorioRestricciones
windowquery24h \7d \30d \
monthquerystringnopatrón `^\d4-(0[1-9]\
yearquerystringnopatrón ^\d{4}$
fromquerystringno
toquerystringno
curl https://api.vitrinadev.com/api/v1/insights/sources \
  -H "Authorization: Bearer $VITRINA_KEY"

Ejemplo de respuesta (200)

{
  "data": [
    {
      "key": "__none__",
      "label": "Directo / sin origen",
      "conversations": 14,
      "avg_first_response_seconds": 728.65,
      "avg_resolution_seconds": 9891.67,
      "avg_customer_wait_seconds": 728.65,
      "resolutions": 4
    },
    {
      "key": "website",
      "label": "Sitio web",
      "conversations": 4,
      "avg_first_response_seconds": 0,
      "avg_resolution_seconds": 0,
      "avg_customer_wait_seconds": 0,
      "resolutions": 0
    }
  ],
  "meta": {
    "window": {
      "kind": "rolling",
      "preset": "7d",
      "hours": 168,
      "from": "2026-09-15T16:38:51.000Z",
      "to": "2026-09-22T16:38:51.000Z",
      "label": "últimos 7d"
    }
  }
}

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

GET /insights/storefront

Storefront performance for a calendar month

The one ranged report that does NOT take the ?window= vocabulary: it is monthly by construction, so it takes ?month=YYYY-MM and nothing else, defaulting to the month in progress. The month is the DEALERSHIP’s (America/Santiago), and the resolved bounds come back in meta.window.

analytics:read buys the numbers, not the people. The recent_leads rows carry contact_name/contact_phone, which are the contact’s own — without contacts:read both keys are DELETED from each row (absent, never null: what is missing is the reader, not the data). The MCP twin applies the same projection, so neither surface is the loose one.

ParámetroEnTipoObligatorioRestricciones
monthquerystringnopatrón `^\d4-(0[1-9]\
curl https://api.vitrinadev.com/api/v1/insights/storefront \
  -H "Authorization: Bearer $VITRINA_KEY"

Ejemplo de respuesta (200)

{
  "data": {
    "storefront_visits": 240,
    "storefront_leads": 4,
    "conversion": {
      "value": 0.017,
      "numerator": {
        "name": "storefront_leads",
        "count": 4
      },
      "denominator": {
        "name": "storefront_visits",
        "count": 240
      }
    },
    "visits_series": [
      {
        "bucket": "2026-09-22",
        "value": 12
      }
    ],
    "leads_series": [
      {
        "bucket": "2026-09-22",
        "value": 1
      }
    ],
    "top_vehicles": [
      {
        "vehicle_id": "e1e1e1e1-0000-4000-8000-000000000001",
        "views": 30
      }
    ],
    "recent_leads": [
      {
        "lead_id": "99999999-0000-4000-8000-000000000001",
        "created_at": "2026-09-22T12:40:29.275Z",
        "vehicle_id": "e1e1e1e1-0000-4000-8000-000000000001",
        "vehicle_label": "Peugeot 2008 2024"
      }
    ],
    "window": {
      "kind": "calendar_month",
      "month": "2026-09",
      "from": "2026-09-01T04:00:00.000Z",
      "to": "2026-10-01T03:00:00.000Z",
      "tz": "America/Santiago",
      "label": "septiembre 2026",
      "as_of": "2026-09-22T16:38:51.570Z"
    }
  },
  "meta": {
    "window": {
      "kind": "calendar_month",
      "month": "2026-09",
      "from": "2026-09-01T04:00:00.000Z",
      "to": "2026-10-01T03:00:00.000Z",
      "tz": "America/Santiago",
      "label": "septiembre 2026",
      "as_of": "2026-09-22T16:38:51.570Z"
    }
  }
}

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

GET /insights/storefront/card

Storefront summary for the CRM dashboard "Mi sitio" card

A compact, ROLLING sibling of GET /insights/storefront (ticket 86e2rwqxw): a FIXED trailing 30-day period, plus the immediately-preceding 30-day period for a period-over-period comparison. Takes no query parameters — the window is not caller-chosen, unlike the calendar-month report. Returns visits/leads as \{current, previous\} raw counts (never a pre-computed delta), a zero-filled visits_series sparkline, and the top 5 (not 10) most-viewed vehicles. Carries NO personal data — no recent_leads, no contact fields — so unlike the full report this endpoint needs no contacts:read branch. Site status (published/draft, the dealer’s own domain) is deliberately not here: GET /tenant/sitio and GET /tenant/settings already serve it.

curl https://api.vitrinadev.com/api/v1/insights/storefront/card \
  -H "Authorization: Bearer $VITRINA_KEY"

Ejemplo de respuesta (200)

{
  "data": {
    "period": {
      "from": "2026-08-23T16:38:51.606Z",
      "to": "2026-09-22T16:38:51.606Z",
      "label": "últimos 30d"
    },
    "previous_period": {
      "from": "2026-07-24T16:38:51.606Z",
      "to": "2026-08-23T16:38:51.606Z",
      "label": "período anterior"
    },
    "visits": {
      "current": 240,
      "previous": 180
    },
    "leads": {
      "current": 4,
      "previous": 2
    },
    "visits_series": [
      {
        "bucket": "2026-09-22",
        "value": 12
      }
    ],
    "top_vehicles": [
      {
        "vehicle_id": "e1e1e1e1-0000-4000-8000-000000000001",
        "views": 30
      }
    ],
    "as_of": "2026-09-22T16:38:51.606Z"
  },
  "meta": {
    "period": {
      "from": "2026-08-23T16:38:51.606Z",
      "to": "2026-09-22T16:38:51.606Z",
      "label": "últimos 30d"
    }
  }
}

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

GET /insights/tags

Volume by tag

Conversation counts grouped by the tags applied to them.

ParámetroEnTipoObligatorioRestricciones
windowquery24h \7d \30d \
monthquerystringnopatrón `^\d4-(0[1-9]\
yearquerystringnopatrón ^\d{4}$
fromquerystringno
toquerystringno
curl https://api.vitrinadev.com/api/v1/insights/tags \
  -H "Authorization: Bearer $VITRINA_KEY"

Ejemplo de respuesta (200)

{
  "data": [
    {
      "key": "__none__",
      "label": "Sin etiqueta",
      "conversations": 17,
      "avg_first_response_seconds": 728.65,
      "avg_resolution_seconds": 9891.67,
      "avg_customer_wait_seconds": 728.65,
      "resolutions": 4
    },
    {
      "key": "e1e1e1e1-9000-4000-8000-000000000001",
      "label": "garantia",
      "conversations": 1,
      "avg_first_response_seconds": 0,
      "avg_resolution_seconds": 0,
      "avg_customer_wait_seconds": 0,
      "resolutions": 0
    }
  ],
  "meta": {
    "window": {
      "kind": "rolling",
      "preset": "7d",
      "hours": 168,
      "from": "2026-09-15T16:38:51.000Z",
      "to": "2026-09-22T16:38:51.000Z",
      "label": "últimos 7d"
    }
  }
}

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

GET /insights/teams

Volume by team

Conversation counts grouped by the team they were routed to.

ParámetroEnTipoObligatorioRestricciones
windowquery24h \7d \30d \
monthquerystringnopatrón `^\d4-(0[1-9]\
yearquerystringnopatrón ^\d{4}$
fromquerystringno
toquerystringno
curl https://api.vitrinadev.com/api/v1/insights/teams \
  -H "Authorization: Bearer $VITRINA_KEY"

Ejemplo de respuesta (200)

{
  "data": [
    {
      "key": "__none__",
      "label": "Sin equipo",
      "conversations": 18,
      "avg_first_response_seconds": 728.65,
      "avg_resolution_seconds": 9891.67,
      "avg_customer_wait_seconds": 728.65,
      "resolutions": 4
    },
    {
      "key": "cccccccc-0000-4000-8000-000000000001",
      "label": "Marketing",
      "conversations": 0,
      "avg_first_response_seconds": 0,
      "avg_resolution_seconds": 0,
      "avg_customer_wait_seconds": 0,
      "resolutions": 0
    }
  ],
  "meta": {
    "window": {
      "kind": "rolling",
      "preset": "7d",
      "hours": 168,
      "from": "2026-09-15T16:38:51.000Z",
      "to": "2026-09-22T16:38:51.000Z",
      "label": "últimos 7d"
    }
  }
}

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

En esta página