Axuz Developers

Erros

Formato real das respostas de erro da API da Axuz, mapa de status HTTP, o 404 de recurso inexistente versus rota inexistente, rastreio por X-Trace-Id e quando repetir uma chamada.

Toda resposta de erro da API vem com um status HTTP e, quase sempre, um corpo JSON pequeno. Decida o que fazer pelo status; use o texto do corpo para mostrar ao usuário ou para o log, não como contrato.

O formato

O formato canônico é um objeto com a chave error:

{ "error": "task not found" }

Isso vale para o serviço principal, para o de workspace, calendário, formulários, arquivos e cobrança. Alguns erros acrescentam campos próprios ao lado de error:

SituaçãoCorpo
422 de validação (helper UnprocessableEntity){"error": "name_required"}, e opcionalmente "message" com o detalhe
429 do token pessoal{"error": "rate_limited", "limit": 60, "count": 61, "retry_after": 12}
429 de mutações em rotas de equipe{"error": "rate_limited", "retry_after": 12}
429 de bloqueio por IP no serviço de autenticação{"error": "ip_blocked", "retry_after_seconds": 300}
503 do limitador indisponível (modo fail-closed){"error": "rate_limiter_unavailable"}
409 de campo personalizado em uso{"error": "field_has_values"}

Os endpoints de OAuth (/oauth/token, /oauth/revoke, /oauth/introspect) seguem o formato de erro do OAuth 2 (RFC 6749), com error em código curto como rate_limited; veja Autenticação.

O texto de error não é um código estável

O valor de error é uma de duas coisas: um slug (board_not_found, name_required, field_has_values) ou uma frase curta em inglês (task not found, missing team membership, invalid body). Slugs tendem a ser estáveis; frases podem ser reescritas sem aviso. Por isso:

  • ramifique por status HTTP (e, no máximo, por slugs que a referência cite);
  • nunca faça error.includes("not found") para decidir fluxo;
  • mostre ao usuário uma mensagem sua, e guarde o error original no log.

Em alguns handlers mais antigos do serviço principal, uma falha de banco ainda pode chegar como 500 com o texto cru do driver em error. Não dependa disso, não exiba ao usuário e não o interprete: o serviço de workspace já devolve apenas internal error e registra o detalhe no servidor.

Mapa de status

StatusSignificadoExemplos reais de errorO que fazer
400Requisição malformada: JSON inválido, id inválido, parâmetro inválidoinvalid body, name required, invalid teamId, invalid before, at least one scope requiredCorrija a requisição. Não repita igual.
401Sem credencial válida: token ausente, expirado, revogado ou sessão encerradaunauthorizedRenove ou recrie a credencial. Não repita igual.
403Autenticado, mas sem permissãomissing team membership, insufficient team role: requires admin, scope required: me:read, api key not accepted on this routeFalta papel, escopo ou participação. Repetir não ajuda.
404Recurso (ou rota) não existe, ou não é visível para vocêtask not found, team not found, board_not_found, no draftVeja a seção seguinte.
409Conflito com o estado atualslug already taken, an export is already queued or running for this scope, field_has_valuesResolva o conflito; reler o recurso costuma ser o primeiro passo.
413Corpo grande demaisbody too largeReduza o corpo (ou envie arquivos pelo fluxo de upload).
422JSON válido, mas regra de negócio violadaname_required, invalid_rangeCorrija o dado. Não repita igual.
429Limite de requisiçõesrate_limitedEspere Retry-After. Veja Limites de taxa.
500Falha internainternal errorPode repetir com backoff, só se a operação for segura (abaixo).
503Dependência indisponível ou limitador em fail-closedrate_limiter_unavailable, guarda de idempotência indisponível; tente de novoRepita com backoff; respeite Retry-After quando houver.

Dois detalhes que costumam confundir. Primeiro, o serviço de calendário às vezes escreve a mensagem em português (cursor inválido), porque o texto humano varia por serviço. Segundo, autorização negada nem sempre é 403: para não revelar a existência de recursos de outras equipes, várias rotas respondem 404 quando o recurso existe mas pertence a outro tenant.

