1. Docs
  2. Event Types

Webhook Event Types

Every event you can subscribe a webhook to, grouped by scope.

Overview

When something changes in Canopy (a role is assigned, an identity is created, a node moves), an event fires. A webhook subscription lists the event types it wants, and Canopy delivers a signed HTTP POST to your endpoint for each matching change. Subscribe to a specific set, or use the wildcard "*" to receive every event in the subscription's scope (including ones added later).

Events come in two scopes

Canopy's data model is two-tiered, so events are too. Environment-scoped events carry an environment_id and cover authorization config (assignments, roles, permissions, and hierarchy nodes), delivered to env-scoped subscriptions. Account-scoped events span environments (identity lifecycle, membership, and sessions), delivered to account-scoped subscriptions. The identity events that concern an Environment are delivered in both: to the account, and to each Environment the identity belongs to. Every other event reaches only its own scope.

Environment events

Fired by changes to an Environment's authorization configuration, however they were made: by hand, by API, by an accepted invitation, by a first sign-in through SSO, or by a customer's directory. Delivered to environment-scoped subscriptions. The four identity events at the top are shared with the account scope: an environment subscription receives the ones about its own Environment, so one subscription follows the fate of the identities in it. The scim.* events are the directory's own activity, for mirroring the directory rather than its results.

identity.status_set: An identity was activated or deactivated.identity.erased: An identity's personal data was erased (GDPR/CCPA). The payload carries only the pseudonymous identity id.identity.env_membership.added: An identity was granted membership to an environment.identity.env_membership.removed: An identity's environment membership was removed.assignment.created: A role was assigned to an identity at a node.assignment.updated: An existing role assignment was updated.assignment.removed: A role assignment was removed from an identity.assignment.bulk_created: Multiple role assignments were created in one operation.assignment.bulk_removed: Multiple role assignments were removed in one operation.assignment.bulk_role_changed: The role on multiple assignments was changed in bulk.scim.user.provisioned: A directory pushed a person: an identity was created, or an existing one linked, and made active here.scim.user.updated: A directory changed a person's name, address or id.scim.user.deprovisioned: A directory deactivated a person, giving up what it had granted them.scim.user.reactivated: A directory brought back a person it had deprovisioned.scim.group.created: A directory pushed a group.scim.group.updated: A directory changed a group's name, id or members.scim.group.deleted: A directory removed a group.scim.group.member_added: A directory added a person to a group.scim.group.member_removed: A directory removed a person from a group.scim.group.mapped: A pushed group was mapped to a role, which its members now hold.scim.group.unmapped: A pushed group's mapping was cleared.role.created: A role was created.role.updated: A role's name or metadata was updated.role.deleted: A role was deleted.role.permissions.updated: The permissions attached to a role were changed.permission.created: A permission was created.permission.updated: A permission was updated.permission.deleted: A permission was deleted.node.created: A hierarchy node was created.node.updated: A hierarchy node was updated.node.deleted: A hierarchy node was deleted.node.moved: A hierarchy node was moved, changing inheritance.hierarchy_schema.updated: The hierarchy node-type schema was updated.organization.created: An organization was created in the Environment.organization.updated: An organization was renamed or updated.organization.deleted: An organization was deleted, along with its memberships, pending invitations, SSO binding and owned connection, and email-domain claims.organization.member.added: An identity became a member of an organization, with a role at its node or none.organization.member.role_changed: A directory moved its arriving members from one role to another at the organization node.organization.member.removed: An identity's membership in an organization was deactivated, taking every role it held there.organization.deleted_all: Every organization in the Environment was deleted in one call, with their memberships, pending invitations, SSO bindings and connections, domain claims, and subtrees.organization.policy.updated: An organization's authentication policy was changed (MFA requirement, factor allowlist, MFA after SSO, or require SSO).organization.sso_connection.bound: An SSO connection was bound to an organization; sign-ins through it now land there.organization.sso_connection.unbound: An SSO connection was unbound from an organization.

