Erros e convenções
Códigos de status, formato de erro, paginação e cabeçalhos comuns.
Convenções gerais
- Base path: rotas de usuário sob
/api, rotas de serviço sob/service. - Autenticação: sempre
Authorization: Bearer <jwt>. - Content-Type:
application/jsonem requisições com corpo. - Datas: ISO 8601 em UTC (ex.:
2026-07-10T12:00:00.000Z). - IDs: UUID v4.
Códigos de status
| Status | Quando ocorre |
|---|---|
200 | Leitura/edição bem-sucedida. |
201 | Criação bem-sucedida (POST). |
204 | Remoção bem-sucedida (DELETE), sem corpo. |
400 | Falha de validação (Zod) ou regra de negócio violada. |
401 | Token ausente, inválido ou expirado. |
403 | Autenticado, mas sem a permissão exigida (NOT_ALLOWED). |
404 | Recurso não encontrado. |
409 | Conflito de unicidade ou de estado (ex.: federalTaxId já existente). |
500 | Falha interna (inclui falha de storage no upload/download). |
503 | Dependência externa indisponível (ex.: Keycloak). |
Formato de erro
Toda falha da API responde o mesmo envelope, com um code estável:
{
"statusCode": 409,
"code": "DOCUMENT_ALREADY_VALID",
"message": "A valid document of this type already exists",
"path": "/api/documents",
"timestamp": "2026-07-17T12:00:00.000Z"
}| Campo | Descrição |
|---|---|
statusCode | Status HTTP da resposta. |
code | Contrato estável — identifica a falha e nunca muda depois de publicado. |
message | Texto legível em inglês, para logs e consumidores sem tradução própria. |
path | Rota que originou o erro. |
timestamp | Momento da falha, ISO 8601 em UTC. |
details | Só em VALIDATION_ERROR: lista de { path, message } por campo inválido. |
Trate sempre o code, nunca o message — o texto pode mudar, o código não.
O painel usa o code para escolher a mensagem exibida ao usuário, com
fallback para o message quando o código for desconhecido.
Erro de validação (400) traz o detalhamento por campo em details:
{
"statusCode": 400,
"code": "VALIDATION_ERROR",
"message": "Validation failed",
"path": "/api/users",
"timestamp": "2026-07-17T12:00:00.000Z",
"details": [
{ "path": "email", "message": "Invalid email" },
{ "path": "name", "message": "Too small: expected string to have >=1 characters" }
]
}Falha inesperada responde 500 com code: "INTERNAL_ERROR" e mensagem
genérica — o erro real (com stack) fica só no log do servidor.
Códigos genéricos
Não vêm de uma regra de negócio específica; são produzidos pelo filtro global.
code | Status | Quando |
|---|---|---|
VALIDATION_ERROR | 400 | Corpo ou query reprovados pelo schema Zod. |
BAD_REQUEST | 400 | Requisição inválida sem regra de domínio própria. |
UNAUTHORIZED | 401 | Token ausente, inválido ou expirado. |
FORBIDDEN | 403 | Sem permissão (UMA/scope). |
NOT_ALLOWED | 403 | Sem a role de persona exigida pelo endpoint. |
NOT_FOUND | 404 | Rota inexistente. |
RESOURCE_NOT_FOUND | 404 | Recurso genérico não encontrado. |
CONFLICT | 409 | Conflito sem regra de domínio própria. |
INTERNAL_ERROR | 500 | Falha inesperada. |
SERVICE_UNAVAILABLE | 503 | Dependência externa indisponível. |
Códigos de domínio
O code de cada endpoint está listado na tabela Erros da respectiva página
em Endpoints. Referência completa:
code | Status | Significado |
|---|---|---|
USER_NOT_FOUND | 404 | Usuário inexistente. |
USER_ALREADY_EXISTS | 409 | Já existe usuário com esse email/federalTaxId. |
USER_DATA_MISMATCH | 409 | federalTaxId e email pertencem a usuários diferentes. |
IDENTITY_ALREADY_LINKED | 409 | Identidade do Keycloak já vinculada a outro usuário. |
IDENTITY_PROVIDER_UNAVAILABLE | 503 | Keycloak indisponível. |
CUSTOMER_NOT_FOUND | 404 | Customer inexistente. |
CUSTOMER_ALREADY_EXISTS | 409 | Já existe customer com esses dados. |
APP_NOT_FOUND | 404 | App inexistente. |
APP_ALREADY_EXISTS | 409 | Já existe app com esse nome. |
TENANT_NOT_FOUND | 404 | Tenant inexistente. |
USER_TENANT_NOT_FOUND | 404 | Usuário não é membro ativo desse tenant. |
ORGANIZATION_NOT_LINKED_TO_TENANT | 400 | Uma ou mais organizations não estão vinculadas a esse tenant. |
NOT_ALLOWED_ORGANIZATION | 403 | Organização fora do escopo de cooperativas do usuário (write). |
ORGANIZATION_NOT_FOUND | 404 | Organização inexistente. |
ORGANIZATION_ALREADY_EXISTS | 409 | Já existe organização com esse federalTaxId. |
ORGANIZATION_ADDRESS_NOT_FOUND | 404 | Endereço da organização inexistente. |
ORGANIZATION_ADDRESS_NOT_CURRENT | 400 | O endereço não é o vigente da organização. |
ORGANIZATION_CONTACT_NOT_FOUND | 404 | Contato da organização inexistente. |
STATE_NOT_FOUND | 404 | Estado inexistente. |
STATE_ALREADY_EXISTS | 409 | Estado já cadastrado. |
CITY_NOT_FOUND | 404 | Cidade inexistente. |
CITY_ALREADY_EXISTS | 409 | Cidade já cadastrada (código IBGE ou SIAFI). |
CITY_STATE_MISMATCH | 400 | A cidade não pertence ao estado informado. |
DOCUMENT_NOT_FOUND | 404 | Documento inexistente. |
DOCUMENT_ALREADY_VALID | 409 | Já há documento válido desse tipo para a organização. |
DOCUMENT_PENDING_REVIEW_EXISTS | 409 | Já há documento desse tipo aguardando análise. |
DOCUMENT_INVALID_REVIEW_STATUS | 400 | Status de revisão diferente de APPROVED/REJECTED. |
DOCUMENT_EXPIRATION_DATE_REQUIRED | 400 | O tipo exige validade e ela não foi informada. |
DOCUMENT_AUTHENTICITY_CODE_REQUIRED | 400 | O tipo exige código de autenticidade e ele não foi informado. |
DOCUMENT_TYPE_NOT_FOUND | 404 | Tipo de documento inexistente, ou restrito a outro tenant. |
DOCUMENT_TYPE_ALREADY_EXISTS | 409 | Já existe tipo com esse nome no escopo (tenant ou global). |
DOCUMENT_TYPE_TENANT_REQUIRED | 400 | Pediu tipo restrito sem tenant ativo na request. |
DOCUMENT_TYPE_INACTIVE | 400 | Tipo de documento inativo. |
DOCUMENT_TYPE_INVALID_EXPIRATION | 400 | defaultValidityDays preenchido com hasExpiration=false. |
DOCUMENT_TYPE_IN_USE | 409 | Tipo de documento em uso por um ou mais documentos — não pode ser removido. |
ATTACHMENT_NOT_FOUND | 404 | Attachment inexistente. |
INVALID_ATTACHMENT_TYPE | 400 | Tipo de arquivo não permitido. |
FILE_UPLOAD_FAILED | 500 | Falha ao enviar o arquivo ao storage. |
FILE_DOWNLOAD_FAILED | 500 | Falha ao baixar o arquivo do storage. |
FILE_REMOVE_FAILED | 500 | Falha ao remover o arquivo do storage. |
Paginação
Endpoints de listagem (GET de coleção) aceitam os query params:
| Param | Tipo | Padrão | Regras |
|---|---|---|---|
page | number | 1 | inteiro, mín. 1 |
limit | number | 20 | inteiro, 1 a 100 |
E respondem no envelope:
{
"data": [],
"total": 0,
"totalPages": 0,
"currentPage": 1
}Filtros adicionais (ex.: name, email, type) são específicos de cada
recurso e estão descritos na respectiva página de endpoints.