Skip to content

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 ​

RotaMétodoTipo de RespostaDescrição
/audio/speechPOSTStreamingText-to-Speech (compatível OpenAI)
/tts/generatePOSTJSONText-to-Speech
/asr/transcribePOSTJSONSpeech-to-Text
/asr/transcribe/batchPOSTJSONSpeech-to-Text (batch assíncrono). Também aceita x-request-id como input (deve ser um UUID válido).
/audio/transcriptionsPOSTJSONSpeech-to-Text (compatível OpenAI)
/llms/completionPOSTStreamingGeração de texto LLM
/chat/completionsPOSTStreamingChat LLM (compatível OpenAI)
/text-inteligencePOSTJSONAnálise de texto
/anonymizePOSTJSONAnonimização de texto

Como Extrair o x-request-id ​

javascript
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();
python
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()
bash
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 resposta

Consultando 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:

javascript
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);
python
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 ​

bash
curl -X GET https://api.sippulse.ai/usage-requests/req_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6 \
  -H "Authorization: Bearer seu_token"

Exemplo de Resposta ​

json
{
  "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 ​

CampoTipoDescrição
idstringID único da requisição
organization_idstringID da organização
user_idstringID do usuário que fez a requisição
agent_idstringID do agente (se aplicável)
thread_idstringID da thread de conversa (se aplicável)
auth_modestringTipo de autenticação: jwt, api_key, guest_token, oauth
execution_time_msnumberTempo de execução em milissegundos
processing_time_msnumberTempo de processamento/setup em milissegundos
total_price_localnumberCusto total na moeda local (BRL)
speednumberThroughput (tokens/s, caracteres/s, etc.)

Campos de Items ​

Cada item representa um componente cobrado da requisição:

CampoTipoDescrição
charge_subtypestringTipo de uso, como text-to-speech, speech-to-text, text-generation ou agent
pricing_rulestringRegra de cobrança aplicada, como tts_char ou voice
identifierstringIdentificador do modelo, recurso ou agente usado. Itens de voz trazem o ID do agente
quantitynumberQuantidade medida, na unidade unit
unitstringUnidade de quantity: token, character, second, etc.
billed_quantitynumber ou nullQuantidade pela qual o preço foi multiplicado, na unidade billed_unit. Null para consumo registrado antes de existir a quantidade faturada
billed_unitstring ou nullUnidade de billed_quantity, como minute para sessões de agente de voz. Null nas mesmas condições de billed_quantity
price_amountnumberPreço de venda do item, na moeda currency
currencystringMoeda de price_amount
event_timestampstringMomento 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:

CampoTipoDescrição
input_tokens_detailsobjectCache de entrada, presente apenas quando o provedor reporta. Pode ter cached_tokens e/ou cache_write_tokens
output_tokens_detailsobjectTokens 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âmetroObrigatórioDescrição
fromSimPrimeiro dia local, YYYY-MM-DD, inclusive, no tz
toSimÚltimo dia local, YYYY-MM-DD, inclusive, no tz. Deve ser igual ou posterior a from
tzNãoFuso horário IANA em que os dias são lidos. Padrão UTC
agent_idNãoApenas sessões deste agente
cursorNãonext_cursor da página anterior
limitNãoTamanho 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 ​

bash
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 ​

json
{
  "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 ​

CampoTipoDescrição
request_idstringID da sessão, com prefixo vce_
thread_idstring ou nullThread de conversa
room_namestring ou nullNome da sala de voz
agent_idstring ou nullAgente que atendeu a sessão
project_idstring ou nullProjeto da sessão
ended_atstringFim da sessão em ISO-8601
duration_secondsnumberDuração medida da sessão, em segundos
billed_quantitynumber ou nullQuantidade cobrada, na unidade billed_unit. Null para sessões cobradas antes de existir a quantidade faturada
billed_unitstring ou nullUnidade de billed_quantity: minute para sessões de voz. Null nas mesmas condições de billed_quantity
pricenumberPreço de venda do tempo de voz da sessão, na moeda currency
currencystringMoeda 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:

javascript
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:

javascript
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 ​

  1. Sempre armazene o request_id para requisições importantes que você pode precisar auditar
  2. Não exponha request_ids para usuários finais, pois eles permitem acesso a informações de custo
  3. Implemente rate limiting baseado nos custos consultados
  4. Configure alertas para requisições com custo acima do esperado

Próximos Passos ​