Skip to main content

Estrutura de Erro

Todos os erros da API seguem este formato:
{
  "success": false,
  "error": {
    "code": "error_code",
    "message": "Descrição legível do erro",
    "requestId": "req_abc123"
  }
}

Códigos HTTP

StatusSignificadoAção
400Requisição inválidaCorrija os parâmetros
401Não autenticadoVerifique API Key
403Sem permissãoVerifique scopes
404Não encontradoRecurso não existe
429Rate limitAguarde e tente novamente
500Erro do servidorTente novamente depois

Erros Comuns

Autenticação

CódigoDescriçãoSolução
invalid_api_keyAPI Key inválidaVerifique se a key está correta
missing_api_keyAPI Key não enviadaAdicione header Authorization
expired_api_keyAPI Key expiradaGere uma nova key

Mensagens

CódigoDescriçãoSolução
instance_disconnectedInstância offlineReconecte a instância
invalid_phone_numberNúmero inválidoUse formato internacional
media_too_largeArquivo muito grandeReduza o tamanho
unsupported_media_typeTipo não suportadoVeja tipos suportados

Rate Limit

CódigoDescriçãoSolução
rate_limit_exceededLimite excedidoAguarde retryAfter segundos

Tratamento em Código

Node.js

import { EnviaAI, EnviaAIError } from '@enviaai/sdk';

const client = new EnviaAI({ apiKey: process.env.API_KEY });

async function sendMessage(request) {
  try {
    const response = await client.messages.send(request);
    return response;
  } catch (error) {
    if (error instanceof EnviaAIError) {
      switch (error.code) {
        case 'instance_disconnected':
          // Tentar reconectar
          await reconnectInstance(request.instanceId);
          // Tentar novamente
          return client.messages.send(request);

        case 'rate_limit_exceeded':
          // Aguardar e tentar novamente
          await sleep(error.retryAfter * 1000);
          return client.messages.send(request);

        case 'invalid_phone_number':
          // Logar e não tentar novamente
          console.error(`Número inválido: ${request.to}`);
          throw error;

        default:
          console.error(`Erro: ${error.code} - ${error.message}`);
          throw error;
      }
    }

    // Erro inesperado
    console.error('Erro inesperado:', error);
    throw error;
  }
}

Python

from enviaai import EnviaAI, EnviaAIError
import time

client = EnviaAI(api_key=os.environ["API_KEY"])

def send_message(request):
    try:
        return client.messages.send(**request)
    except EnviaAIError as e:
        if e.code == "instance_disconnected":
            reconnect_instance(request["instance_id"])
            return client.messages.send(**request)

        elif e.code == "rate_limit_exceeded":
            time.sleep(e.retry_after)
            return client.messages.send(**request)

        elif e.code == "invalid_phone_number":
            print(f"Número inválido: {request['to']}")
            raise

        else:
            print(f"Erro: {e.code} - {e.message}")
            raise

Retry com Backoff

Para erros transientes, implemente retry com exponential backoff:
async function withRetry(fn, maxRetries = 3) {
  let lastError;

  for (let attempt = 0; attempt < maxRetries; attempt++) {
    try {
      return await fn();
    } catch (error) {
      lastError = error;

      // Não fazer retry para erros de validação
      if (error.code === 'invalid_phone_number' ||
          error.code === 'invalid_api_key') {
        throw error;
      }

      // Rate limit: usar retryAfter
      if (error.code === 'rate_limit_exceeded') {
        await sleep(error.retryAfter * 1000);
        continue;
      }

      // Outros erros: exponential backoff
      const delay = Math.pow(2, attempt) * 1000 + Math.random() * 1000;
      console.log(`Tentativa ${attempt + 1} falhou. Aguardando ${delay}ms...`);
      await sleep(delay);
    }
  }

  throw lastError;
}

// Uso
const response = await withRetry(() =>
  client.messages.send(request)
);

Circuit Breaker

Para evitar cascata de falhas:
class CircuitBreaker {
  constructor(threshold = 5, timeout = 60000) {
    this.failures = 0;
    this.threshold = threshold;
    this.timeout = timeout;
    this.state = 'CLOSED';
    this.nextAttempt = 0;
  }

  async call(fn) {
    if (this.state === 'OPEN') {
      if (Date.now() < this.nextAttempt) {
        throw new Error('Circuit breaker is OPEN');
      }
      this.state = 'HALF-OPEN';
    }

    try {
      const result = await fn();
      this.onSuccess();
      return result;
    } catch (error) {
      this.onFailure();
      throw error;
    }
  }

  onSuccess() {
    this.failures = 0;
    this.state = 'CLOSED';
  }

  onFailure() {
    this.failures++;
    if (this.failures >= this.threshold) {
      this.state = 'OPEN';
      this.nextAttempt = Date.now() + this.timeout;
    }
  }
}

// Uso
const breaker = new CircuitBreaker();

try {
  await breaker.call(() => client.messages.send(request));
} catch (error) {
  if (error.message === 'Circuit breaker is OPEN') {
    // Usar fallback ou notificar
  }
}

Logging

Sempre log erros com contexto suficiente:
function logError(error, context = {}) {
  console.error(JSON.stringify({
    timestamp: new Date().toISOString(),
    error: {
      code: error.code,
      message: error.message,
      requestId: error.requestId,
      stack: error.stack
    },
    context
  }));
}

// Uso
try {
  await client.messages.send(request);
} catch (error) {
  logError(error, {
    action: 'send_message',
    instanceId: request.instanceId,
    to: request.to
  });
  throw error;
}

Monitoramento

Alertas

Configure alertas para erros frequentes

Métricas

Monitore taxa de erro e latência

Logs centralizados

Use serviços como Datadog, LogRocket

Dashboards

Visualize saúde da integração

Checklist de Produção

  • Retry com backoff para erros transientes
  • Tratamento específico por código de erro
  • Logging estruturado com contexto
  • Alertas para erros críticos
  • Circuit breaker para evitar cascata
  • Fallback para operações críticas
  • Monitoramento de taxa de erro