Solicitação de Webhooks
Quando um evento de webhook é disparado, o Logto envia uma solicitaçã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 solicitação que o Logto entrega.
Cabeçalhos da solicitação
| Chave | Personalizável | Observações |
|---|---|---|
| user-agent | ✅ | Logto (https://logto.io/) por padrão. |
| content-type | ✅ | application/json por padrão. |
| logto-signature-sha-256 | Assinatura do corpo da solicitação. Veja protegendo seus webhooks. |
Cabeçalhos personalizáveis podem ser substituídos através da configuração de webhook seguro.
Visão geral do corpo da solicitação
O corpo é um objeto JSON. Seu formato exato depende de qual família o evento pertence:
| Família | Eventos | Quando é disparado |
|---|---|---|
| Fluxo de usuário | PostRegister, PostSignIn, PostResetPassword | Um usuário completa um fluxo de cadastro, login ou redefinição de senha tratado pela Experience API. |
| Mutação de dados | User.*, Role.*, Scope.*, Organization.*, OrganizationRole.*, OrganizationScope.* | O modelo de dados subjacente é alterado — por uma chamada da Management API ou por um fluxo de usuário na Experience API. |
| Exceção | Identifier.Lockout | Um 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 solicitação, além de uma carga útil específica do evento.
Campos comuns
Presentes em todas as entregas, independentemente da família:
| Campo | Tipo | Opcional | Notas |
|---|---|---|---|
| hookId | string | O identificador de configuração do webhook no Logto. | |
| event | string | O evento que disparou esta entrega. | |
| createdAt | string | O horário de criação da carga útil no formato ISO 8601. | |
| userAgent | string | ✅ | O user-agent da solicitação que disparou o evento. |
Cada família também inclui o endereço IP da solicitação que disparou o evento — sob o nome do campo userIp para eventos de fluxo de usuário e ip para eventos de mutação de dados e exceção. As semânticas são idênticas; a diferença de nome histórica é preservada para compatibilidade retroativa.
Cargas úteis de eventos de fluxo de 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 contém:
| Campo | Tipo | Opcional | Notas |
|---|---|---|---|
| interactionEvent | 'SignIn' | 'Register' | 'ForgotPassword' | O tipo de evento de fluxo de usuário. Mapeia para PostSignIn / PostRegister / PostResetPassword respectivamente. O nome do campo mantém a nomenclatura histórica "interaction". | |
| sessionId | string | ✅ | O ID da Sessão (não o ID de Interação) para este evento, se aplicável. |
| userIp | string | ✅ | O endereço IP da solicitação que disparou o evento. |
| userId | string | ✅ | O ID do Usuário associado a este evento, se aplicável. |
| user | UserEntity | ✅ | A entidade de usuário associada a este evento, se aplicável. |
| applicationId | string | ✅ | O ID do Aplicativo associado a este evento, se aplicável. |
| application | ApplicationEntity | ✅ | A entidade de aplicativo associada a este evento, se aplicável. |
Formatos de entidade
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.
Cargas úteis de eventos de mutação de dados
Eventos: todos os eventos 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 contém:
- Os campos comuns.
- Um campo
ip— o endereço IP da solicitaçã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 disparo:
- Contexto da Experience API — quando a alteração veio de um fluxo voltado para o usuário.
- Contexto da Management API — quando a alteração veio de uma chamada direta da Management API.
- Uma carga útil específica do evento — a entidade afetada em
datae (para alguns eventos) campos adicionais de nível superior. Veja cargas úteis de dados específicas do evento.
Campos de contexto da Experience API
Presentes quando a alteração foi disparada por um fluxo voltado para o usuário na Experience API — por exemplo, User.Created durante o cadastro ou User.Data.Updated durante atualizações de perfil.
| Campo | Tipo | Opcional | Notas |
|---|---|---|---|
| interactionEvent | 'SignIn' | 'Register' | 'ForgotPassword' | ✅ | O tipo de evento de fluxo de usuário que produziu a alteração. O nome do campo mantém a nomenclatura histórica "interaction". |
| sessionId | string | ✅ | O ID da Sessão (não o ID de Interação) para este evento, se aplicável. |
| applicationId | string | ✅ | O ID do Aplicativo, se aplicável. |
| application | ApplicationEntity | ✅ | A entidade de aplicativo, se aplicável. |
Campos de contexto da Management API
Presentes quando a alteração foi disparada por uma chamada da Management API.
| Campo | Tipo | Opcional | Notas |
|---|---|---|---|
| path | string | ✅ | O caminho da chamada de API que disparou este hook. |
| method | string | ✅ | O método HTTP da chamada de API. |
| status | number | ✅ | O código de status da resposta da chamada de API. |
| params | object | ✅ | Os parâmetros de caminho koa da chamada de API. |
| matchedRoute | string | ✅ | A rota correspondente koa. O Logto usa este campo para corresponder filtros de eventos de webhook habilitados. |
Cargas úteis de dados específicas do evento
Todo evento de mutação de dados inclui um campo data de nível superior que carrega a entidade afetada, ou null quando a alteração não pode ser resumida como uma única entidade (eventos de exclusão e associação). Alguns eventos também incluem campos de nível superior específicos do evento além de data — Organization.Membership.Updated é um desses casos, documentado abaixo.
Eventos de usuário
| Evento | Campo | Tipo | Opcional | Notas |
|---|---|---|---|---|
| User.Created | data | UserEntity | A entidade de usuário criada. | |
| User.Data.Updated | data | UserEntity | A entidade de usuário atualizada. | |
| User.Deleted | data | null | / |
Eventos de papel
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;
};
| Evento | Campo | Tipo | Opcional | Notas |
|---|---|---|---|---|
| Role.Created | data | Role | A entidade de papel criada. | |
| Role.Data.Updated | data | Role | A entidade de papel atualizada. | |
| Role.Deleted | data | null | / | |
| Role.Scopes.Updated | data | Scope[] | Os escopos atualizados atribuídos ao papel. | |
| Role.Scopes.Updated | roleId | string | ✅ | O ID do papel ao qual os escopos são 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 (escopo)
| Evento | Campo | Tipo | Opcional | Notas |
|---|---|---|---|---|
| Scope.Created | data | Scope | A entidade de escopo criada. | |
| Scope.Data.Updated | data | Scope | A entidade de escopo atualizada. | |
| Scope.Deleted | data | null | / |
Eventos de organização
type Organization = {
id: string;
name: string;
description?: string;
customData: object;
createdAt: number;
};
| Evento | Campo | Tipo | Opcional | Notas |
|---|---|---|---|---|
| Organization.Created | data | Organization | A entidade de organização criada. | |
| Organization.Data.Updated | data | Organization | A entidade de organização atualizada. | |
| Organization.Deleted | data | null | / | |
| Organization.Membership.Updated | data | null | / | A alteração é descrita por matrizes delta opcionais de nível superior. Veja carga útil de Organization.Membership.Updated abaixo. |
Carga útil de Organization.Membership.Updated
Além dos campos comuns e dos campos de contexto de API correspondentes à origem do gatilho (contexto da Management API para rotas da Management API, contexto da Experience API para o provisionamento just-in-time), o evento Organization.Membership.Updated carrega um organizationId mais matrizes delta opcionais no nível superior da carga útil (ao lado de event, createdAt, etc.; não dentro de data, que é sempre null para este evento).
| Campo | Tipo | Opcional | Notas |
|---|---|---|---|
| organizationId | string | A organização cuja associação foi alterada. | |
| addedUserIds | string[] | ✅ | IDs de usuários recém-adicionados por este disparo. Omitido quando nenhum usuário foi adicionado, ou quando o disparo não afeta a associação de usuários. |
| removedUserIds | string[] | ✅ | IDs de usuários removidos por este disparo. Omitido quando nenhum usuário foi removido. |
| addedApplicationIds | string[] | ✅ | IDs de aplicativos recém-adicionados. Omitido quando nenhum aplicativo foi adicionado, ou quando o disparo não afeta a associação de aplicativos. |
| removedApplicationIds | string[] | ✅ | IDs de aplicativos removidos. Omitido quando nenhum aplicativo foi removido. |
As quatro matrizes delta são opcionais e aditivas — elas não alteram o formato da carga útil existente para consumidores que não as esperam, e o campo legado data: null ainda é emitido inalterado.
Disparos e quais campos delta eles podem emitir
| Disparo | Campos delta possíveis |
|---|---|
POST /organizations/:id/users | addedUserIds |
PUT /organizations/:id/users | addedUserIds, removedUserIds |
DELETE /organizations/:id/users/:userId | removedUserIds |
POST /organizations/:id/applications | addedApplicationIds |
PUT /organizations/:id/applications | addedApplicationIds, removedApplicationIds |
DELETE /organizations/:id/applications/:applicationId | removedApplicationIds |
PUT /organization-invitations/:id/status (Accepted) | addedUserIds |
| Provisionamento just-in-time ao adicionar o usuário a uma nova organização | addedUserIds |
Deltas vazios são omitidos — ausente ≠ alteração vazia
Matrizes delta vazias são omitidas inteiramente da carga útil. Por exemplo, um PUT /organizations/:id/users que substitui o conjunto de associação pelo conjunto existente não produz nenhuma alteração real, e a carga útil se reduz apenas a { organizationId } com todos os quatro campos delta ausentes. O mesmo se aplica a uma re-adição de um membro existente, uma re-aceitação de um convite por um usuário que já é membro que já é membro.
Os consumidores devem tratar um campo ausente como "nenhuma alteração desse lado", não como "uma alteração vazia".
Limite por matriz (truncamento silencioso)
Cada matriz delta é limitada a 5000 entradas. Quando uma única chamada da Management API adiciona ou remove mais de 5000 usuários (ou aplicativos) em uma operação, a matriz delta correspondente é truncada silenciosamente para suas primeiras 5000 entradas — não há marcador na carga útil indicando que um limite foi atingido.
Se sua aplicação realiza operações administrativas em massa que podem afetar plausivelmente mais de 5000 membros em uma chamada, trate uma matriz de 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 os consumidores para a API de comparação para a lista completa.
Ignorando eventos sem operação
Cada PUT contra as rotas de associação emite um evento independentemente de algo realmente ter mudado — para que qualquer chamada de substituição de estado tenha pelo menos uma entrega de webhook para fins de auditoria. Para ignorar entregas sem operação no lado do consumidor, filtre pela presença de matriz delta:
if (
payload.addedUserIds?.length ||
payload.removedUserIds?.length ||
payload.addedApplicationIds?.length ||
payload.removedApplicationIds?.length
) {
// alteração real de associação — lide com isso
}
?.length é falso tanto para undefined quanto para [], então o mesmo predicado é robusto, seja o campo ausente ou (em algum futuro hipotético) emitido como uma matriz vazia.
Exemplos de cargas úteis
Adicionar um usuário (POST /organizations/:id/users):
{
"event": "Organization.Membership.Updated",
"organizationId": "org_abc",
"addedUserIds": ["u_001"]
}
Substituir o conjunto de associação de usuários (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 existente, operação sem efeito PUT, ou re-aceitação de um convite 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 no total */"]
}
Ver uma matriz de exatamente 5000 entradas deve acionar uma reconciliação 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;
};
| Evento | Campo | Tipo | Opcional | Notas |
|---|---|---|---|---|
| OrganizationRole.Created | data | OrganizationRole | A entidade de papel de organização criada. | |
| OrganizationRole.Data.Updated | data | OrganizationRole | A entidade de papel de organização atualizada. | |
| OrganizationRole.Deleted | data | null | / | |
| OrganizationRole.Scopes.Updated | data | null | / | |
| OrganizationRole.Scopes.Updated | organizationRoleId | string | ✅ | O ID do papel ao qual os escopos são 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 (escopo)
| Evento | Campo | Tipo | Opcional | Notas |
|---|---|---|---|---|
| OrganizationScope.Created | data | OrganizationScope | A entidade de escopo de organização criada. | |
| OrganizationScope.Data.Updated | data | OrganizationScope | A entidade de escopo de organização atualizada. | |
| OrganizationScope.Deleted | data | null | / |
Cargas úteis 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 para o usuário, então o corpo contém:
- Os campos comuns.
- O campo
ip(mesmo formato que eventos de mutação de dados). - Os campos de contexto da Experience API.
- Os campos específicos da exceção abaixo.
enum SignInIdentifier {
Email = 'email',
Phone = 'phone',
Username = 'username',
}
| Campo | Tipo | Opcional | Notas |
|---|---|---|---|
| type | SignInIdentifier | O tipo de identificador do usuário, por exemplo, email, telefone ou nome de usuário. | |
| value | string | O valor do identificador do usuário que disparou o bloqueio. |