Onvox AI Docs

Exemplos

Exemplos completos de código em JavaScript, Python e cURL para casos de uso comuns da API Onvox AI.

Exemplos de Código

Todos os exemplos desta página usam os 4 únicos endpoints que aceitam autenticação por X-API-Key: POST /api/analyses, GET /api/salespersons, GET /api/analyses e GET /api/analyses/{id}. Detalhe completo de cada um em Endpoints. Operações como criar usuário ou consultar saldo de créditos não têm endpoint de API Key hoje; o acesso é feito apenas pelo painel web (sessão de navegador).

Criar Análise a partir de um arquivo local (base64)

Quando o áudio está no seu disco (não numa URL pública), codifique em base64 e envie como Data URL no campo audioKey:

const API_KEY = 'evai_your_key_here';
const BASE_URL = 'https://api.evolu-ai.com/api';

async function criarAnaliseDeArquivoLocal(caminhoArquivo, salespersonId) {
  const fs = require('node:fs');
  const buffer = fs.readFileSync(caminhoArquivo);
  const base64 = buffer.toString('base64');
  const dataUrl = `data:audio/mpeg;base64,${base64}`;

  const res = await fetch(`${BASE_URL}/analyses`, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'X-API-Key': API_KEY
    },
    body: JSON.stringify({
      salespersonId,
      audioKey: dataUrl
    })
  });

  return await res.json();
}

Base64 aumenta o tamanho do payload em ~33%. Para arquivos grandes, prefira hospedar o áudio numa URL pública HTTPS temporária e usar o formato do exemplo abaixo (mais leve e mais rápido).


Criar Análise a partir de uma URL de áudio

curl -X POST 'https://api.evolu-ai.com/api/analyses' \
  -H 'Content-Type: application/json' \
  -H 'X-API-Key: evai_your_key' \
  -d '{
    "salespersonId": "user_xxx",
    "audioKey": "https://exemplo.com/audio.mp3"
  }'

Varrer o histórico e correlacionar com o seu sistema

GET /api/analyses é o único endpoint que varre o histórico: pagine por cursor até nextCursor vir null. Cada item traz externalCallId, que é a chave de correlação com o CDR do PABX.

const API_KEY = 'evai_your_key_here';
const BASE_URL = 'https://api.evolu-ai.com/api';

async function* varrerAnalises(filtros = {}) {
  let cursor = null;

  do {
    const query = new URLSearchParams({ ...filtros, limit: '100' });
    if (cursor) {
      query.set('cursor', cursor);
    }

    const res = await fetch(`${BASE_URL}/analyses?${query}`, {
      headers: { 'X-API-Key': API_KEY }
    });
    if (!res.ok) {
      throw new Error(`HTTP ${res.status}: ${await res.text()}`);
    }

    const pagina = await res.json();
    yield* pagina.items;
    cursor = pagina.nextCursor;
  } while (cursor);
}

// Setembro inteiro, só as concluídas, indexadas pelo id da chamada no PABX
const porChamada = new Map();
for await (const analise of varrerAnalises({
  from: '2026-09-01T00:00:00Z',
  to: '2026-09-30T23:59:59Z',
  status: 'completed'
})) {
  porChamada.set(analise.externalCallId, analise.displayScore);
}

A listagem é um índice: não traz transcrição, áudio nem scorecard. Depois de achar o id aqui, busque o dossiê completo em GET /api/analyses/{id} (exemplo abaixo), só para as análises que você realmente vai abrir.


Achar a análise de uma chamada específica do PABX

curl -s 'https://api.evolu-ai.com/api/analyses?externalCallId=yeastar:1694000000.123' \
  -H 'X-API-Key: evai_your_key'

Devolve {"items": [...], "nextCursor": null, "hasMore": false} com zero ou um item, já que externalCallId é único por empresa.


Buscar uma análise pelo ID

const API_KEY = 'evai_your_key_here';

const res = await fetch('https://api.evolu-ai.com/api/analyses/anal_xxx', {
  headers: { 'X-API-Key': API_KEY }
});

const analise = await res.json();
console.log(`Score: ${analise.displayScore} | Status: ${analise.status}`); // displayScore = nota oficial

Configurar e receber Webhooks

Documentação completa (payload por papel, verificação de assinatura em JS e Python, retry/backoff) em Webhooks.

Ao contrário dos outros exemplos desta página, analysisWebhooks.create não é chamável com X-API-Key: é uma adminProcedure (sessão de navegador), configurada pela tela Configurações → Webhooks de Análise, não por integração externa. O trecho abaixo é só o lado RECEPTOR (o que roda no SEU servidor para verificar a assinatura de cada entrega).

// No seu endpoint receptor: verificar a assinatura antes de processar
const crypto = require('node:crypto');

function verifyOnvoxAiWebhook(secret, timestamp, rawBody, signatureHeader) {
  const expected = crypto
    .createHmac('sha256', secret)
    .update(`${timestamp}.${rawBody}`)
    .digest('hex');
  const received = signatureHeader.replace(/^sha256=/, '');
  const a = Buffer.from(expected, 'hex');
  const b = Buffer.from(received, 'hex');
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}

Tipos TypeScript

Os tipos abaixo cobrem os campos principais da resposta de POST /api/analyses. GET /api/analyses/{id} devolve um objeto bem maior (dossiê completo com diagnóstico, indicadores universais, transcrição e custo). Veja a lista completa em Endpoints.

interface Analysis {
  id: string;
  score: number | null;
  summary: string | null;
  clientName?: string | null;
  salespersonName?: string | null;
  audioDurationSeconds?: number | null;
  profile?: string | null;
  stage?: string | null;
  bant?: {
    budget: string;
    authority: string;
    need: string;
    timing: string;
  } | null;
  objections?: Array<{
    objection: string;
    resolved: boolean;
  }> | null;
  dimensionScores?: {
    rapport: number;
    listening: number;
    objections: number;
    clarity: number;
  } | null;
  interactionType?: string | null;
  interactionScore?: number | null;
  interactionScorecard?: Record<string, unknown> | null; // critérios do tipo de interação, ver Endpoints
  productExploration?: { explored: boolean; products: string[] } | null;
  keywordDetection?: Record<string, unknown> | null;
  publicLink?: string;
  createdAt: string;
}

interface Salesperson {
  id: string;
  name: string;
  role: 'salesperson' | 'supervisor';
  supervisorName: string | null;
}

// Resposta de GET /api/analyses (listagem, índice): conjunto FECHADO de campos.
interface AnalysisListItem {
  id: string;
  createdAt: string;
  updatedAt: string;
  status: 'pending' | 'processing' | 'completed' | 'failed';
  source: string;
  provider: string | null;
  externalCallId: string | null;
  externalOwnerId: string | null;
  companyId: string | null;
  salespersonId: string | null;
  salespersonName: string | null;
  supervisorName: string | null;
  clientName: string | null;
  displayScore: number | null;
  score: number | null;
  interactionScore: number | null;
  interactionType: string | null;
  interactionTypeSource: string | null;
  interactionTypeLabel: string | null;
  summary: string | null;
  audioDurationSeconds: number | null;
  attributionStatus: string;
}

interface AnalysisListPage {
  items: AnalysisListItem[];
  nextCursor: string | null;
  hasMore: boolean;
}

Nesta página