O 404 de recurso singular e o 404 de rota

Um 404 tem dois significados que o cliente precisa separar.

404 por contrato. Algumas rotas que devolvem um recurso por escopo respondem 404 com um slug quando o recurso simplesmente ainda não foi criado. É parte do desenho, não uma falha. Dois casos reais:

  • GET/teams/{teamId}/timelines/{timelineId}/board responde 404 com {"error": "board_not_found"} quando o canal ainda não tem quadro. O fluxo certo é oferecer “criar quadro” e chamar o POST do mesmo caminho, que é idempotente: devolve 201 ao criar e 200 com o quadro existente se ele já estava lá.
  • GET/teams/{teamId}/timelines/{timelineId}/draft responde 404 com {"error": "no draft"} quando não há rascunho salvo para aquele canal (e parent_post_id). Trate como “vazio”, não como erro.

404 de rota. Um caminho que a API não conhece (erro de digitação, versão errada, serviço errado) também dá 404. Nos serviços principal e de workspace não há tratador de 404 customizado: a resposta vem do roteador, normalmente em texto simples (404 page not found), sem JSON.

Na prática, o cliente deve tratar assim:

const res = await fetch(url, { headers });
if (res.status === 404) {
  const type = res.headers.get('content-type') ?? '';
  if (!type.includes('application/json')) {
    throw new Error(`rota inexistente: ${url}`); // bug seu, não repita
  }
  const { error } = await res.json();
  if (error === 'board_not_found') return null; // recurso singular ainda não criado
  throw new Error(`recurso não encontrado: ${error}`);
}

Rastreio: X-Trace-Id

O serviço principal devolve em toda resposta o cabeçalho X-Trace-Id. Se você enviar um X-Trace-Id seu (até 128 caracteres, sem aspas, barra invertida nem quebra de linha), ele é preservado; caso contrário, o servidor gera um UUID. O valor aparece nos logs do servidor e é o que o suporte usa para achar a linha exata de uma falha. Guarde-o junto de qualquer 5xx que você registrar ou reportar.

curl -sS -i https://api.axuz.net/me/summary \
  -H "Authorization: Bearer $AXUZ_TOKEN" \
  -H "X-Trace-Id: meu-job-2026-10-22-0001" | grep -i '^x-trace-id'

O serviço de workspace correlaciona os erros internos pelo identificador de requisição do roteador, mas não está documentado que ele o devolva em um cabeçalho de resposta; não conte com isso.

Repetir com segurança

Regra geral: repita apenas quando a falha for transitória e a operação for idempotente.

  • Sempre seguro repetir: GET, HEAD, PUT e DELETE (o resultado final é o mesmo; uma segunda repetição de DELETE pode devolver 404 porque o recurso já saiu, e isso conta como sucesso), nos status 429, 500, 502, 503, 504 e em falha de rede, com backoff exponencial e jitter.
  • Nunca repita sem pensar: POST que cria algo. Se a conexão cair depois de enviar, você não sabe se o servidor processou. Nas rotas que aceitam o cabeçalho Idempotency-Key, envie uma chave única por operação; a repetição devolve a mesma resposta com Idempotent-Replay: true. A mesma chave com outro corpo dá 422, e a mesma chave ainda em processamento dá 409. Rotas sem esse suporte devem ser reconciliadas lendo o estado (por exemplo, listando antes de recriar).
  • Nunca repita: 400, 401, 403, 404, 409, 413 e 422. A mesma requisição produzirá a mesma resposta.
import random
import time

RETRY_STATUS = {429, 500, 502, 503, 504}

def backoff(attempt: int, retry_after: float | None) -> float:
    """Espera: respeita Retry-After; senão exponencial com jitter total."""
    if retry_after is not None:
        return retry_after + random.uniform(0, 0.5)
    return random.uniform(0, min(30, 0.5 * 2 ** attempt))

O exemplo completo, com leitura do Retry-After, está em Limites de taxa.

Atualizado em