Skip to main content

Convite de usuário — API

Cria, inspeciona, aceita, reenvia e revoga um convite por e-mail que atribui um ou mais perfis (roles) a um usuário — novo ou já existente — em uma unidade organizacional. É o ponto de entrada para criar conta por convite (em vez de cadastro direto por Gerenciar usuário ou Usuário por tenant). Um worker de fundo expira automaticamente convites pendentes vencidos.

Funcionamento​

  1. Criar (POST /invitations): exige que o ator tenha, na unidade do convite, user:write e role:manage. Recusa se já existe um convite pendente para o mesmo e-mail normalizado (unicidade garantida também por índice parcial no banco). Determina o modo do convite comparando o e-mail com o diretório de usuários ativos: new_account (não existe perfil ativo com esse e-mail — o aceite vai criar a conta) ou profile_grant (já existe um usuário ativo com esse e-mail — o aceite só atribui os perfis, sem pedir senha). Envia um e-mail com um token de uso único.
  2. Inspecionar (GET /invitations/accept/:token): rota pública, com rate limit; devolve modo, status e o e-mail mascarado (a***@dominio.com) — nunca o e-mail completo. Um convite pendente cujo prazo já passou é reportado como expired mesmo antes do worker de expiração rodar.
  3. Aceitar (POST /invitations/accept/:token): rota pública, com rate limit mais restrito.
    • Convite inexistente, revogado ou expirado → recusado.
    • Convite já aceito → idempotente: devolve o mesmo invitationId/userId com already_accepted: true, sem duplicar nada.
    • Convite pending: se o modo é new_account, display_name e password são obrigatórios e uma conta nova é criada (com e-mail já marcado como verificado, já que veio de convite); se é profile_grant, esses dois campos não são necessários. Em ambos os casos, cada role_id do convite é atribuído ao usuário (na unidade do convite, ou no tenant, se o convite não tinha unidade).
  4. Reenviar (POST /invitations/:invitationId/resend): só convites pendentes e não vencidos; respeita um cooldown configurável desde o último reenvio/criação — reenviar antes do cooldown terminar é recusado. Gera um novo token (o anterior deixa de valer) e uma nova data de expiração.
  5. Revogar (DELETE /invitations/:invitationId): só convites pendentes; muda o status para revoked e grava revokedAt.
  6. Reenviar e revogar são escopados às unidades onde o ator tem as duas permissões (user:write e role:manage); um administrador de plataforma vê/gerencia qualquer convite.
  7. Worker de expiração (IdentityInvitationExpirationWorker, a cada 60s): move todo convite pending cujo prazo já passou para expired, com evento de auditoria — mesmo que ninguém tenha consultado ou tentado aceitá-lo.

Endpoints​

MétodoRotaDescrição
POST/v1/invitationsCria um convite
GET/v1/invitations/accept/:tokenInspeciona um convite antes de aceitar (público)
POST/v1/invitations/accept/:tokenAceita o convite e materializa a conta/perfis (público)
POST/v1/invitations/:invitationId/resendReenvia um convite pendente
DELETE/v1/invitations/:invitationIdRevoga um convite pendente

Versão: v1

Swagger: Identity — Invitations · Rota (Dev): http://localhost:3000/v1/invitations

Fluxo completo, do convite ao aceite:

Permissões​

RotaGuardsPermissão / escopo exigido
POST /invitationsIdentityOAuthAccessTokenGuard, AuthorizationGuarduser:write e role:manage, escopo unit (unit_id do corpo)
GET/POST /invitations/accept/:tokennenhum (público)posse do token do convite
POST /invitations/:invitationId/resendIdentityOAuthAccessTokenGuarduser:write e role:manage em alguma unidade que alcance o convite (ou platform-admin)
DELETE /invitations/:invitationIdIdentityOAuthAccessTokenGuardidem

Headers​

HeaderObrigatórioDescrição
AuthorizationSim (criar/reenviar/revogar)Bearer <access_token>
Content-TypeSim (POST)application/json

Path parameters​

NomeTipoObrigatórioDescrição
tokenstringSim (GET/POST accept)token de convite recebido por e-mail (1–512 caracteres)
invitationIduuidSim (resend/DELETE)identificador do convite

Body​

POST /invitations — IdentityInvitationCreateRequest:

json
{
"email": "novo.usuario@exemplo.com",
"role_ids": ["8e061c1d-b2fa-4f55-92ee-54208e152f90"],
"unit_id": "0192f3c6-44e8-7a32-a6ef-dad4679fe83b"
}
CampoTipoObrigatórioValidação
emailstringSimformato de e-mail
role_idsuuid[]Simao menos 1 item; cada perfil deve existir e estar ativo
unit_iduuidSimunidade onde o(s) perfil(is) serão atribuídos

