Pular para o conteúdo principal

Requisição de Webhooks (Webhooks request)

Quando um evento de webhook é disparado, o Logto envia uma requisição POST para cada endpoint inscrito nele. O catálogo completo de eventos está em Eventos de Webhooks; esta página documenta o formato da requisição entregue pelo Logto.

Cabeçalhos da requisição

KeyPersonalizávelNotas
user-agentLogto (https://logto.io/) por padrão.
content-typeapplication/json por padrão.
logto-signature-sha-256Assinatura do corpo da requisição. Veja protegendo seus webhooks.

Cabeçalhos personalizáveis podem ser sobrescritos via a configuração de webhook seguro.

Visão geral do corpo da requisição

O corpo é um objeto JSON. Seu formato exato depende de qual família o evento pertence:

FamíliaEventosQuando é disparado
Fluxo do usuárioPostRegister, PostSignIn, PostResetPasswordUm usuário completa um fluxo de cadastro, login ou redefinição de senha tratado pela Experience API.
Mutação de dadosUser.*, Role.*, Scope.*, Organization.*, OrganizationRole.*, OrganizationScope.*O modelo de dados subjacente é alterado por uma chamada da Management API ou um fluxo de usuário na Experience API.
ExceçãoIdentifier.LockoutUm incidente de segurança, por exemplo, uma conta bloqueada após tentativas consecutivas de verificação falhadas.

Cada família compartilha um pequeno conjunto de campos comuns. Cada família então adiciona seus próprios campos de contexto de requisição, além de um payload específico do evento.

Campos comuns

Presentes em toda entrega, independentemente da família:

CampoTipoOpcionalNotas
hookIdstringO identificador da configuração do webhook no Logto.
eventstringO evento que disparou esta entrega.
createdAtstringO horário de criação do payload no formato ISO 8601.
userAgentstringO user-agent da requisição que disparou o evento.

Cada família também inclui o endereço IP da requisição que disparou o evento, sob o nome de campo userIp para eventos de fluxo do usuário e ip para eventos de mutação de dados e exceção. A semântica é idêntica; a diferença de nome é mantida por compatibilidade retroativa.

Payloads de eventos de fluxo do usuário

Eventos: PostRegister, PostSignIn, PostResetPassword.

Disparado quando um usuário completa um fluxo de cadastro, login ou redefinição de senha tratado pela Experience API. Além dos campos comuns, o corpo carrega:

CampoTipoOpcionalNotas
interactionEvent'SignIn' | 'Register' | 'ForgotPassword'O tipo de evento do fluxo do usuário. Mapeia para PostSignIn / PostRegister / PostResetPassword respectivamente. O nome do campo mantém a nomenclatura histórica "interaction".
sessionIdstringO ID da sessão (não o ID de interação) para este evento, se aplicável.
userIpstringO endereço IP da requisição que disparou o evento.
userIdstringO ID do usuário associado a este evento, se aplicável.
userUserEntityA entidade de usuário associada a este evento, se aplicável.
applicationIdstringO ID do aplicativo associado a este evento, se aplicável.
applicationApplicationEntityA entidade de aplicativo associada a este evento, se aplicável.

Formatos das entidades

type UserEntity = {
id: string;
username?: string;
primaryEmail?: string;
primaryPhone?: string;
name?: string;
avatar?: string;
customData?: object;
identities?: object;
lastSignInAt?: string;
createdAt?: string;
applicationId?: string;
isSuspended?: boolean;
};
enum ApplicationType {
Native = 'Native',
SPA = 'SPA',
Traditional = 'Traditional',
MachineToMachine = 'MachineToMachine',
Protected = 'Protected',
SAML = 'SAML',
}

type ApplicationEntity = {
id: string;
type: ApplicationType;
name: string;
description?: string;
};

Veja Usuários e Aplicativos para a referência completa dos campos.

Payloads de eventos de mutação de dados

Eventos: todo evento sob User.*, Role.*, Scope.*, Organization.*, OrganizationRole.*, OrganizationScope.*. Veja Eventos de Webhooks → Eventos de mutação de dados para o catálogo completo.

O corpo sempre carrega:

  • Os campos comuns.
  • Um campo ip, o endereço IP da requisição que disparou o evento (opcional, presente quando conhecido).
  • Um contexto de API descrevendo como a alteração foi disparada. O contexto é uma de duas variantes dependendo da fonte do gatilho:
  • Um payload específico do evento: a entidade afetada em data e (para alguns eventos) campos adicionais no topo. Veja payloads de dados específicos do evento.

Campos de contexto da Experience API

Presentes quando a alteração foi disparada por um fluxo voltado ao usuário na Experience API, por exemplo User.Created durante o cadastro ou User.Data.Updated durante atualizações de perfil.

CampoTipoOpcionalNotas
interactionEvent'SignIn' | 'Register' | 'ForgotPassword'O tipo de evento do fluxo do usuário que produziu a alteração. O nome do campo mantém a nomenclatura histórica "interaction".
sessionIdstringO ID da sessão (não o ID de interação) para este evento, se aplicável.
applicationIdstringO ID do aplicativo, se aplicável.
applicationApplicationEntityA entidade do aplicativo, se aplicável.

Campos de contexto da Management API

Presentes quando a alteração foi disparada por uma chamada da Management API.

CampoTipoOpcionalNotas
pathstringO caminho da chamada de API que disparou este webhook.
methodstringO método HTTP da chamada de API.
statusnumberO código de status da resposta da chamada de API.
paramsobjectOs parâmetros de caminho koa da chamada de API.
matchedRoutestringA rota koa correspondente. O Logto usa este campo para corresponder filtros de eventos de webhook habilitados.

Payloads de dados específicos do evento

Todo evento de mutação de dados inclui um campo de topo data carregando a entidade afetada, ou null quando a alteração não pode ser resumida como uma única entidade (eventos de exclusão e de associação). Alguns eventos também incluem campos de topo específicos do evento além de data; Organization.Membership.Updated é um desses casos, documentado abaixo.

Eventos de usuário

EventoCampoTipoOpcionalNotas
User.CreateddataUserEntityA entidade de usuário criada.
User.Data.UpdateddataUserEntityA entidade de usuário atualizada.
User.Deleteddatanull/

Eventos de papel (Role)

type Role = {
id: string;
name: string;
description: string;
type: 'User' | 'MachineToMachine';
isDefault: boolean;
};
type Scope = {
id: string;
name: string;
description: string;
resourceId: string;
createdAt: number;
};
EventoCampoTipoOpcionalNotas
Role.CreateddataRoleA entidade de papel criada.
Role.Data.UpdateddataRoleA entidade de papel atualizada.
Role.Deleteddatanull/
Role.Scopes.UpdateddataScope[]Os escopos atualizados atribuídos ao papel.
Role.Scopes.UpdatedroleIdstringO ID do papel ao qual os escopos foram atribuídos. (Disponível apenas quando o evento foi disparado pela criação de um papel com escopos pré-atribuídos.)

Eventos de permissão (Scope)

EventoCampoTipoOpcionalNotas
Scope.CreateddataScopeA entidade de escopo criada.
Scope.Data.UpdateddataScopeA entidade de escopo atualizada.
Scope.Deleteddatanull/

Eventos de organização

type Organization = {
id: string;
name: string;
description?: string;
customData: object;
createdAt: number;
};
EventoCampoTipoOpcionalNotas
Organization.CreateddataOrganizationA entidade de organização criada.
Organization.Data.UpdateddataOrganizationA entidade de organização atualizada.
Organization.Deleteddatanull/
Organization.Membership.Updateddatanull/A alteração é descrita por arrays delta opcionais no topo. Veja Payload de Organization.Membership.Updated abaixo.
Payload de Organization.Membership.Updated

Além dos campos comuns e dos campos de contexto de API que se aplicam à fonte do gatilho (Contexto da Management API para rotas da Management API, Contexto da Experience API para provisionamento just-in-time), o evento Organization.Membership.Updated carrega um organizationId mais arrays delta opcionais no topo do payload (ao lado de event, createdAt, etc., não dentro de data, que é sempre null para este evento).

CampoTipoOpcionalNotas
organizationIdstringA organização cuja associação foi alterada.
addedUserIdsstring[]IDs de usuários recém-adicionados por este gatilho. Omitido quando nenhum usuário foi adicionado, ou quando o gatilho não afeta associação de usuários.
removedUserIdsstring[]IDs de usuários removidos por este gatilho. Omitido quando nenhum usuário foi removido.
addedApplicationIdsstring[]IDs de aplicativos recém-adicionados. Omitido quando nenhum aplicativo foi adicionado, ou quando o gatilho não afeta associação de aplicativos.
removedApplicationIdsstring[]IDs de aplicativos removidos. Omitido quando nenhum aplicativo foi removido.

Os quatro arrays delta são opcionais e aditivos: eles não alteram o formato do payload existente para consumidores que não os esperam, e o campo legado data: null ainda é emitido sem alterações.

Gatilhos e quais campos delta eles podem emitir
GatilhoCampos delta possíveis
POST /organizations/:id/usersaddedUserIds
PUT /organizations/:id/usersaddedUserIds, removedUserIds
DELETE /organizations/:id/users/:userIdremovedUserIds
POST /organizations/:id/applicationsaddedApplicationIds
PUT /organizations/:id/applicationsaddedApplicationIds, removedApplicationIds
DELETE /organizations/:id/applications/:applicationIdremovedApplicationIds
PUT /organization-invitations/:id/status (Accepted)addedUserIds
Provisionamento just-in-time ao adicionar o usuário a uma nova organizaçãoaddedUserIds
Deltas vazios são omitidos (ausente ≠ alteração vazia)

Arrays delta vazios são omitidos completamente do payload. Por exemplo, um PUT /organizations/:id/users que substitui o conjunto de membros pelo conjunto já existente não produz alteração real, e o payload se reduz a apenas { organizationId } com todos os quatro campos delta ausentes. O mesmo se aplica a uma re-adicionamento de um membro já existente e a uma reaceitação de convite por um usuário que já é membro.

Consumidores devem tratar um campo ausente como "sem alteração desse lado", e não como "uma alteração vazia".

Limite por array (truncamento silencioso)

Cada array delta é limitado a 5000 entradas. Quando uma única chamada da Management API adiciona ou remove mais de 5000 usuários (ou aplicativos) em uma operação, o array delta correspondente é truncado silenciosamente para suas primeiras 5000 entradas. Não há marcador no payload indicando que o limite foi atingido.

Se seu aplicativo realiza operações administrativas em massa que podem afetar mais de 5000 membros em uma chamada, trate um array com exatamente 5000 entradas como um sinal para reconciliar a associação autoritativa via Management API:

  • GET /organizations/:id/users: associação completa de usuários.
  • GET /organizations/:id/applications: associação completa de aplicativos.

Isso segue o mesmo padrão do evento push do GitHub, que limita commits a 20 entradas e aponta consumidores para a compare API para a lista completa.

Pulando eventos sem alteração (no-op)

Para pular entregas sem alteração (eventos sem campos delta) do lado do consumidor, filtre pela presença dos arrays delta:

if (
payload.addedUserIds?.length ||
payload.removedUserIds?.length ||
payload.addedApplicationIds?.length ||
payload.removedApplicationIds?.length
) {
// alteração real de associação, trate aqui
}

?.length é falsy tanto para undefined quanto para [], então o mesmo predicado é robusto se o campo estiver ausente ou (em algum futuro hipotético) emitido como array vazio.

Exemplos de payloads

Adicionar um usuário (POST /organizations/:id/users):

{
"event": "Organization.Membership.Updated",
"organizationId": "org_abc",
"addedUserIds": ["u_001"]
}

Substituir o conjunto de membros (PUT /organizations/:id/users):

{
"event": "Organization.Membership.Updated",
"organizationId": "org_abc",
"addedUserIds": ["u_002"],
"removedUserIds": ["u_001"]
}

Remover um usuário (DELETE /organizations/:id/users/:userId):

{
"event": "Organization.Membership.Updated",
"organizationId": "org_abc",
"removedUserIds": ["u_001"]
}

Adicionar um aplicativo (POST /organizations/:id/applications):

{
"event": "Organization.Membership.Updated",
"organizationId": "org_abc",
"addedApplicationIds": ["app_xyz"]
}

Re-adicionar um membro já existente, PUT sem alteração, ou reaceitação de convite de quem já é membro (sem alteração real):

{
"event": "Organization.Membership.Updated",
"organizationId": "org_abc"
}

Operação em massa que atinge o limite de 5000 (truncado silenciosamente):

{
"event": "Organization.Membership.Updated",
"organizationId": "org_abc",
"removedUserIds": ["u_0001", "u_0002", "/* … exatamente 5000 entradas */"]
}

Ver um array com exatamente 5000 entradas deve indicar a necessidade de reconciliar via GET /organizations/:id/users (ou /applications).

Eventos de papel de organização

type OrganizationRole = {
id: string;
name: string;
description?: string;
};
type OrganizationScope = {
id: string;
name: string;
description?: string;
};
EventoCampoTipoOpcionalNotas
OrganizationRole.CreateddataOrganizationRoleA entidade de papel de organização criada.
OrganizationRole.Data.UpdateddataOrganizationRoleA entidade de papel de organização atualizada.
OrganizationRole.Deleteddatanull/
OrganizationRole.Scopes.Updateddatanull/
OrganizationRole.Scopes.UpdatedorganizationRoleIdstringO ID do papel ao qual os escopos foram atribuídos. (Disponível apenas quando o evento foi disparado pela criação de um papel com escopos pré-atribuídos.)

Eventos de permissão de organização (scope)

EventoCampoTipoOpcionalNotas
OrganizationScope.CreateddataOrganizationScopeA entidade de escopo de organização criada.
OrganizationScope.Data.UpdateddataOrganizationScopeA entidade de escopo de organização atualizada.
OrganizationScope.Deleteddatanull/

Payloads de eventos de exceção

Eventos: Identifier.Lockout.

Disparado em incidentes de segurança, por exemplo, uma conta bloqueada após tentativas consecutivas de verificação falhadas. Esses eventos sempre se originam de um fluxo voltado ao usuário, então o corpo carrega:

enum SignInIdentifier {
Email = 'email',
Phone = 'phone',
Username = 'username',
}
CampoTipoOpcionalNotas
typeSignInIdentifierO tipo de identificador do usuário, por exemplo, email, telefone ou nome de usuário.
valuestringO valor do identificador do usuário que disparou o bloqueio.