Skip to content

Error Handling

Introdução

Todos os erros retornados pela BaaS API seguem um formato padrão estruturado, permitindo tratamento consistente e automatizado pelo cliente integrador. Cada erro inclui um código único, mensagem descritiva, HTTP status code, identificador de requisição e timestamp para auditoria e troubleshooting.

Formato Padrão de Erro

json
{
  "error": "INVALID_REQUEST",
  "code": "E00001",
  "message": "Missing required field: email",
  "statusCode": 400,
  "requestId": "550e8400-e29b-41d4-a716-446655440000",
  "timestamp": "2024-03-15T10:30:00Z"
}

Campos obrigatórios:

  • error — Tipo genérico do erro (INVALID_REQUEST, UNAUTHORIZED, etc)
  • code — Código único para identifying o erro específico (E00001–E99999)
  • message — Descrição em linguagem natural da causa
  • statusCode — HTTP status code
  • requestId — UUID da requisição (incluso em logs para rastreamento)
  • timestamp — ISO 8601 timestamp do erro

Categorias de Erro

CategoriaHTTPDescriçãoRetryableAção Recomendada
Validação400Request inválido (schema, formato, checksum)NãoValidar antes de enviar; revisar documentação da API
Autenticação401Token expirado, inválido ou revogadoSim (refresh)Renovar token ou re-autenticar
Autorização403Sem permissão para recurso (scope, tenant, etc)NãoVerificar credenciais e permissões
Não encontrado404Recurso não existe ou foi deletadoNãoVerificar ID e fazer requisição novamente
Conflito409Duplicata, state mismatch, violação únicaNãoUsar idempotência; verificar estado atual
Rate limit429Limite de requisições excedidoSim (backoff)Aguardar + respeitar header Retry-After
Servidor5xxErro interno, serviço temporariamente indisponívelSim (backoff)Retry com exponential backoff; logar e alertar

HTTP Status Codes

CodeTipoDescrição
200OKSucesso
202AcceptedRequisição aceita, processamento assíncrono (ex: antifraud, onboarding)
400Bad RequestValidação falhou (formato, required field, checksum inválido)
401UnauthorizedToken expirado, inválido ou credenciais rejeitadas
402Payment RequiredErro de dados específico (deprecated em favor de 400/422)
403ForbiddenAcesso negado por permissão, tenant bloqueado ou antifraud
404Not FoundRecurso não existe ou foi removido
405Method Not AllowedVerb HTTP não suportado para endpoint
409ConflictDuplicata, inconsistência de estado ou violação de constraint única
422Unprocessable EntityRequest válida, mas falha na lógica de negócio (KYC, saldo insuficiente)
429Too Many RequestsRate limit excedido; respeitar Retry-After
500Internal Server ErrorErro não-esperado no servidor
503Service UnavailableServiço temporariamente indisponível (manutenção, deps down)

Códigos de Erro por Endpoint

Authentication & Access

CódigoMessageStatusCausa
E00101Invalid client credentials401Client ID ou Secret incorrect; tenant desativado
E00102Token expired401Bearer token expirou (TTL padrão: 1h)
E00103Invalid token format401Token malformado, assinatura inválida ou não-JWT
E00104Token revoked401Token foi explicitamente revogado (logout, password reset)
E00105Insufficient scope403Token válido mas sem scope necessário para operação
E00106Tenant suspended403Tenant bloqueado por issue de billing ou compliance

Bank Accounts & Onboarding

CódigoMessageStatusCausa
E00201Invalid CPF/CNPJ400Documento inválido (checksum, formato ou blacklist)
E00202Email already registered409Email duplicado no sistema (constraint única)
E00203Missing required address field400Address (street, number, city, state, zip) incompleto
E00204Onboarding failed422KYC rejeitado (documentos inválidos, PEP, sanção, liveness fail)
E00205Account creation timeout504Provider BaaS demorou > 10s
E00206Account already exists409Cliente já possui conta ativa neste tenant
E00207Identity verification failed422Biometria, selfie ou OTP não validado

PIX & Transfers

CódigoMessageStatusCausa
E00301Invalid PIX key format400Email, telefone, CPF ou chave aleatória malformados
E00302PIX key already registered409Chave PIX já existe para esta conta ou outro usuário
E00303Insufficient balance422Saldo insuficiente para transferência ou taxa
E00304PIX receiver not found404Chave PIX não existe (dict lookup falhou ou receiver não possui conta)
E00305PIX reversal not allowed422Reversão só permitida dentro de 1h após transação
E00306PIX key limit reached429Máximo de chaves PIX por conta atingido
E00307Transfer in progress409Já existe transferência pendente com mesma chave/valor

