Rastreamento de Requisições e Custos
O SipPulse AI fornece um sistema completo de rastreamento de requisições que permite monitorar custos, performance e uso de cada chamada de API individualmente. Isso é especialmente útil para:
- Controle de custos por cliente ou projeto
- Relatórios de uso detalhados
- Repasse de custos para clientes finais
O que é o x-request-id?
Toda requisição de consumo (TTS, ASR, LLM, etc.) retorna um header HTTP chamado x-request-id. Este identificador único permite rastrear os custos e métricas específicas daquela requisição.
Formato do ID
O ID segue o padrão: req_ + 32 caracteres alfanuméricos
Exemplo: req_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6
Rotas que Retornam x-request-id
| Rota | Método | Tipo de Resposta | Descrição |
|---|---|---|---|
/audio/speech | POST | Streaming | Text-to-Speech (compatível OpenAI) |
/tts/generate | POST | JSON | Text-to-Speech |
/asr/transcribe | POST | JSON | Speech-to-Text |
/asr/transcribe/batch | POST | JSON | Speech-to-Text (batch assíncrono). Também aceita x-request-id como input (deve ser um UUID válido). |
/audio/transcriptions | POST | JSON | Speech-to-Text (compatível OpenAI) |
/llms/completion | POST | Streaming | Geração de texto LLM |
/chat/completions | POST | Streaming | Chat LLM (compatível OpenAI) |
/text-inteligence | POST | JSON | Análise de texto |
/anonymize | POST | JSON | Anonimização de texto |
Como Extrair o x-request-id
const response = await fetch('https://api.sippulse.ai/tts/generate', {
method: 'POST',
headers: {
'Authorization': 'Bearer seu_token',
'Content-Type': 'application/json'
},
body: JSON.stringify({
model: 'openai-tts-1',
input: 'Olá, como posso ajudar?',
voice: 'alloy'
})
});
// Extrair o request ID do header
const requestId = response.headers.get('x-request-id');
console.log('Request ID:', requestId);
const data = await response.json();import requests
response = requests.post(
'https://api.sippulse.ai/tts/generate',
headers={
'Authorization': 'Bearer seu_token',
'Content-Type': 'application/json'
},
json={
'model': 'openai-tts-1',
'input': 'Olá, como posso ajudar?',
'voice': 'alloy'
}
)
# Extrair o request ID do header
request_id = response.headers.get('x-request-id')
print(f'Request ID: {request_id}')
data = response.json()curl -i -X POST https://api.sippulse.ai/tts/generate \
-H "Authorization: Bearer seu_token" \
-H "Content-Type: application/json" \
-d '{
"model": "openai-tts-1",
"input": "Olá, como posso ajudar?",
"voice": "alloy"
}'
# O header x-request-id aparecerá na respostaConsultando Detalhes de Uso
Com o request_id em mãos, você pode consultar os detalhes completos de custo e performance:
GET https://api.sippulse.ai/usage-requests/{request_id}Processamento Assíncrono
O cálculo de custos e a cobrança são processados de forma assíncrona. Isso significa que imediatamente após receber o x-request-id, os detalhes de uso podem ainda não estar disponíveis (tipicamente um delay de alguns milissegundos).
Para acesso programático, considere implementar um pequeno delay ou lógica de retry para garantir que você obtenha os dados corretamente.
Exemplo de Implementação com Retry
Ao consultar detalhes de uso de forma programática, é recomendado implementar um mecanismo de retry:
async function getUsageWithRetry(requestId, maxRetries = 3, delayMs = 100) {
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
const response = await fetch(
`https://api.sippulse.ai/usage-requests/${requestId}`,
{ headers: { 'Authorization': 'Bearer seu_token' } }
);
if (response.ok) {
const data = await response.json();
// Verifica se os dados de custo estão disponíveis
if (data.total_price_local !== undefined) {
return data;
}
}
// Aguarda antes de tentar novamente (backoff exponencial)
if (attempt < maxRetries) {
await new Promise(resolve => setTimeout(resolve, delayMs * attempt));
}
} catch (error) {
if (attempt === maxRetries) throw error;
}
}
throw new Error(`Falha ao obter uso após ${maxRetries} tentativas`);
}
// Uso
const usage = await getUsageWithRetry('req_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6');
console.log('Custo:', usage.total_price_local);import time
import requests
def get_usage_with_retry(request_id, max_retries=3, delay_ms=100):
for attempt in range(1, max_retries + 1):
try:
response = requests.get(
f'https://api.sippulse.ai/usage-requests/{request_id}',
headers={'Authorization': 'Bearer seu_token'}
)
if response.ok:
data = response.json()
# Verifica se os dados de custo estão disponíveis
if data.get('total_price_local') is not None:
return data
# Aguarda antes de tentar novamente (backoff exponencial)
if attempt < max_retries:
time.sleep((delay_ms * attempt) / 1000)
except Exception as e:
if attempt == max_retries:
raise e
raise Exception(f'Falha ao obter uso após {max_retries} tentativas')
# Uso
usage = get_usage_with_retry('req_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6')
print(f"Custo: {usage['total_price_local']}")Exemplo de Requisição
curl -X GET https://api.sippulse.ai/usage-requests/req_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6 \
-H "Authorization: Bearer seu_token"Exemplo de Resposta
{
"id": "req_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6",
"organization_id": "org_xxxx",
"user_id": "usr_yyyy",
"agent_id": "agt_zzzz",
"thread_id": "thr_wwww",
"project_id": null,
"auth_mode": "jwt",
"created_at": "2026-01-07T10:30:45.000Z",
"event_timestamp": "2026-01-07T10:30:45.000Z",
"updated_at": "2026-01-07T10:30:47.000Z",
"execution_time_ms": 2350,
"processing_time_ms": 50,
"total_price_local": 0.0025,
"speed": 10.21,
"items": [
{
"charge_subtype": "text-to-speech",
"pricing_rule": "tts_char",
"identifier": "openai-tts-1",
"quantity": 24,
"unit": "character",
"billed_quantity": 24,
"billed_unit": "character",
"price_amount": 0.0024,
"currency": "BRL",
"event_timestamp": "2026-01-07T10:30:45.000Z"
}
]
}Estrutura da Resposta
Campos Principais
| Campo | Tipo | Descrição |
|---|---|---|
id | string | ID único da requisição |
organization_id | string | ID da organização |
user_id | string | ID do usuário que fez a requisição |
agent_id | string | ID do agente (se aplicável) |
thread_id | string | ID da thread de conversa (se aplicável) |
auth_mode | string | Tipo de autenticação: jwt, api_key, guest_token, oauth |
execution_time_ms | number | Tempo de execução em milissegundos |
processing_time_ms | number | Tempo de processamento/setup em milissegundos |
total_price_local | number | Custo total na moeda local (BRL) |
speed | number | Throughput (tokens/s, caracteres/s, etc.) |
Campos de Items
Cada item representa um componente cobrado da requisição:
| Campo | Tipo | Descrição |
|---|---|---|
charge_subtype | string | Tipo de uso, como text-to-speech, speech-to-text, text-generation ou agent |
pricing_rule | string | Regra de cobrança aplicada, como tts_char ou voice |
identifier | string | Identificador do modelo, recurso ou agente usado. Itens de voz trazem o ID do agente |
quantity | number | Quantidade medida, na unidade unit |
unit | string | Unidade de quantity: token, character, second, etc. |
billed_quantity | number ou null | Quantidade pela qual o preço foi multiplicado, na unidade billed_unit. Null para consumo registrado antes de existir a quantidade faturada |
billed_unit | string ou null | Unidade de billed_quantity, como minute para sessões de agente de voz. Null nas mesmas condições de billed_quantity |
price_amount | number | Preço de venda do item, na moeda currency |
currency | string | Moeda de price_amount |
event_timestamp | string | Momento do evento em ISO-8601 |
Quantidade faturada e arredondamento explica como o billed_quantity é arredondado por produto.
Campos de Uso (LLM)
Nas respostas de /v1/llms/completion e na metadata das mensagens de thread, o objeto usage pode trazer:
| Campo | Tipo | Descrição |
|---|---|---|
input_tokens_details | object | Cache de entrada, presente apenas quando o provedor reporta. Pode ter cached_tokens e/ou cache_write_tokens |
output_tokens_details | object | Tokens de raciocínio, presente apenas quando o provedor reporta reasoning_tokens |
A plataforma nunca inventa esses números: o campo aparece só quando o provedor reporta. Em cache, um zero reportado é preservado e aparece como 0. input_tokens continua contando os tokens cacheados por inteiro. Em items[].performance, is_cached significa que o provedor reportou leituras de cache nesta requisição, não é um cálculo da plataforma.
Sessões de Voz Cobradas
GET /usage/sessions lista as sessões de voz cobradas: uma linha por sessão de voz (tempo de voz do agente), da mais recente para a mais antiga, com a duração medida, os minutos cobrados e o preço de venda. Use para conciliar a cobrança de voz com os seus registros, sem consultar cada requisição.
Autenticação: uma chave API com a permissão billing:read. Uma chave API restrita a um projeto, ou o header X-Project-ID, limita os resultados a esse projeto.
Parâmetros da Consulta
| Parâmetro | Obrigatório | Descrição |
|---|---|---|
from | Sim | Primeiro dia local, YYYY-MM-DD, inclusive, no tz |
to | Sim | Último dia local, YYYY-MM-DD, inclusive, no tz. Deve ser igual ou posterior a from |
tz | Não | Fuso horário IANA em que os dias são lidos. Padrão UTC |
agent_id | Não | Apenas sessões deste agente |
cursor | Não | next_cursor da página anterior |
limit | Não | Tamanho da página, de 1 a 1000. Padrão 100 |
Uma sessão pertence ao período do seu horário de término (ended_at). Uma sessão que começa em um dia e termina no seguinte conta para o dia em que termina.
Exemplo de Requisição
curl -X GET "https://api.sippulse.ai/usage/sessions?from=2026-09-01&to=2026-09-30&tz=America/Sao_Paulo&limit=100" \
-H "Authorization: Bearer sua_chave_api"Exemplo de Resposta
{
"data": [
{
"request_id": "vce_a1b2c3d4e5f6g7h8",
"thread_id": "thr_wwww",
"room_name": "room_xxxx",
"agent_id": "agt_zzzz",
"project_id": "prj_qqqq",
"ended_at": "2026-09-15T17:22:01.000Z",
"duration_seconds": 61,
"billed_quantity": 2,
"billed_unit": "minute",
"price": 0.0480,
"currency": "BRL"
}
],
"has_more": true,
"next_cursor": "eyJ0IjoiMjAyNi0wOS0xNVQxNzoyMjowMS4wMDBaIiwiaWQiOiJ2Y2VfYTFiMmMzZDRlNWY2ZzdoOCJ9"
}Paginação
Os resultados vêm ordenados pelo horário de término, do mais recente para o mais antigo. Para ler a próxima página, envie o valor de next_cursor como cursor e repita até has_more ser false. O cursor é opaco: devolva-o sem alterações e não monte nem edite o valor.
Campos da Resposta
| Campo | Tipo | Descrição |
|---|---|---|
request_id | string | ID da sessão, com prefixo vce_ |
thread_id | string ou null | Thread de conversa |
room_name | string ou null | Nome da sala de voz |
agent_id | string ou null | Agente que atendeu a sessão |
project_id | string ou null | Projeto da sessão |
ended_at | string | Fim da sessão em ISO-8601 |
duration_seconds | number | Duração medida da sessão, em segundos |
billed_quantity | number ou null | Quantidade cobrada, na unidade billed_unit. Null para sessões cobradas antes de existir a quantidade faturada |
billed_unit | string ou null | Unidade de billed_quantity: minute para sessões de voz. Null nas mesmas condições de billed_quantity |
price | number | Preço de venda do tempo de voz da sessão, na moeda currency |
currency | string | Moeda de price |
Sessões cobradas antes de existir a quantidade faturada retornam billed_quantity e billed_unit como null. O price continua sendo retornado.
Erros
O endpoint retorna 400 quando from ou to não é uma data YYYY-MM-DD válida, quando to é anterior a from, quando tz não é um nome de fuso IANA ou quando cursor não foi devolvido por este endpoint.
Casos de Uso
1. Repasse de Custos para Clientes
Armazene o request_id junto com o identificador do seu cliente:
async function processRequest(clientId, text) {
const response = await ttsGenerate(text);
const requestId = response.headers.get('x-request-id');
// Salvar no banco de dados
await db.usageLog.create({
client_id: clientId,
request_id: requestId,
timestamp: new Date()
});
return response;
}
// Depois, para gerar relatório de custos:
async function generateClientReport(clientId) {
const logs = await db.usageLog.findByClientId(clientId);
let totalCost = 0;
for (const log of logs) {
const usage = await fetch(`/usage-requests/${log.request_id}`);
totalCost += usage.total_price_local;
}
return totalCost;
}2. Monitoramento de Performance
Acompanhe métricas de performance:
const response = await asrTranscribe(audioFile);
const requestId = response.headers.get('x-request-id');
const usage = await getUsageRequest(requestId);
const metrics = {
executionTime: usage.execution_time_ms,
throughput: usage.speed,
cost: usage.total_price_local
};
// Enviar para sistema de monitoramento
monitoring.track('asr_performance', metrics);Boas Práticas
- Sempre armazene o request_id para requisições importantes que você pode precisar auditar
- Não exponha request_ids para usuários finais, pois eles permitem acesso a informações de custo
- Implemente rate limiting baseado nos custos consultados
- Configure alertas para requisições com custo acima do esperado
Próximos Passos
- API REST - Documentação completa da API
- Dashboard de Consumo - Visualização agregada de custos
- Créditos e Faturamento - Gerenciamento de créditos
