API Tolky Reasoning - Quick Start

📋 Informações Essenciais

ItemValor
EndpointPOST {BASE_URL}/api/externalAPIs/public/tolkyReasoning/callReasoning
AutenticaçãoAuthorization: Bearer {TOKEN}
Timeout60 segundos
Content-Typeapplication/json

⚡ Request Básico

const API_URL = 'https://api.tolky.to/api/externalAPIs/public/tolkyReasoning/callReasoning';
const TOKEN = process.env.TOLKY_API_TOKEN;

const response = await fetch(API_URL, {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${TOKEN}`,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    hostId: "uuid-do-host",
    conversationId: "uuid-da-conversa",
    question: "Sua pergunta aqui"
  })
});

const data = await response.json();

// ⚠️ CRÍTICO: Sempre validar responseStatus
if (!data.data?.responseStatus?.ok) {
  throw new Error(data.data.responseStatus?.message || 'Erro no processamento');
}

// ✅ Processar resposta
const text = data.data.assistantResponse.string;
const parsed = data.data.assistantResponse.wppParsed; // ⚠️ É ARRAY!

📊 Estrutura da Resposta

{
  code: 200,
  message: "OK",
  data: {
    assistantResponse: {
      string: "Resposta em markdown",
      wppParsed: [{ type, content, url?, description? }], // ← ARRAY!
      isAudio: false,
      mediaDescriptionArray: []
    },
    tokensControl: [{ model: string, tokens: number }],
    conversationId: "uuid",
    createdMessageIds: ["uuid-1", "uuid-2"],
    requestControlId: "rc-uuid",
    responseStatus: { 
      ok: true,           // ← Sempre validar!
      message: null 
    }
  }
}

⚠️ Pontos Críticos

1. wppParsed é ARRAY, não Object!

// ❌ ERRADO
const text = response.data.assistantResponse.wppParsed.content;

// ✅ CORRETO
response.data.assistantResponse.wppParsed.forEach(item => {
  console.log(item.type, item.content);
  // Processar cada item conforme o tipo
});

// ✅ Ou acessar por índice
const firstItem = response.data.assistantResponse.wppParsed[0];

2. Sempre Validar responseStatus.ok

// ⚠️ SEMPRE validar antes de processar
if (response.data.responseStatus.ok === false) {
  const errorMessage = response.data.responseStatus.message || 
                       'Serviço temporariamente indisponível';
  
  // Implementar retry para erros temporários
  console.error('Service error:', errorMessage);
  throw new Error(errorMessage);
}

// ✅ Processar apenas se ok === true
const text = response.data.assistantResponse.string;

3. Implementar Retry (408, 500, 503)

async function callWithRetry(payload, maxRetries = 3) {
  for (let i = 0; i < maxRetries; i++) {
    try {
      const response = await fetch(API_URL, {
        method: 'POST',
        headers: {
          'Authorization': `Bearer ${TOKEN}`,
          'Content-Type': 'application/json'
        },
        body: JSON.stringify(payload),
        signal: AbortSignal.timeout(60000) // 60s timeout
      });

      if (!response.ok) {
        // Retry apenas para erros temporários
        if ([408, 500, 503].includes(response.status)) {
          const waitTime = Math.pow(2, i) * 1000; // 1s, 2s, 4s
          console.log(`Retrying in ${waitTime}ms... (attempt ${i + 1}/${maxRetries})`);
          await sleep(waitTime);
          continue;
        }
        throw new Error(`HTTP ${response.status}: ${response.statusText}`);
      }

      const data = await response.json();
      
      // Validar responseStatus mesmo com HTTP 200
      if (!data.data?.responseStatus?.ok) {
        // Serviço retornou 200 mas com erro interno (503)
        if (i < maxRetries - 1) {
          const waitTime = Math.pow(2, i) * 1000;
          await sleep(waitTime);
          continue;
        }
        throw new Error(data.data.responseStatus?.message || 'Erro no processamento');
      }

      return data.data;

    } catch (error) {
      if (i === maxRetries - 1) throw error;
      
      // Retry para timeouts e erros de rede
      if (error.name === 'TimeoutError' || error.name === 'NetworkError') {
        const waitTime = Math.pow(2, i) * 1000;
        await sleep(waitTime);
        continue;
      }
      
      throw error;
    }
  }
}

function sleep(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}

🎨 Tipos de Conteúdo wppParsed

TipoEstruturaDescrição
text{ type: "text", content: string }Texto simples ou com formatação *negrito*
image{ type: "image", content: string, url: string, description: string }Imagens (jpg, png, gif, webp)
site{ type: "site", content: string, url: string, description: string }Links para sites
audio{ type: "audio", content: string, url: string, description: string }Arquivos de áudio (mp3, wav)
document{ type: "document", content: string, url: string, description: string }Documentos (pdf, doc, xls)
pix{ type: "pix", content: string }Código PIX Copia e Cola
boleto{ type: "boleto", content: string }Linha digitável de boleto

Exemplo de Processamento

function renderWppParsed(wppParsed) {
  return wppParsed.map(item => {
    switch (item.type) {
      case 'text':
        return item.content;
      
      case 'image':
        return `[Imagem: ${item.description}]\n${item.url}`;
      
      case 'site':
        return `[Link: ${item.description}]\n${item.url}`;
      
      case 'audio':
        return `[Áudio: ${item.description}]\n${item.url}`;
      
      case 'document':
        return `[Documento: ${item.description}]\n${item.url}`;
      
      case 'pix':
        return `[PIX Copia e Cola]\n${item.content}`;
      
      case 'boleto':
        return `[Boleto]\n${item.content}`;
      
      default:
        return item.content;
    }
  }).join('\n\n');
}

// Uso
const rendered = renderWppParsed(response.data.assistantResponse.wppParsed);
console.log(rendered);

⚙️ Configurações Úteis

Produção (Otimizado)

{
  "reasoningConfig": {
    "tolkyCompleteLog": false,
    "returnDialogue": false,
    "saveAssistantResponse": true,
    "returnGlobalData": false
  }
}

Benefícios:

  • ✅ Resposta mais rápida
  • ✅ Menor consumo de banda
  • ✅ Ideal para produção

Debug (Completo)

{
  "reasoningConfig": {
    "tolkyCompleteLog": true,
    "returnDialogue": true,
    "returnGlobalData": true,
    "saveAssistantResponse": true
  }
}

Benefícios:

  • ✅ Log completo do processamento
  • ✅ Histórico completo da conversa
  • ✅ Dados globais para análise
  • ⚠️ Resposta muito maior (use apenas para debug)

White-Label (Personalização de Marca)

{
  "reasoningConfig": {
    "tolkyCompleteLog": true,
    "replaceDomainName": "MinhaEmpresa"
  }
}

Substitui todas as ocorrências de “tolky” (case-insensitive) nos logs pelo nome fornecido.


🔍 Códigos de Erro Comuns

CódigoSignificadoAção Recomendada
200✅ SucessoValidar responseStatus.ok antes de processar
400⚠️ Requisição InválidaValidar parâmetros enviados
401🔒 Não AutorizadoVerificar token de autenticação
403🚫 ProibidoVerificar permissões do token
408⏱️ TimeoutImplementar retry com backoff exponencial
500❌ Erro InternoImplementar retry com backoff exponencial
503🔧 Serviço IndisponívelImplementar retry com backoff exponencial

💻 Exemplo Completo

class TolkyClient {
  constructor(apiUrl, token, hostId) {
    this.apiUrl = apiUrl;
    this.token = token;
    this.hostId = hostId;
    this.maxRetries = 3;
  }

  async callReasoning(conversationId, question, options = {}) {
    const payload = {
      hostId: this.hostId,
      conversationId,
      question,
      ...options
    };

    return this.callWithRetry(payload);
  }

  async callWithRetry(payload, retryCount = 0) {
    try {
      const response = await fetch(
        `${this.apiUrl}/api/externalAPIs/public/tolkyReasoning/callReasoning`,
        {
          method: 'POST',
          headers: {
            'Authorization': `Bearer ${this.token}`,
            'Content-Type': 'application/json'
          },
          body: JSON.stringify(payload),
          signal: AbortSignal.timeout(60000)
        }
      );

      if (!response.ok) {
        if ([408, 500, 503].includes(response.status) && retryCount < this.maxRetries) {
          const waitTime = Math.pow(2, retryCount) * 1000;
          console.log(`Retrying in ${waitTime}ms...`);
          await this.sleep(waitTime);
          return this.callWithRetry(payload, retryCount + 1);
        }
        throw new Error(`HTTP ${response.status}: ${response.statusText}`);
      }

      const data = await response.json();

      // Validar responseStatus
      if (!data.data?.responseStatus?.ok) {
        if (retryCount < this.maxRetries) {
          const waitTime = Math.pow(2, retryCount) * 1000;
          await this.sleep(waitTime);
          return this.callWithRetry(payload, retryCount + 1);
        }
        throw new Error(
          data.data.responseStatus?.message || 'Serviço temporariamente indisponível'
        );
      }

      return data.data;

    } catch (error) {
      if (retryCount < this.maxRetries && 
          (error.name === 'TimeoutError' || error.name === 'NetworkError')) {
        const waitTime = Math.pow(2, retryCount) * 1000;
        await this.sleep(waitTime);
        return this.callWithRetry(payload, retryCount + 1);
      }
      throw error;
    }
  }

  sleep(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
  }

  renderWppParsed(wppParsed) {
    return wppParsed.map(item => {
      switch (item.type) {
        case 'text':
          return item.content;
        case 'image':
          return `[Imagem: ${item.description}]\n${item.url}`;
        case 'site':
          return `[Link: ${item.description}]\n${item.url}`;
        case 'pix':
          return `[PIX Copia e Cola]\n${item.content}`;
        case 'boleto':
          return `[Boleto]\n${item.content}`;
        default:
          return item.content;
      }
    }).join('\n\n');
  }
}

// Uso
const client = new TolkyClient(
  process.env.TOLKY_API_URL,
  process.env.TOLKY_API_TOKEN,
  process.env.TOLKY_HOST_ID
);

try {
  const result = await client.callReasoning(
    'conv-uuid-123',
    'Olá, preciso de ajuda',
    {
      userData: {
        userName: 'João Silva',
        email: 'joao@exemplo.com'
      },
      reasoningConfig: {
        returnDialogue: false,
        tolkyCompleteLog: false
      }
    }
  );

  console.log('Resposta:', result.assistantResponse.string);
  console.log('Parsed:', client.renderWppParsed(result.assistantResponse.wppParsed));
  console.log('Tokens:', result.tokensControl);
  console.log('Request ID:', result.requestControlId);

} catch (error) {
  console.error('Erro:', error.message);
}

✅ Checklist de Integração

Antes de ir para produção, certifique-se de:

  • Token em variável de ambiente - Nunca exponha o token no código
  • Validação de responseStatus.ok - Sempre valide antes de processar
  • Tratamento de wppParsed como array - Sempre itere sobre ele
  • Retry implementado - Para códigos 408, 500, 503 e quando responseStatus.ok === false
  • Timeout de 60s configurado - Configure no cliente HTTP
  • Logging de requestControlId - Para rastreamento e auditoria
  • Logging de createdMessageIds - Para rastreamento de mensagens
  • Monitoramento de tokens - Acompanhe o consumo através de tokensControl
  • Tratamento de erros - Implemente tratamento adequado para todos os casos
  • Testes com diferentes tipos de resposta - Texto, imagens, links, PIX, boletos

📚 Documentação Completa

Para informações detalhadas, consulte:


🆘 Suporte

Para mais informações ou suporte técnico:


Versão: 2.0 | Data: 06/02/2026 | Última atualização: 06/02/2026