Profile Server API

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: Authorization: Bearer <jwt> em /api/*. Em /service/*, o Bearer do Keycloak ou o par X-Api-Key + X-Username — ver autenticação.
  • Content-Type: application/json em requisições com corpo.
  • Datas: ISO 8601 em UTC (ex.: 2026-07-10T12:00:00.000Z).
  • IDs: UUID v4.

Códigos de status

StatusQuando ocorre
200Leitura/edição bem-sucedida.
201Criação bem-sucedida (POST).
204Remoção bem-sucedida (DELETE), sem corpo.
400Falha de validação (Zod) ou regra de negócio violada.
401Token ausente, inválido ou expirado.
403Autenticado, mas sem a permissão exigida (NOT_ALLOWED).
404Recurso não encontrado.
409Conflito de unicidade ou de estado (ex.: federalTaxId já existente).
413Arquivo acima do tamanho máximo aceito no upload.
500Falha interna (inclui falha de storage no upload/download).
503Dependê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"
}
CampoDescrição
statusCodeStatus HTTP da resposta.
codeContrato estável — identifica a falha e nunca muda depois de publicado.
messageTexto legível em inglês, para logs e consumidores sem tradução própria.
pathRota que originou o erro.
timestampMomento da falha, ISO 8601 em UTC.
detailsSó 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.

codeStatusQuando
VALIDATION_ERROR400Corpo ou query reprovados pelo schema Zod.
BAD_REQUEST400Requisição inválida sem regra de domínio própria.
UNAUTHORIZED401Token ausente, inválido ou expirado.
FORBIDDEN403Sem permissão (UMA/scope).
NOT_ALLOWED403Sem a role de persona exigida pelo endpoint.
NOT_FOUND404Rota inexistente.
RESOURCE_NOT_FOUND404Recurso genérico não encontrado.
CONFLICT409Conflito sem regra de domínio própria.
PAYLOAD_TOO_LARGE413Corpo acima do limite, sem regra de domínio própria.
INTERNAL_ERROR500Falha inesperada.
SERVICE_UNAVAILABLE503Dependê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:

codeStatusSignificado
USER_NOT_FOUND404Usuário inexistente.
USER_ALREADY_EXISTS409Já existe usuário com esse email/federalTaxId.
USER_DATA_MISMATCH409federalTaxId e email pertencem a usuários diferentes.
IDENTITY_ALREADY_LINKED409Identidade do Keycloak já vinculada a outro usuário.
IDENTITY_PROVIDER_UNAVAILABLE503Keycloak indisponível.
CUSTOMER_NOT_FOUND404Customer inexistente.
CUSTOMER_ALREADY_EXISTS409Já existe customer com esses dados.
APP_NOT_FOUND404App inexistente.
APP_ALREADY_EXISTS409Já existe app com esse nome.
TENANT_NOT_FOUND404Tenant inexistente.
USER_TENANT_NOT_FOUND404Usuário não é membro ativo desse tenant.
API_KEY_EXPIRES_AT_IN_THE_PAST400Data de expiração da chave de API já passou.
SERVICE_USERNAME_REQUIRED401X-Username ausente ou inválido em /service/* (obrigatório nos dois modos).
INVALID_API_KEY401Chave de API desconhecida ou revogada (/service/*).
API_KEY_EXPIRED401Chave de API expirada (/service/*).
TENANT_INACTIVE403Tenant da credencial está inativo (/service/*).
SERVICE_USER_NOT_FOUND403E-mail de X-Username não existe ou usuário removido.
SERVICE_USER_NOT_IN_TENANT403Usuário sem vínculo ativo (ou sem persona) no tenant da credencial.
ORGANIZATION_NOT_LINKED_TO_TENANT400Uma ou mais organizations não estão vinculadas a esse tenant.
NOT_ALLOWED_ORGANIZATION403Organização fora do escopo de cooperativas do usuário (write).
ORGANIZATION_NOT_FOUND404Organização inexistente.
ORGANIZATION_ALREADY_EXISTS409Já existe organização com esse federalTaxId.
ORGANIZATION_ADDRESS_NOT_FOUND404Endereço da organização inexistente.
ORGANIZATION_ADDRESS_NOT_CURRENT400O endereço não é o vigente da organização.
ORGANIZATION_CONTACT_NOT_FOUND404Contato da organização inexistente.
STATE_NOT_FOUND404Estado inexistente.
STATE_ALREADY_EXISTS409Estado já cadastrado.
CITY_NOT_FOUND404Cidade inexistente.
CITY_ALREADY_EXISTS409Cidade já cadastrada (código IBGE ou SIAFI).
CITY_STATE_MISMATCH400A cidade não pertence ao estado informado.
DOCUMENT_NOT_FOUND404Documento inexistente.
DOCUMENT_ALREADY_VALID409Já há documento vigente sem vencimento desse tipo — não admite renovação.
DOCUMENT_PENDING_REVIEW_EXISTS409Já há documento desse tipo aguardando análise.
DOCUMENT_RENEWAL_EXPIRATION_NOT_LATER409A renovação não vence depois do documento vigente.
DOCUMENT_INVALID_REVIEW_STATUS400Status de revisão diferente de APPROVED/REJECTED.
DOCUMENT_NOT_PENDING_REVIEW409Só documento pendente é revisado; este já foi decidido.
DOCUMENT_INACTIVE409Documento inativo (substituído ou com alerta desligado) não é editado nem revisado.
DOCUMENT_ALREADY_INACTIVE409O documento já está inativo.
DOCUMENT_ALREADY_ACTIVE409O documento já está ativo.
DOCUMENT_NOT_EXPIRED409Só documento aprovado e vencido pode ter o alerta desligado.
DOCUMENT_TYPE_REQUIRED_CANNOT_DEACTIVATE409Documento de tipo obrigatório não pode ter o alerta desligado.
DOCUMENT_ACTIVE_VERSION_EXISTS409A organização já tem outro documento ativo desse tipo e status — reativar deixaria dois.
DOCUMENT_EXPIRATION_DATE_REQUIRED400O tipo exige validade e ela não foi informada.
DOCUMENT_AUTHENTICITY_CODE_REQUIRED400O tipo exige código de autenticidade e ele não foi informado.
DOCUMENT_COMPLIANCE_TENANT_REQUIRED400Painel de pendências pedido pela persona global sem tenant escolhido (X-Tenant-Id).
DOCUMENT_TYPE_NOT_FOUND404Tipo de documento inexistente, ou restrito a outro tenant.
DOCUMENT_TYPE_ALREADY_EXISTS409Já existe tipo com esse nome no escopo (tenant ou global).
DOCUMENT_TYPE_TENANT_REQUIRED400Pediu tipo restrito sem tenant ativo na request.
DOCUMENT_TYPE_INACTIVE400Tipo de documento inativo.
DOCUMENT_TYPE_INVALID_EXPIRATION400defaultValidityDays preenchido com hasExpiration=false.
DOCUMENT_TYPE_IN_USE409Tipo de documento em uso por um ou mais documentos — não pode ser removido.
ATTACHMENT_NOT_FOUND404Attachment inexistente.
INVALID_ATTACHMENT_TYPE400Tipo de arquivo não permitido.
ATTACHMENT_TOO_LARGE413Arquivo acima de ATTACHMENT_MAX_SIZE_MB (10 MB por padrão).
FILE_UPLOAD_FAILED500Falha ao enviar o arquivo ao storage.
FILE_DOWNLOAD_FAILED500Falha ao baixar o arquivo do storage.
FILE_REMOVE_FAILED500Falha ao remover o arquivo do storage.

Paginação

Endpoints de listagem (GET de coleção) aceitam os query params:

ParamTipoPadrãoRegras
pagenumber1inteiro, mín. 1
limitnumber20inteiro, 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.