Análises de Uso
Análises de Uso é um catálogo mais uma consulta declarativa para agentes e scripts. O chamador informa um conjunto de dados, medidas, dimensões, uma janela de tempo e filtros, todos chaves de um catálogo no servidor. O servidor resolve o escopo do chamador, escolhe a tabela com o grão certo e devolve uma tabela compacta. Não existe entrada SQL de nenhum tipo: chaves desconhecidas são recusadas antes de qualquer acesso ao banco.
Comece pelo catálogo e depois consulte. O catálogo mostra apenas o que a credencial atual pode ver:
spai analytics catalog
spai analytics catalog --dataset usageConjuntos de dados
usage: consumo cobrado
Consumo cobrado da organização, sempre em preço de venda na moeda da organização (veja Preço é preço de venda).
| Medida | O que conta |
|---|---|
price_local | Valor cobrado em moeda local. Exige billing:read. |
quantity | Quantidade consumida na unidade do item. Exige uma única unidade por linha (veja abaixo). |
billed_quantity | Quantidade pela qual o preço foi multiplicado, na unidade billed_unit. Exige uma única unidade cobrada por linha (veja Quantidade faturada e arredondamento). |
items | Linhas de cobrança. |
requests | Requisições cobradas. Uma requisição pode usar vários modelos, então não pode ser agrupada por modelo. |
ttfb_p50_ms, ttfb_p95_ms | Tempo até o primeiro byte, mediana e p95. Só dado bruto, máximo de 31 dias. |
Dimensões: project, user, agent, model, charge_subtype, unit, pricing_rule, billed_unit. billed_unit existe só no dado bruto de itens, então uma consulta que a usa fica limitada a 31 dias.
As fontes são escolhidas automaticamente. Rollups diários cobrem até 400 dias; a tabela bruta de itens cobre 31 dias e sustenta as medidas de latência. Dias fechados vêm do rollup e o dia de hoje vem das tabelas transacionais com a mesma agregação.
quantity mistura unidades (tokens, caracteres, segundos), então cada linha precisa resolver para uma unidade: adicione unit ou charge_subtype às dimensões, ou filtre uma única unidade. Caso contrário a consulta é recusada com mixed_units e uma correção pronta. billed_quantity segue a mesma regra com billed_unit, unit ou charge_subtype.
charge_subtype aceita os tipos de uso gravados em cada item, como agent, speech-to-text e text-generation. O catálogo lista os valores que o token pode ver, e pricing_rule aceita as regras de preço do catálogo (por exemplo voice). Nomes legados como input_token continuam aceitos como filtro.
conversations: conversas por agente, canal e contato
Conversas (threads excluídas ficam de fora) com escalação para humano e custo cobrado.
| Medida | O que conta |
|---|---|
conversations | Conversas iniciadas. |
escalated | Conversas com escalação para humano. |
escalation_rate | Escaladas divididas pelas conversas. |
price_local | Valor cobrado por trás das conversas. Exige billing:read. |
price_per_conversation | Valor cobrado dividido pelas conversas. Exige billing:read. |
Dimensões: project, user, agent, channel, hour_of_day, contact.
A janela cobre até 92 dias. hour_of_day segue o fuso horário pedido; contact sai mascarado salvo se o chamador puder ler conversas (veja abaixo).
analysis: campos da análise pós-atendimento
Campos de uma análise estruturada viram entradas do catálogo tipadas pelo JSON schema dela: uma string com enum vira dimensão, um booleano vira dimensão mais uma medida .rate (fração de respostas verdadeiras), e um número vira medidas .avg, .min, .max e .count. Texto livre, arrays e objetos nunca são expostos. A consulta precisa referenciar exatamente um id de análise estruturada nas dimensões, medidas e filtros analysis.*.
Dimensões: project, agent, channel, mais uma entrada por campo exposto, chamada analysis.<id_da_analise_estruturada>.<campo>. A janela cobre até 183 dias.
Quantidade faturada e arredondamento
billed_quantity é a quantidade pela qual o preço foi multiplicado, então pode diferir da quantity medida. O arredondamento depende do produto:
- Agente de voz (
charge_subtypeagent, regra de preçovoice): cobrado por sessão em blocos de 60 segundos, arredondados para cima, com mínimo de 1 segundo. Uma sessão de 1 segundo cobra 1 minuto, 61 segundos cobra 2 minutos e 95 segundos cobra 2 minutos.billed_unitéminute. - Demais produtos: seguem a regra de preço do modelo ou serviço usado. Uma regra cobrada em blocos informa os blocos cobrados (por exemplo, um modelo de transcrição cobrado por minuto iniciado informa
minute); qualquer outra regra informa a quantidade cobrada, entãobilled_quantityé igual aquantityebilled_unité igual aunit(token,character,second, ...).
Leia sempre billed_unit junto com billed_quantity: é a unidade sobre a qual o preço foi aplicado.
O consumo registrado antes de existir a quantidade faturada não é preenchido retroativamente, então períodos anteriores a essa mudança mostram um billed_quantity menor ou vazio. Use quantity para esses períodos.
Janelas de tempo e limites
{
"dataset": "usage",
"measures": ["price_local"],
"dimensions": ["model"],
"time": { "range": "last_56d", "granularity": "week", "tz": "America/Sao_Paulo" },
"filters": [{ "dim": "charge_subtype", "op": "in", "values": ["input_token", "output_token"] }],
"limit": 200
}range:last_<N>dcom N de 1 a 400,this_month,last_month, ou um{ "from": "YYYY-MM-DD", "to": "YYYY-MM-DD" }explícito. Padrãolast_30d. As janelas são dias UTC inteiros, exceto no caminho detzexato abaixo, onde os dias são dias locais notz.toé limitado ao dia de hoje.granularity:none,day,weekoumonth. Padrãonone. A colunaperiodtraz a data de início do intervalo emYYYY-MM-DD(a semana começa na segunda-feira, o mês no dia 1). Semorder, as linhas saem por período crescente e depois pela primeira medida decrescente.tz: um nome IANA, padrãoUTC. Ele muda o agrupamento das fontes brutas e a dimensãohour_of_day. Parausagecom fuso diferente de UTC:- Até 31 dias (uma janela de
compareconta em dobro), com todas as medidas, dimensões e filtros escolhidos servidos pelos itens brutos: os limites de dia e de mês são calculados no fuso, com horário de verão respeitado em cada limite, e nenhum avisorollup_utc_daysé devolvido. Os itens brutos servemprice_local,quantity,billed_quantity,items,ttfb_p50_ms,ttfb_p95_ms,project,user,model,charge_subtype,unit,pricing_ruleebilled_unit. - Janelas maiores, ou uma consulta que usa
agentourequests: os rollups agrupam por dia UTC, e uma granularidade de tempo soma um avisorollup_utc_days.
- Até 31 dias (uma janela de
- No máximo 6 medidas, 3 dimensões e 10 filtros por consulta. Chaves desconhecidas são recusadas.
- No máximo 5000 linhas; linhas extras marcam
truncated: truecom um avisotruncated. O limite padrão é 200. - Toda consulta roda sob um teto de 5 segundos.
- Janela máxima por seleção: 400 dias nos rollups, 92 dias nas conversas, 183 dias na análise, 31 dias no dado bruto de itens.
compare: "previous_period" roda a mesma consulta na janela anterior e junta as linhas pelas dimensões. Funciona só com granularity: "none". Para uma série temporal, amplie a janela em vez de comparar.
Dimensões de entidade (project, agent, user) agrupam por id e emitem duas colunas: <dim> com o nome de exibição e <dim>_id com o id.
As respostas trazem meta.warnings com códigos como partial_last_period (a janela termina hoje e o último intervalo está incompleto), rollup_utc_days, contact_masked e truncated.
Mascaramento de contato
contact agrupa pelo identificador completo mas imprime mascarado, salvo se o chamador tiver acesso de leitura às conversas (no OAuth, também o escopo spai:threads:read). Filtrar por contact sem esse acesso é recusado com forbidden_dimension em vez de devolver linhas mascaradas.
Preço é preço de venda
price_local é sempre o preço de venda na moeda da organização. Custo nunca está disponível: nenhuma medida, dimensão ou filtro expõe custo, margem ou tabelas de tarifa. Pedir uma medida parecida com custo responde unknown_measure e aponta para price_local; nunca confirma se tal coluna existe.
Erros
| Código | Status | Quando |
|---|---|---|
analytics_invalid_query | 400 | Forma ruim ou chave desconhecida. details.reason nomeia a causa: unknown_dataset, unknown_measure, unknown_dimension, invalid_filter, invalid_filter_value, invalid_granularity, invalid_order, invalid_limit, too_many_measures, too_many_dimensions, incompatible_selection, mixed_units, range_too_large ou invalid_query. details também traz available, allowed ou fix quando útil. |
analytics_forbidden | 403 | forbidden_measure (medida de gasto sem billing:read) ou forbidden_dimension (contato sem leitura de conversas). |
analytics_query_timeout | 422 | O teto de 5 segundos foi atingido. Reduza a janela ou tire dimensões. |
analytics_busy | 429 | Mais de 2 consultas de análises já rodam para a organização. Aguarde e tente de novo. |
analytics_unavailable | 503 | A fonte de dados de análises não está configurada. |
Um valor fora do escopo do chamador (id ou nome de projeto, agente, usuário ou contato de outro projeto ou organização, ou que não existe) sempre responde invalid_filter_value com allowed listando os valores do escopo do chamador, de modo que entidades fora do escopo são indistinguíveis das inexistentes.
Escopo OAuth
spai:analytics:read é um escopo de leitura para os recursos de API e MCP. Ele mapeia para dashboard:read, nunca cobra, e está incluído no login padrão da CLI, no login somente leitura e nos escopos anunciados do MCP. Concessões existentes não ganham o escopo no refresh: entre de novo para autorizá-lo. Veja Autenticar com OAuth.
Ferramentas MCP
| Ferramenta | O que faz | Escopo |
|---|---|---|
analytics_catalog | Lista conjuntos, medidas, dimensões, valores permitidos e limites para o token | spai:analytics:read |
analytics_query | Roda uma consulta declarativa. Nunca aceita SQL. | spai:analytics:read |
As duas ferramentas são somente leitura. Uma consulta com falha devolve um erro de ferramenta com code, message e details estruturados (por exemplo analytics_invalid_query com reason e fix), para o agente se corrigir em vez de adivinhar.
Exemplos de CLI
Comandos de análises exigem spai:analytics:read. Uma sessão sem ele falha antes de qualquer requisição com a correção:
This command needs spai:analytics:read. Run: spai auth loginCom uma chave API não há verificação local de escopo.
Gasto semanal de LLM por modelo, renderizado como spec de gráfico:
spai analytics query usage \
--measure price_local \
--dimension model \
--range last_56d \
--granularity week \
--tz America/Sao_Paulo \
--filter 'charge_subtype in input_token,output_token' \
--vega > spend.vl.jsonCusto por conversa por agente, comparado com os 30 dias anteriores:
spai analytics query conversations \
--measure price_per_conversation,conversations \
--dimension agent \
--range last_30d \
--compare previous_periodEscalação por hora do dia, para dimensionar a equipe humana:
spai analytics query conversations \
--measure escalated,escalation_rate \
--dimension hour_of_day \
--range last_30d \
--tz America/Sao_Paulo \
--order hour_of_day:ascTaxa de resolução e CSAT médio por motivo de contato, enviados de um arquivo em CSV:
spai analytics query --file query.json --format csvcom query.json guardando a forma exata da consulta:
{
"dataset": "analysis",
"measures": ["analysis.support_review.resolved.rate", "analysis.support_review.csat.avg", "conversations"],
"dimensions": ["analysis.support_review.reason"],
"time": { "range": "last_30d" },
"order": [{ "by": "conversations", "dir": "desc" }]
}support_review é um exemplo de id de análise estruturada vindo do catálogo: chame GET /v1/analytics/catalog (ou spai analytics catalog --dataset analysis) para listar os ids visíveis para o token, e use exatamente um id nas dimensões, medidas e filtros.
--file é mutuamente exclusivo com as flags de construção da consulta. Em pipes o formato padrão é JSON; --format table|json|csv escolhe a saída, e --vega imprime uma spec Vega-Lite v5 com os dados embutidos. Erros de uso saem com código 2 e erros do servidor imprimem código, mensagem e detalhes no stderr.
Exemplo REST
curl -X POST "https://api.sippulse.ai/v1/analytics/query" \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"dataset": "conversations",
"measures": ["escalation_rate", "conversations"],
"dimensions": ["channel"],
"time": { "range": "last_30d" },
"order": [{ "by": "escalation_rate", "dir": "desc" }]
}'Chame GET /v1/analytics/catalog antes para listar os conjuntos, medidas, dimensões e limites visíveis para o token. As respostas usam Cache-Control: no-store.
Minutos cobrados por projeto em um mês
Minutos de agente e minutos de transcrição por projeto em setembro de 2026, no horário de São Paulo. Cada linha traz uma única unidade, então a consulta agrupa por billed_unit: as linhas de agente vêm em minute, e as linhas de transcrição vêm na unidade da regra de preço do modelo de transcrição (minute para regras por minuto, second para regras por segundo).
curl -X POST "https://api.sippulse.ai/v1/analytics/query" \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"dataset": "usage",
"measures": ["billed_quantity"],
"dimensions": ["project", "charge_subtype", "billed_unit"],
"filters": [{ "dim": "charge_subtype", "op": "in", "values": ["agent", "speech-to-text"] }],
"time": { "range": { "from": "2026-09-01", "to": "2026-09-30" }, "tz": "America/Sao_Paulo" },
"order": [{ "by": "billed_quantity", "dir": "desc" }]
}'A janela tem 30 dias, então a consulta lê os itens brutos com limites de dia em America/Sao_Paulo e não devolve o aviso rollup_utc_days.
