Gestão de erros
Todas as respostas de erro seguem uma única estrutura JSON. Use os códigos de estado HTTP para encaminhar a sua lógica de tratamento de erros e o campo error para contexto legível por humanos.
// Every non-2xx response returns:
{
"error": "human-readable description of what went wrong"
}Códigos de erro
A tabela abaixo cobre todos os códigos de estado devolvidos pelo Connect Hub. A coluna Repetir indica se a repetição automática é segura.
| Código | Nome | Repetir | Descrição |
|---|---|---|---|
400 | Bad Request | NO | Campos em falta ou inválidos (p. ex. telefone fora do E.164, parâmetro obrigatório em falta no corpo). Corrija o seu payload. |
401 | Unauthorized | NO | Cabeçalho x-api-key em falta ou inválido. Verifique a sua chave de API — não repita automaticamente. |
402 | Payment Required | NO | Saldo da conta insuficiente. Carregue a sua conta Novauth antes de repetir. |
403 | Forbidden | NO | A chave de API não tem permissão para executar esta ação. |
404 | Not Found | NO | O recurso não existe (p. ex. UUID desconhecido em /call/flash/{uuid}/cdr). Não repita. |
409 | Conflict | NO | Foi detetada uma operação duplicada ou em conflito (p. ex. comunicar o webhook duas vezes para o mesmo UUID). |
422 | Unprocessable Entity | NO | Específico do Telegram: pedido semanticamente inválido (p. ex. request_id expirado em check-verification-status). |
429 | Too Many Requests | YES | Limite de taxa excedido. Respeite o cabeçalho Retry-After e implemente backoff exponencial. |
480 | Temporarily Unavailable | YES | Destinatário temporariamente inacessível (derivado de SIP). Seguro repetir após um curto atraso. |
482 | Loop Detected | NO | Ciclo de encaminhamento do pedido detetado. Não repita — investigue a sua integração. |
486 | Busy Here | YES | O destinatário está ocupado. Repita após um atraso ou recorra a outro canal. |
488 | Not Acceptable Here | NO | Parâmetros do pedido não aceitáveis (p. ex. codec ou formato não suportado). Corrija o pedido. |
500 | Internal Server Error | YES | Falha inesperada do lado do servidor. Repita com backoff exponencial (máx. 3 tentativas). |
502 | Bad Gateway | YES | O serviço a montante (operadora, gateway) devolveu uma resposta inválida. Repita após uma breve espera. |
503 | Service Unavailable | YES | Serviço temporariamente sobrecarregado ou em manutenção. Respeite o Retry-After, se presente. |
603 | Decline | NO | O destinatário recusou explicitamente (derivado de SIP). Não repita — o utilizador rejeitou ativamente a chamada. |
Lógica de repetição
Repita apenas nos códigos 429, 480, 486, 500, 502, 503. Use backoff exponencial a começar em 500 ms (duplicando a cada tentativa). Nunca repita automaticamente erros de cliente 4xx — corrija primeiro a causa raiz.
async function callWithRetry(fn, { maxAttempts = 3, baseDelay = 500 } = {}) {
// Safe codes to retry: 429, 500, 502, 503
const RETRYABLE = new Set([429, 500, 502, 503]);
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
const res = await fn();
if (res.ok) return res;
const { status } = res;
// Non-retryable — surface error immediately
if (!RETRYABLE.has(status)) {
const body = await res.json().catch(() => ({}));
throw Object.assign(new Error(body.error ?? 'API error'), { status, body });
}
if (attempt === maxAttempts) {
throw Object.assign(new Error('Max retries reached'), { status });
}
// Exponential backoff: 500ms, 1000ms, 2000ms …
const delay = baseDelay * 2 ** (attempt - 1);
await new Promise(r => setTimeout(r, delay));
}
}
// Usage
const res = await callWithRetry(() =>
fetch('https://api.novauth.com/api/v1/connect-hub/call/flash', {
method: 'POST',
headers: {
'x-api-key': process.env.BETATEL_API_KEY,
'x-account-id': process.env.BETATEL_ACCOUNT_ID,
'Content-Type': 'application/json'
},
body: JSON.stringify({ callee: '+14155552671' })
})
);As operações idempotentes (consultas de estado GET) podem ser repetidas livremente. Nas operações POST que criam recursos, verifique sempre se o recurso já foi criado antes de repetir — use o UUID devolvido para consultar o estado primeiro.
Cascata de fallback
O Verify Call funciona na maioria das regiões, mas não é universal. Implemente uma cascata para que os utilizadores recebam sempre o seu OTP, mesmo quando o canal principal falha.
Success dentro de uma janela de tempo configurável (recomendado: 15 s para o Verify Call).async function sendWithFallback(phone, otp) {
// 1. Try Verify Call first
try {
const flash = await fetch('.../call/flash', {
method: 'POST',
headers,
body: JSON.stringify({ callee: phone })
});
if (flash.ok) return { channel: 'flash', ...(await flash.json()) };
} catch {}
// 2. Flash failed — fall back to Telegram if user has it
try {
const tg = await fetch('.../telegram', {
method: 'POST',
headers,
body: JSON.stringify({ to: phone, code: otp, ttl: 120 })
});
if (tg.ok) return { channel: 'telegram', ...(await tg.json()) };
} catch {}
// 3. Final fallback: SMS (widest global reach)
const sms = await fetch('.../sms', {
method: 'POST',
headers,
body: JSON.stringify({ to: phone, text: `Your code: ${otp}` })
});
if (!sms.ok) throw new Error('All channels failed');
return { channel: 'sms', ...(await sms.json()) };
}Para desativar a cascata num fluxo específico, basta omitir os blocos try/catch de fallback e apresentar o erro diretamente ao utilizador. Não existe qualquer definição de cascata do lado do servidor — a lógica vive inteiramente no seu código de integração.
Limitação de taxa
Os limites de taxa são aplicados na camada do gateway da API. Quando um limite é atingido, recebe uma resposta 429 Too Many Requests. Consulte o cabeçalho Retry-After para saber o tempo de espera exato.
Retry-AfterX-RateLimit-LimitX-RateLimit-RemainingX-RateLimit-Resetconst res = await fetch(url, { method: 'POST', headers, body });
if (res.status === 429) {
const retryAfter = Number(res.headers.get('Retry-After') ?? 1);
console.warn(`Rate limited — waiting ${retryAfter}s`);
await new Promise(r => setTimeout(r, retryAfter * 1000));
// retry the request
}Erros de integração comuns
Estes são os problemas mais frequentemente comunicados durante a integração. Consulte esta lista antes de abrir um pedido de suporte.