Membership events describe admission; assignment events describe the roles. Adding a member with a role fires organization.member.added and assignment.created, and without one organization.member.added alone; changing or removing a role held in the organization fires assignment.updated or assignment.removed; removing a member fires organization.member.removed, counting the assignments that went with the membership, and no assignment events for them. organization.member.role_changed is directory sync's: a mapped group moving a member between roles, or the role people arrive with changing. Subscribe to whichever family you reason in, not both, unless you deduplicate. A membership created by a just-in-time SSO sign-in through a bound connection fires organization.member.added only, and its data.metadata carries source: "sso_jit" and the connection_id, so a consumer can tell a provisioned membership from one an administrator added. An invitation into an organization, accepted by the invitee, fires organization.member.added with source: "invite", the invite_id and the membership_id, beside assignment.created when it carries a role; accepted by someone who is already a member, it fires assignment.created alone, or assignment.updated when it opens again a role they held there that had ended. identity.env_membership.added carries the same marker when that sign-in also created the identity's Environment membership. Directory sync announces the same events. A person an organization's identity provider pushes fires organization.member.added; a group mapped or unmapped, or the provider moving somebody between groups, fires organization.member.role_changed for each member whose role moved; a person the provider removes fires organization.member.removed. Each carries source: "scim" and the directory_id, and none is paired with an assignment.* event, as a just-in-time membership is not. A group mapped to a node beneath the organization grants and withdraws roles there, announced as assignment.bulk_created and assignment.bulk_removed with the same marker.

Account events

Account-scoped events span environments, because identities live at the Account level. They are delivered to account-scoped subscriptions, created from the Console's Account-level Webhooks page or with POST /portal/v1/accounts/:accountSlug/webhooks and a Console token. The identity events that concern an Environment (identity.status_set, identity.erased, and the two identity.env_membership events) also reach that Environment's subscriptions, so an integration holding only an environment API key still hears about its own members. The rest (identity.created, identity.updated, the account member events, and session.all_revoked) are account-only: GET /api/v1/webhooks/event-types lists them with scope: "account", and naming one on an environment subscription answers 400 webhook.invalid_event_type.

identity.created: An identity was created.identity.updated: An identity's profile or attributes were updated.identity.status_set: An identity was activated or deactivated.identity.erased: An identity's personal data was erased (GDPR/CCPA). The payload carries only the pseudonymous identity id.identity.env_membership.added: An identity was granted membership to an environment.identity.env_membership.removed: An identity's environment membership was removed.account_member.added: A member was added to the account.account_member.removed: A member was removed from the account.session.all_revoked: All active sessions for an identity were revoked.

What each event carries

Every delivery's data is the audit entry's envelope (see below); what differs per event is resource_type, resource_id, and the keys inside metadata. Ids are Canopy ids; external_id on organization events is the id you supplied when you created the organization, so a receiver can correlate without a follow-up read.