POST /invitations/accept/:token — IdentityInvitationAcceptRequest:

json
{ "display_name": "Novo Usuário", "password": "SenhaForte123!", "client_id": "web-portal" }
CampoTipoObrigatórioValidação
display_namestringSim se mode: new_account1–255 caracteres
passwordstringSim se mode: new_account8–128 caracteres
client_idstringSim1–128 caracteres

Response​

200 — criação (IdentityInvitationResponse):

json
{ "id": "8e061c1d-...-52f90", "expires_at": "2026-08-27T12:00:00.000Z", "mode": "new_account", "status": "pending" }

200 — inspeção (sem id, com masked_email):

json
{ "masked_email": "n***@exemplo.com", "mode": "new_account", "status": "pending", "expires_at": "2026-08-27T12:00:00.000Z" }

200 — aceite (IdentityInvitationAcceptanceResponse):

json
{ "invitation_id": "8e061c1d-...-52f90", "user_id": "8f2a...-uuid", "already_accepted": false }

200 — reenvio/revogação (IdentityInvitationAcknowledgementResponse):

json
{ "message": "Invitation resent." }

Erros​

Classe de erroerrorCodeStatusQuando ocorre
ForbiddenActionIDENTITY_INVITATION_FORBIDDEN403ator sem user:write e role:manage combinados na unidade exigida
IdentityInvitationUnavailableErrorIDENTITY_INVITATION_UNAVAILABLE400e-mail já com convite pendente; perfil inexistente/inativo; token inexistente/revogado/expirado; aceite sem senha/nome quando exigido; reenvio fora da janela (não pendente, vencido ou em cooldown); revogação de convite não pendente
— (validação de payload)BAD_REQUEST400corpo inválido (e-mail malformado, role_ids vazio etc.)
—RATE_LIMIT_EXCEEDED429limite de requisições excedido em GET/POST accept
—IDENTITY_INVALID_OAUTH_ACCESS_TOKEN401token ausente/inválido nas rotas autenticadas

Regras de negócio​

IDRegraComportamento esperado
RN-01Só um convite pendente por e-mailcriar um segundo convite para o mesmo e-mail enquanto o primeiro está pendente é recusado
RN-02Modo é decidido na criação, pelo estado do e-mail naquele instantenew_account se não há usuário ativo com esse e-mail; profile_grant caso contrário
RN-03Aceitar um convite já aceito é idempotentenão recria conta nem duplica atribuição de perfil; devolve o mesmo resultado da primeira vez
RN-04E-mail nunca aparece completo antes do aceiteGET/inspeção só devolve e-mail mascarado
RN-05Conta criada por convite já nasce com e-mail verificadodispensa o fluxo de verificação de e-mail do primeiro acesso
RN-06Reenvio respeita um cooldownreenviar antes do intervalo configurado terminar é recusado, mesmo que o convite continue pendente
RN-07Expiração é ativa, não só "lida sob demanda"um worker de fundo marca expired os convites vencidos independentemente de qualquer consulta
RN-08Revogação só se aplica a convite pendenteconvite já aceito, expirado ou revogado não pode ser revogado de novo

Compliance​

Órgão / normaExigênciaComo a rota atende
LGPDminimização na exposição públicae-mail sempre mascarado antes do aceite; token de uso único, não reaproveitável
HIPAA/ANVISA (indireto)rastreabilidade de concessão de acessotoda criação/aceite/reenvio/revogação/expiração gera evento de auditoria com o ator (ou identity-scheduler para expiração automática)

Variáveis de ambiente​

VariávelUsoObrigatória
Config identity.invitation.tokenTtlInHoursvalidade do convite (criação e reenvio)Sim
Config identity.invitation.resendCooldownInMinutesintervalo mínimo entre reenviosSim
Config identity.invitation.acceptUrlbase da URL enviada no e-mail de conviteSim

Requisitos não funcionais​

RequisitoDefinição
IdempotênciaAceitar um convite já aceito é idempotente; criar/reenviar/revogar não são
Rate limitGET accept/:token: 20/60s · POST accept/:token: 10/60s
AuditoriaSim — identity.invitation.created, .accepted, .resent, .revoked, .expired

Relacionado​

  • 🖥️ Tela: Aceitar convite por e-mail — cobre GET/POST /v1/invitations/accept/:token. Criar, reenviar e revogar convite não têm tela nesta branch.
  • 📂 Módulo: Usuário
  • 👥 Usuário por tenant — via de criar usuário sem passar por convite
  • 🔑 Login — a conta criada aqui autentica pelo fluxo padrão de login