# Event types: organization, accounts, billing

The subscribable event types from organization, storage connections, access, accounts, sessions, billing, and joining, for reference when you create an endpoint or interpret a delivery.

> **Type:** reference
> **For:** Anyone who chooses the events of an endpoint or reads the body of a delivery.
> **Prerequisite:** None for reading. To subscribe, an endpoint under **Settings › Organization › Webhooks**.

## How to read the tables

- **Event type** is the string you subscribe to on the endpoint. It arrives in the header `X-Event-Type` with every delivery. Instead of single types you can also subscribe to a whole area (`composition.*`) or to everything with `*`. There are no other forms, and the server rejects anything else, see [Fetch the event catalog through the API](/webhooks/fetch-event-catalog).
- **Core payload** names two to four of the most important fields that are in the body of the delivery. It is an excerpt. Each area determines the full shape, which can carry more fields. The time, the event identifier, and the trigger do not travel in the body.
- **When it is raised** is a short phrase for context, not a statement about frequency.

Under **Subscribed events**, the portal offers exactly the types of this page and of the page [Event types: content, programs, devices, apps](/webhooks/event-types-content-devices) for selection, the same list that `GET /v1/webhooks/event-types` returns. Everything else cannot be subscribed to, and creating an endpoint rejects it. Which types these are is described under [What never arrives](/webhooks/event-types-content-devices#what-never-arrives). Only what is raised in the context of your organization is delivered.

## The event types

### Organization

Lifecycle of the organization, brand, custom portal address, and company sign-in. `tenancy.tenant.suspended` and `.deleted` are the triggers that other areas follow.

| Event type | Core payload | When it is raised |
|---|---|---|
| `tenancy.tenant.created` | `tenantId`, `slug`, `displayName` | An organization was created. |
| `tenancy.tenant.updated` | `tenantId`, `changedFields` | The master data of the organization was changed. |
| `tenancy.tenant.suspended` | `tenantId`, `reason`, `suspendedById`, `deletionDueAt` | The organization was suspended, and `deletionDueAt` gives the deletion date. |
| `tenancy.tenant.reactivated` | `tenantId`, `reactivatedById` | A suspension was lifted. |
| `tenancy.tenant.frozen` | `tenantId`, `reason`, `frozenById` | The organization was frozen. |
| `tenancy.tenant.unfrozen` | `tenantId`, `unfrozenById` | The freeze was lifted. |
| `tenancy.tenant.deletion-warned` | `tenantId`, `deletesAt`, `daysLeft` | Warning before deletion. `daysLeft` gives the remaining days. |
| `tenancy.tenant.deleted` | `tenantId`, `slug`, `deletedById` | The organization was deleted. |
| `tenancy.branding.updated` | `tenantId`, `changedFields` | The brand (logo, colors) was changed. |
| `tenancy.custom-domain.verified` | `tenantId`, `customDomain` | A custom portal address was verified. |
| `tenancy.custom-domain.removed` | `tenantId`, `domain` | A custom portal address was removed. |
| `tenancy.sso.configured` | `tenantId`, `providerId`, `protocol`, `status` | The company sign-in was set up or changed. |
| `tenancy.sso.disabled` | `tenantId`, `providerId`, `protocol` | The company sign-in was switched off. |
| `tenancy.sso.domain-changed` | `tenantId`, `providerId`, `domain`, `status` | The domain of the company sign-in was changed. |
| `tenancy.sso.enforcement-changed` | `tenantId`, `providerId`, `enforce`, `breakGlassUserIds` | Enforcement of the company sign-in was turned on or off. |
| `tenancy.sso.break-glass-used` | `tenantId`, `providerId`, `userId`, `email` | A break-glass account signed in, bypassing the company sign-in. |

### Storage connections

Standing authorizations for third-party storage accounts. `broken` tells you since when no new files have arrived.

| Event type | Core payload | When it is raised |
|---|---|---|
| `cloud-connections.connection.connected` | `connectionId`, `provider`, `accountLabel`, `repaired` | A storage account was connected, and `repaired` distinguishes the first connection from a repair. |
| `cloud-connections.connection.disconnected` | `connectionId`, `provider`, `revocationAcknowledged` | The connection was disconnected. |
| `cloud-connections.connection.broken` | `connectionId`, `provider`, `reason` | The authorization no longer holds. From here on, syncing stops. It cannot be triggered from the portal. |

### Access: roles and grants

Roles of the organization and grants on individual resources.

| Event type | Core payload | When it is raised |
|---|---|---|
| `access.role.created` | `roleId`, `tenantId`, `name`, `permissions` | A role was created. |
| `access.role.updated` | `roleId`, `changedFields`, `newPermissions`, `newVersion` | The permissions or the name of a role were changed. |
| `access.role.deleted` | `roleId`, `tenantId`, `requesterId` | A role was deleted. |
| `access.grant.created` | `grantId`, `userId`, `resourceType`, `resourceId` | A grant on a single resource was issued. |
| `access.grant.revoked` | `grantId`, `userId`, `resourceType`, `resourceId` | A grant was revoked. |
| `access.grant.expired` | `grantId`, `userId`, `resourceId`, `expiredAt` | A time-limited grant has expired. |
| `access.permission-catalog.rebuilt` | `tenantId`, `trigger`, `entryCount` | The permission catalog of the organization was rebuilt, and `trigger` gives the reason. |

### Accounts, keys, and device identities

User accounts, API keys, SCIM tokens, applications with delegated access, and the identity of devices. The accounts and keys of the operator do not belong here, they cannot be subscribed to (see [What never arrives](/webhooks/event-types-content-devices#what-never-arrives)).

| Event type | Core payload | When it is raised |
|---|---|---|
| `identity.user.created` | `userId`, `tenantId`, `email`, `initialRoles` | A user account was created. |
| `identity.user.updated` | `userId`, `changedFields`, `newValues` | Profile data of an account was changed. |
| `identity.user.role.changed` | `userId`, `previousRoles`, `newRoles` | The roles of an account were changed. |
| `identity.user.suspended` | `userId`, `reason`, `cascadeSource` | An account was suspended. |
| `identity.user.deleted` | `userId`, `deletedById`, `pseudonymizationJobId` | An account was deleted, and pseudonymization was triggered. |
| `identity.user.pseudonymized` | `userId`, `pseudonymId`, `entriesReplaced` | Entries with personal data in a deleted account were replaced. |
| `identity.user.mfa.enabled` | `userId`, `tenantId`, `enabledAt` | The second factor was set up. |
| `identity.user.mfa.disabled` | `userId`, `tenantId`, `disabledAt` | The second factor was removed. |
| `identity.user.mfa.recovery-codes.regenerated` | `userId`, `tenantId`, `regeneratedAt` | New recovery codes were generated. |
| `identity.user.password-changed` | `userId`, `via`, `changedAt` | The password was changed, and `via` gives the route. |
| `identity.api-key.minted` | `apiKeyId`, `name`, `keyPrefix`, `scope` | An API key was created. |
| `identity.api-key.revoked` | `apiKeyId`, `revokedById`, `reason` | An API key was revoked. |
| `identity.scim-token.minted` | `scimTokenId`, `tenantId`, `tokenPrefix`, `createdById` | A SCIM token for the personnel directory of the organization was issued. |
| `identity.scim-token.revoked` | `scimTokenId`, `tenantId`, `revokedById`, `reason` | A SCIM token was revoked. Provisioning from the personnel directory stops. |
| `identity.oauth.client.registered` | `clientId`, `name`, `redirectUris`, `registeredIp` | A third-party application registered for delegated access. It gains nothing until a person consents. |
| `identity.oauth.client.blocked` | `clientId`, `name`, `blockedById`, `reason`, `revokedGrants` | Platform operations blocked an application, and `revokedGrants` counts the grants that expired with it. |
| `identity.oauth.client.unblocked` | `clientId`, `name`, `unblockedById` | The block on an application was lifted. The grants that expired do not come back. |
| `identity.oauth.grant.granted` | `clientId`, `clientName`, `tenantId`, `userId`, `scopes` | An account delegated rights to an application, scoped to this organization. |
| `identity.oauth.grant.denied` | `clientId`, `clientName`, `tenantId`, `userId`, `requestedScopes` | An account declined the request of an application, and no access was created. |
| `identity.oauth.token.issued` | `tokenId`, `clientId`, `userId`, `scopes`, `rotated` | An application received an access token and a refresh token, after a consent or as a rotation (`rotated`). |
| `identity.oauth.token.revoked` | `clientId`, `userId`, `revokedGrants`, `reason` | Grants of an application were invalidated, and `reason` gives the cause. |
| `identity.device.registered` | `deviceId`, `certFingerprint`, `hardwareInfo` | A device registered with the platform for the first time. |
| `identity.device.cert.rotated` | `deviceId`, `oldFingerprint`, `newFingerprint` | The certificate of a device was renewed. |
| `identity.device.reconnected` | `deviceId`, `reconnectedById`, `previousHardwareSerial` | A web display was moved to another browser. It stays paired. |
| `identity.device.released` | `deviceId`, `previousTenantId`, `reason`, `wipeLevel` | A device has left your organization, and its displays are removed. `reason` gives the cause (`reconnect`, `factory`, `removed`, `ops`, `tenant-deleted`), and `wipeLevel` the level to which it wipes itself. |
| `identity.device.blocked` | `deviceId`, `previousTenantId`, `reason`, `blockedAt` | A device was reported lost or blocked by the operator. It no longer belongs to any organization and cannot sign in until the block is lifted. |
| `identity.device.reregistration-requested` | `deviceId`, `tenantId`, `deviceName`, `requestedAt` | A paired device registered with a new key, probably because it was set up again. It waits until someone decides in the device record. |
| `identity.device.reregistration-decided` | `deviceId`, `decision`, `decidedById`, `allowedUntil` | A decision was made about a re-registered device. `decision` is `keep` (keep the displays), `reconnect` (pair again), or `block` (block it). |
| `identity.renderer.minted` | `rendererSessionId`, `playerId`, `expiresAt` | A playback session of a display was issued (machine traffic). |
| `identity.renderer.revoked` | `rendererSessionId`, `playerId`, `reason` | A playback session was revoked (machine traffic). |

### Sessions and sign-in

Sign-ins, renewal of sessions, magic links, and actions by platform operations on behalf of an account. `sessions.session.theft-detected` is the alarm for a reused session token.

| Event type | Core payload | When it is raised |
|---|---|---|
| `sessions.login.succeeded` | `sessionId`, `userId`, `kind`, `method` | A sign-in succeeded, and `method` gives the route. |
| `sessions.login.failed` | `email`, `reason`, `ipAddress` | A sign-in failed. |
| `sessions.mfa.verified` | `sessionId`, `userId`, `via` | The second factor was confirmed. |
| `sessions.mfa.failed` | `sessionId`, `userId`, `attemptCount` | The second factor was entered incorrectly. |
| `sessions.session.refreshed` | `sessionId`, `familyId`, `userId` | A session was renewed (constant traffic). |
| `sessions.session.theft-detected` | `sessionId`, `familyId`, `replayedHashPrefix` | A session token that had already been used up was presented again. |
| `sessions.session.terminated` | `sessionId`, `userId`, `reason` | A session was ended. |
| `sessions.session.all-terminated` | `userId`, `revokedCount`, `keptSessionId` | All sessions of an account were ended. |
| `sessions.password-reset.requested` | `passwordResetId`, `subjectKind`, `purpose` | A password reset was requested. |
| `sessions.password-reset.completed` | `passwordResetId`, `userId`, `subjectKind` | A new password was set through the reset. |
| `sessions.magic-link.requested` | `magicLinkId`, `email`, `expiresAt` | A magic link was requested. |
| `sessions.magic-link.consumed` | `magicLinkId`, `sessionId`, `userId` | A magic link was used. |
| `sessions.impersonation.started` | `operatorUserId`, `targetUserId`, `reason`, `expiresAt` | Platform operations act on behalf of an account. |
| `sessions.impersonation.ended` | `operatorUserId`, `targetUserId`, `endedBy` | Acting on behalf of an account was ended. |

### Billing

Contract, invoices, payment methods, and the unlocked features. A package change reports twice: `subscription.package-changed` says that the contract is on a different package (on every change), and `feature-entitlements.changed` says that one of the feature switches has changed, and `changedKeys` shows which one. There are no separate events for upgrade and downgrade. The direction is in `from` and `to`.

| Event type | Core payload | When it is raised |
|---|---|---|
| `billing.subscription.created` | `subscriptionId`, `tenantId`, `status`, `billingCycle`, `currentPeriodEnd` | A contract was created. |
| `billing.subscription.activated` | `subscriptionId`, `tenantId`, `from` (`trial`/`past_due`) | The contract is active, and `from` gives the previous state. |
| `billing.subscription.past-due` | `subscriptionId`, `graceUntil`, `reason` (`payment-failed`/`trial-ended`) | Payment did not arrive or the trial ended. `graceUntil` gives the deadline. |
| `billing.subscription.grace-lapsed` | `subscriptionId`, `tenantId`, `graceLapsedAt`, `suspendsAt` | The deadline has passed, and `suspendsAt` gives the suspension date. |
| `billing.subscription.trial-ending` | `subscriptionId`, `tenantId`, `trialEndsAt`, `daysLeft` | The trial ends soon, and `daysLeft` gives the remaining days. |
| `billing.subscription.suspended` | `subscriptionId`, `tenantId` | The contract was suspended. |
| `billing.subscription.canceled` | `subscriptionId`, `reason` | The contract was canceled. |
| `billing.subscription.package-changed` | `tenantId`, `from` (nullable), `to`, `at` | The contract is on a different package. It comes on every change, and the direction is in `from` and `to`. |
| `billing.subscription.period-rolled` | `subscriptionId`, `tenantId`, `periodStart`, `periodEnd`, `cameFromTrial` | A new billing period has begun. |
| `billing.subscription.cancel-scheduled` | `subscriptionId`, `tenantId`, `effectiveAt`, `reason` | A cancellation was scheduled for its date. |
| `billing.subscription.cancel-revoked` | `subscriptionId`, `tenantId`, `at` | A scheduled cancellation was withdrawn. |
| `billing.subscription.trial-extended` | `subscriptionId`, `tenantId`, `days`, `trialEndsAt`, `reason` | The trial was extended. |
| `billing.subscription.license-conflict` | `subscriptionId`, `tenantId`, `devices`, `licenses`, `deadline` | The paid reduction of the booked displays has taken effect, and more devices are running than displays are booked. The surplus ones keep running until `deadline`. |
| `billing.subscription.license-conflict-enforced` | `subscriptionId`, `tenantId`, `frozen`, `licenses` | The grace period has ended, and `frozen` counts the frozen displays. They stay frozen until the organization books more displays or unpairs a device. |
| `billing.subscription.pending-change-failed` | `subscriptionId`, `tenantId`, `kind` (`quantity`/`package`), `pendingQuantity`, `usedDisplays`, `effectiveAt` | A scheduled change did not take effect, because more displays are in use than it allows. It stays in place and takes effect at the end of the next period. |
| `billing.invoice.issued` | `invoiceId`, `invoiceNumber`, `amountCents`, `currency` | An invoice was issued. |
| `billing.invoice.paid` | `invoiceId`, `invoiceNumber`, `amountCents`, `paidAt` | An invoice was paid. |
| `billing.invoice.payment-failed` | `invoiceId`, `amountCents`, `attemptCount` | A payment failed. |
| `billing.invoice.refunded` | `invoiceId`, `reason`, `refundId` | An invoice was refunded. |
| `billing.invoice.credited` | `invoiceId`, `creditNoteId`, `creditNoteNumber`, `amountCents`, `settlement`, `reason` | A credit note was created for an invoice. |
| `billing.invoice.voided` | `invoiceId`, `invoiceNumber`, `reason` | An invoice was voided. |
| `billing.invoice.written-off` | `invoiceId`, `invoiceNumber`, `amountCents`, `reason` | An invoice was written off. |
| `billing.invoice.dunned` | `invoiceId`, `noticeId`, `noticeNumber`, `level`, `deadlineAt` | A reminder was created, and `level` gives its level. |
| `billing.vat-id.confirmed` | `tenantId`, `countryCode`, `requestId` | The VAT ID was confirmed. |
| `billing.vat-id.invalidated` | `tenantId`, `countryCode`, `previousStatus` | The VAT ID is no longer valid. |
| `billing.payment-method.attached` | `tenantId`, `stripePaymentMethodId`, `kind`, `lastFour` | A payment method was stored. |
| `billing.payment-method.removed` | `tenantId`, `stripePaymentMethodId` | A payment method was removed. |
| `billing.payment-way.switched` | `tenantId`, `before`, `after` (`invoice`/`stripe`) | The payment way was switched (`invoice` or `stripe`). |
| `billing.feature-entitlements.changed` | `tenantId`, `before`, `after`, `changedKeys` | One of the feature switches has changed, and `changedKeys` shows which. |
| `billing.item-override.ending` | `tenantId`, `subscriptionId`, `skuCode`, `validUntilDate`, `effectiveUnitPriceCents`, `listUnitPriceCents` | Special terms are about to expire. |
| `billing.item-override.expired` | `tenantId`, `subscriptionId`, `skuCode`, `validUntilDate`, `listUnitPriceCents` | Special terms have expired. |
| `billing.checkout.started` | `checkoutRef`, `tenantId`, `purpose`, `packageCode`, `billingCycle`, `entry` | A checkout was started. |
| `billing.checkout.finished` | `checkoutRef`, `tenantId`, `purpose`, `outcome`, `paymentWay` | A checkout has ended, and `outcome` gives the result. |

### Joining and setup

Invitations, the setup wizard, provisioning of a new organization, join requests, and self-registration.

| Event type | Core payload | When it is raised |
|---|---|---|
| `onboarding.invitation.sent` | `invitationId`, `email`, `defaultRoleId`, `expiresAt` | An invitation was sent. |
| `onboarding.invitation.resent` | `invitationId`, `email`, `expiresAt` | An invitation was sent again. |
| `onboarding.invitation.revoked` | `invitationId`, `email`, `revokedById` | An invitation was revoked. |
| `onboarding.invitation.accepted` | `invitationId`, `email`, `acceptedByUserId` | An invitation was accepted. |
| `onboarding.invitation.expired` | `invitationId`, `tenantId`, `email` | An invitation has expired. |
| `onboarding.setup-step.completed` | `tenantId`, `step`, `via` | A step of the setup wizard was completed. |
| `onboarding.setup-wizard.completed` | `tenantId`, `completedAt` | The setup wizard was completed. |
| `onboarding.tenant.provisioning-started` | `tenantId`, `slug`, `provisionedById` | Provisioning of a new organization has begun. |
| `onboarding.tenant.provisioning-completed` | `tenantId`, `subscriptionId`, `ownerEmail` | Provisioning of a new organization is complete. |
| `onboarding.join-request.received` | `joinRequestId`, `tenantId`, `email`, `displayName` | A join request has arrived. |
| `onboarding.join-request.decided` | `joinRequestId`, `tenantId`, `email`, `decision` (`accepted`/`rejected`), `decidedById` | A join request was decided (`accepted` or `rejected`). |
| `onboarding.self-registration.changed` | `enabled`, `reason`, `changedById` | The platform switch for self-registration was flipped (platform operations). |
| `onboarding.signup.started` | `funnelRef`, `entry` | A self-registration was started. |
| `onboarding.signup.verified` | `funnelRef`, `route` (`create`/`join`/`sso`) | The address of a self-registration was verified, and `route` gives the route. |
| `onboarding.signup.completed` | `funnelRef`, `tenantId` | A self-registration is complete. |
| `onboarding.terms.accepted` | `tenantId`, `userId`, `occasion`, `documents`, `locale`, `acceptedAt` | Contract documents were accepted, and `documents` lists which. |

## Related

- [Event types: content, programs, devices, apps](/webhooks/event-types-content-devices)
- [Create a webhook endpoint](/webhooks/create-endpoint)
- [What is sent and how it is retried](/webhooks/delivery-and-retries)