Assignments (resource_type: "role_assignment", resource_id the assignment): assignment.created, assignment.updated, and assignment.removed carry identity_id, oauth_client_id, client_id, node_id, and role_id: a role is held by a person or by a machine client, so either identity_id or oauth_client_id is null, and client_id, the machine client's public id that its token's sub and its grants route use, is null for a person; one accepted from an invitation adds source: "invite". assignment.bulk_created carries assignment_ids, succeeded, and failed; assignment.bulk_removed carries assignment_ids and affected (a list of identity_id, node_id, role_id); assignment.bulk_role_changed carries assignment_ids, the new role_id, and affected (identity_id, node_id).Roles (resource_type: "role", resource_id the role): role.created and role.deleted carry name, and role.created adds copied_from_role_id when the role started as a copy of another; role.updated carries changes, the fields written; role.permissions.updated carries permission_keys, the role's full list after the write.Permissions (resource_type: "permission", resource_id the permission): permission.created and permission.deleted carry key; permission.updated carries changes.Hierarchy (resource_type: "node", resource_id the node): node.created carries name, node_type, and parent_node_id; node.updated carries name, plus status when it changed; node.moved carries name and the new parent_node_id, plus status when it changed as well; node.deleted carries name, descendant_count, and assignments_removed. hierarchy_schema.updated (resource_type: "hierarchy_schema") carries node_types and max_depth.Organizations (resource_type: "organization", resource_id the organization): organization.created and organization.updated carry name, slug, and external_id, and organization.created adds source: "self_signup" when the Environment created it for a person who registered; organization.deleted carries name, external_id, memberships_removed, invites_revoked, nodes_deleted, domains_released, sso_bindings_removed, and sso_connections_removed. organization.deleted_all (resource_type: "environment") carries organizations_deleted and the same removal counts summed across them.Memberships (resource_type: "organization", resource_id the organization, each with external_id, except a membership a just-in-time SSO sign-in or a self-signup created): organization.member.added carries identity_id and, when the person joined with a role, role_id, plus assignment_id on every path but a just-in-time sign-in; membership_id names the membership record where the path has it; a just-in-time SSO sign-in adds connection_id and source: "sso_jit", and a self-signup that created the organization adds source: "self_signup"; organization.member.role_changed carries identity_id, from_role_id, and to_role_id; organization.member.removed carries identity_id, membership_id, and assignments_removed, the roles that went with the membership. Any of the three made by directory sync also carries source: "scim" and directory_id.Organization policy and SSO (resource_type: "organization", with external_id): organization.policy.updated carries mfa_required, mfa_factor_allowlist, mfa_after_sso, allowed_sign_in_methods, and require_sso; organization.sso_connection.bound carries connection_id and default_role_id (null while the binding waits for a role), plus connection_name on a new binding; the same event announces an existing binding's role changing, with changed: "default_role" instead of a name; organization.sso_connection.unbound carries connection_id.Directory sync (resource_type: "identity" for a person, "scim_group" for a group, resource_id the identity or the group): what an identity provider did, as it did it, for a subscriber mirroring the directory itself. Every one carries directory_id. The person events carry user_name and external_id, the provider's own view of them, plus linked on a provision or reactivation and active on an update; the group events carry display_name, external_id and, on create and update, member_count; scim.group.member_added and member_removed carry identity_id and directory_user_id; scim.group.mapped carries role_id and application_node_id. The results of all this, a member joining, a role changing, a person leaving, are the membership, assignment and identity events above, marked source: "scim"; subscribe to those to keep a record of who holds what, and to these to keep a record of the directory.Identities (resource_type: "identity", resource_id the identity): identity.created carries email and password_set, plus environment_id when created inside an Environment, or a source of invite when an invitation was accepted, sso_jit (with connection_id) when a first sign-in through SSO created it, or scim (with directory_id) when directory sync did; identity.updated carries changes or fields_updated; identity.status_set carries previous_is_active and new_is_active, and on an account delivery environment_ids, the Environments the identity belonged to; identity.erased carries environment_ids the same way. An Environment delivery of either names that Environment alone: its own id and Application in the envelope, and no environment_ids, since one Environment's subscriber is not told about the others. The two identity.env_membership events carry the Environment in the envelope's environment_id. identity.env_membership.added carries membership_id, reactivated, and a source of identity_create, invite (with invite_id), sso_jit (with connection_id), or scim (with directory_id); identity.env_membership.removed carries revoked_pending_invites, and source: "scim" with directory_id when a directory deprovisioned the person. Which action fires which: deactivating or reactivating an identity, which is Account-wide (the Workspace, or PATCH /portal/v1/accounts/{accountSlug}/identities/{id}/status), fires identity.status_set; DELETE /api/v1/identities/{id} removes the identity from that Environment and fires identity.env_membership.removed, never identity.erased, which only the Account-level erase action fires.Account members and sessions: account_member.added (resource_type: "account_member") carries email and invite_id; account_member.removed carries no metadata; session.all_revoked (resource_type: "identity" for an end user, "user" for an administrator) carries revoked_count and reason.

Payloads & the audit log

Every event mirrors an audit-log entry

Webhook event types are the same actions Canopy records in its audit log: every delivery corresponds to an audit entry, so you can reconcile what you received against the authoritative trail and use the audit log to replay anything an endpoint missed. The live list of subscribable event types for a scope is also available from the API at GET …/webhooks/event-types, so your integration can validate against it programmatically.

Environment
API version
v1.0
On this page Was this page helpful?

Tell us how we can improve this guide.