Billing & Charges

CódigoMessageStatusCausa
E00401Invalid boleto amount400Valor < R$ 0.01 ou > R$ 999.999,99
E00402Recurring charge limit reached429Máximo de cobranças recorrentes por conta atingido
E00403Charge already paid409Boleto já foi pago ou processado
E00404Charge not found404Boleto não existe ou foi cancelado
E00405Charge expiration invalid400Data de vencimento inválida (passada ou > 120 dias)

Cards & Payments

CódigoMessageStatusCausa
E00501Invalid card400Cartão malformado (número, CVC, data de validade)
E00502Card expired422Data de validade expirada
E00503Card blocked403Cartão bloqueado por fraude, antifraud ou solicitação do cliente
E00504Card limit exceeded422Limite de crédito/débito excedido
E00505Payment declined422Banco recusou transação (motivo não-específico da Axia)

Tratamento por Tipo

4.1 Validação (400)

Característica: Erro do lado cliente; nunca retryar.

Ação:

  1. Validar input ANTES de enviar para API
  2. Usar biblioteca de validação (ex: Joi, Yup, Zod)
  3. Exibir mensagem clara ao usuário
  4. Logar para auditoria

Exemplos:

typescript
// Validar antes de enviar
if (!isValidCPF(cpf)) {
  throw new ValidationError('Invalid CPF format');
}

if (!email.includes('@') || !email.includes('.')) {
  throw new ValidationError('Invalid email format');
}

if (amount <= 0 || amount > 999999.99) {
  throw new ValidationError('Amount must be between 0.01 and 999999.99');
}

// Request com erro de validação
const response = await fetch('/api/v1/accounts', {
  method: 'POST',
  body: JSON.stringify({ email: 'invalid-email', cpf: '12345' })
});

// Esperado: 400 Bad Request
// {
//   "error": "INVALID_REQUEST",
//   "code": "E00201",
//   "message": "Invalid CPF format",
//   "statusCode": 400
// }

4.2 Rate Limit (429)

Característica: Retryável; respeitar backoff exponencial.

Ação:

  1. Extrair header Retry-After (segundos)
  2. Aguardar + retry automático
  3. Implementar circuit breaker após 5+ retries
  4. Logar para alertar DevOps se persistir

Exemplo:

typescript
async function requestWithBackoff(url: string, options: RequestInit, maxRetries = 5) {
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    const response = await fetch(url, options);
    
    if (response.status === 429) {
      const retryAfter = parseInt(response.headers.get('Retry-After') || '60') * 1000;
      console.warn(`Rate limited. Retrying after ${retryAfter}ms`);
      await new Promise(r => setTimeout(r, retryAfter));
      continue;
    }
    
    if (response.ok) return response;
    if (response.status >= 500) {
      // Exponential backoff para 5xx
      const delay = Math.min(1000 * Math.pow(2, attempt), 30000);
      await new Promise(r => setTimeout(r, delay));
      continue;
    }
    
    return response; // 4xx não-retry
  }
  throw new Error('Max retries exceeded');
}

4.3 Autenticação (401)

Característica: Retryável via refresh token.

Ação:

  1. Se Token expired: usar refresh token para renovar
  2. Se Invalid client credentials: erro crítico (revisar .env)
  3. Implementar refresh automático antes de expiração (TTL - 5 min)

Exemplo:

typescript
async function authenticatedRequest(endpoint: string, options: RequestInit) {
  let token = getStoredToken();
  let response = await fetch(endpoint, {
    ...options,
    headers: { ...options.headers, 'Authorization': `Bearer ${token}` }
  });
  
  if (response.status === 401) {
    const errorBody = await response.json();
    
    if (errorBody.code === 'E00102') { // Token expired
      token = await refreshAccessToken();
      response = await fetch(endpoint, {
        ...options,
        headers: { ...options.headers, 'Authorization': `Bearer ${token}` }
      });
    } else if (errorBody.code === 'E00101') { // Invalid credentials
      throw new Error('Invalid client credentials — check .env');
    }
  }
  
  return response;
}

4.4 Servidor (5xx)

Característica: Retryável com backoff exponencial; alerta DevOps se persistir.

Ação:

  1. Retry com exponential backoff (1s, 2s, 4s, 8s, 16s, max 30s)
  2. Logar requestId para correlação com servidor
  3. Alertar após 3 falhas consecutivas
  4. Contatar suporte se > 5 min persistindo

Exemplo:

typescript
async function robustRequest(url: string, options: RequestInit) {
  const maxRetries = 5;
  
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    try {
      const response = await fetch(url, options);
      
      if (response.status < 500) return response;
      
      const error = await response.json();
      const requestId = error.requestId;
      
      if (attempt < maxRetries - 1) {
        const delay = Math.min(1000 * Math.pow(2, attempt), 30000);
        console.error(
          `Server error (attempt ${attempt + 1}). RequestId: ${requestId}. ` +
          `Retrying in ${delay}ms`
        );
        await new Promise(r => setTimeout(r, delay));
        continue;
      } else {
        console.error(
          `Request failed after ${maxRetries} attempts. RequestId: ${requestId}. ` +
          `Contact support.`
        );
        throw new Error(`Server error: ${error.code}`);
      }
    } catch (err) {
      if (attempt < maxRetries - 1) {
        const delay = Math.min(1000 * Math.pow(2, attempt), 30000);
        await new Promise(r => setTimeout(r, delay));
      } else {
        throw err;
      }
    }
  }
}

Troubleshooting por Cenário

"Account creation stuck in PROCESSING"

Causa: KYC lento, documento rejeitado ou provider BaaS indisponível.

Ação:

  1. Polling GET /checkOnboarding a cada 30s (máximo 5 min)
  2. Verificar no BackOffice > Auditoria > Onboarding se há rejection_reason
  3. Se timeout após 5 min: webhook deveria ter chegado; verificar logs do cliente

Exemplo:

typescript
async function waitForOnboarding(accountId: string, maxWaitMs = 300000) {
  const pollInterval = 30000;
  const startTime = Date.now();
  
  while (Date.now() - startTime < maxWaitMs) {
    const response = await fetch(`/api/v1/accounts/${accountId}/onboarding`);
    const data = await response.json();
    
    if (data.status === 'APPROVED') {
      console.log('Account onboarded successfully');
      return data;
    }
    
    if (data.status === 'REJECTED') {
      throw new Error(`KYC rejected: ${data.rejectionReason}`);
    }
    
    await new Promise(r => setTimeout(r, pollInterval));
  }
  
  throw new Error('Onboarding timeout after 5 min');
}

"PIX payment rejected: E00304 (PIX receiver not found)"

Causa: Chave PIX inválida no DICT (Directório de Identificadores de Transações PIX do Banco Central).

Ação:

  1. Verificar se chave PIX é válida no banco do destinatário
  2. Tentar novamente após 5 min (geralmente delay de replicação)
  3. Se persistir, contatar receiver para confirmar chave

Nota: Receiver precisa estar cadastrado no banco e ter chave ativa no DICT.

"Webhook não chegando"

Causa: HTTPS inválido, firewall, ou endpoint retornando erro.

Ação:

  1. Verificar se URL é HTTPS com certificado válido
  2. Verificar se endpoint está respondendo 200–299
  3. Logar headers recebidos em Axia (BackOffice > Auditoria > Webhooks)
  4. Se 5 falhas consecutivas: evento movido para DLQ (Dead Letter Queue)
  5. Usar endpoint de replay para reprocessar

Configuração de Webhook:

typescript
// Seu endpoint deve:
app.post('/webhook/axia', (req, res) => {
  const event = req.body; // { type, data, timestamp, signature }
  
  // Validar assinatura (HMAC-SHA256)
  const isValid = validateWebhookSignature(event, WEBHOOK_SECRET);
  if (!isValid) return res.status(401).send('Unauthorized');
  
  // Responder 200 IMEDIATAMENTE (não processar de forma síncrona)
  res.status(200).send('OK');
  
  // Processar de forma assíncrona + idempotência por event_id
  processEventAsync(event);
});

Common Errors (Referência Rápida)

ErroCódigoCausaSolução
Token inválidoE00101–E00106Credenciais erradas ou token expiradoRe-autenticar ou renovar token
Conta não encontradaE00206Account ID inválidoVerificar ID e fazer requisição novamente
Saldo insuficienteE00303Balance < transfer amountVerificar saldo antes de enviar
Transação duplicadaE00202–E00206Idempotência-Key reutilizadaGerar nova Idempotency-Key (UUID)
Rate limit excedidoE00302, E00306, E00402, E00429Muitas requisiçõesAguardar + implementar backoff
Serviço indisponível503Manutenção ou dependency downRetry com backoff; contatar suporte