TransaktDocs

Webhook events

The events Transakt sends to your endpoints, and their payloads.

Webhook endpoints belong to an API client and receive the event types they subscribe to. subscription.* and seat.* events arrive only for products the API client may read; customer.created only for API clients with access to all your products. Every message is signed: see Verifying webhooks.

Envelope

{
  "id": "msg_0193a8f2c4b17d3e9f0a1b2c3d4e5f60",
  "type": "subscription.plan_changed",
  "timestamp": "2026-10-01T08:15:02.123Z",
  "data": {}
}
MemberMeaning
idThe message id, equal to the webhook-id header; the same on retries and replays.
typeThe event type (below).
timestampWhen the change was committed (UTC), not when this attempt was sent.
dataThe event data: identifiers, your product, plan and edition slugs, the customer's tenant id, the subscription and an entitlement snapshot.

Payloads carry identifiers only: no names, email addresses, keys, tokens or payment data. Re-read anything else through the product API. Bodies are UTF-8 JSON of at most 64 KiB, with the conventions of the API (camelCase, PascalCase enumeration values, UTC timestamps, nullable members always present).

Event types and data

The reference below is generated from the webhooks map of the product API document (GET /openapi/product.json), so it always matches what the platform sends: one section per event type with the Standard Webhooks headers, the body schema and an example. Receivers can be generated from the same document as your entitlement client.

An event is written in the same transaction as the change, so every change produces its event whichever way it was made, and a change that is not applied (a duplicate notification, for example) produces none. Moving a plan to another edition, or reordering editions, sends no event. The entitlement member of subscription and seat events is the tenant's entitlement for the product as of the change, exactly as the entitlement API returns it.

TypeSent when
subscription.activatedA subscription becomes Active for the first time, or again after it ended: Marketplace activation, key redemption, online purchase, manual activation or resumption.
subscription.plan_changedThe plan changed: a lifecycle operation, a billing change, a scheduled change applied at the end of the period, or a reconciliation with Microsoft Marketplace. previousPlan and previousEditionSlug carry the plan before the change (previousEditionSlug equals editionSlug for a lateral change).
subscription.quantity_changedThe quantity (licences) changed. previousQuantity carries the quantity before the change.
subscription.suspendedThe subscription was suspended: by Microsoft, or after failed payments and the grace period.
subscription.reinstatedA suspended subscription became Active again.
subscription.renewedThe validity was extended for a new term: a Marketplace or online renewal, a key redeemed on an existing subscription, a manual extension.
subscription.cancelledThe subscription ended as Cancelled: a cancellation, a Marketplace unsubscribe, the end of a period with renewal off, or non-payment.
subscription.expiredA non-Marketplace subscription passed its end date.
seat.assignedA seat was given to a user.
seat.revokedA seat was taken back, including when a subscription ends.
customer.createdA new customer organisation was created in your workspace. Delivered only to endpoints of API clients with access to all your products.
webhook.testYou pressed Send test event in the Admin Portal; sent to that endpoint only.

A subscription becomes Active for the first time, or again after it ended: Marketplace activation, key redemption, online purchase, manual activation or resumption.

Header Parameters

webhook-id*string

The message id: msg_ followed by 32 hexadecimal characters. The same on every attempt and every replay; deduplicate by it.

webhook-timestamp*string

Unix time in seconds at this attempt (each attempt is signed afresh). Reject a timestamp more than 5 minutes from your clock.

webhook-signature*string

v1, followed by the base64 HMAC-SHA-256 of {webhook-id}.{webhook-timestamp}.{body}, keyed with the decoded secret (the base64 after whsec_). Two space-separated signatures during a secret rotation.

Request Body

The stored body bytes, UTF-8 JSON of at most 64 KiB, signed as {webhook-id}.{webhook-timestamp}.{body}.

application/json
  1. body

The webhook body (6.8.2): the message id, the event type, when the change was committed, and the event data.

id*string

The message id, equal to the webhook-id header.

Match^msg_[0-9a-f]{32}$
type*string

The event type.

Value in"subscription.activated""subscription.plan_changed""subscription.quantity_changed""subscription.suspended""subscription.reinstated""subscription.renewed""subscription.cancelled""subscription.expired"
timestamp*string

When the change was committed (UTC), not when this attempt was sent.

Formatdate-time
data*

The data of every subscription.* event.

Response Body

Acknowledged. Any 2xx within 10 seconds counts as delivered; answer before doing the work.

Example Requests

The webhook body (6.8.2): the message id, the event type, when the change was committed, and the event data.

POST/subscription.activated

The plan changed: a lifecycle operation, a billing change, a scheduled change applied at the end of the period, or a reconciliation with Microsoft Marketplace. previousPlan and previousEditionSlug carry the plan before the change (previousEditionSlug equals editionSlug for a lateral change).

Header Parameters

webhook-id*string

The message id: msg_ followed by 32 hexadecimal characters. The same on every attempt and every replay; deduplicate by it.

webhook-timestamp*string

Unix time in seconds at this attempt (each attempt is signed afresh). Reject a timestamp more than 5 minutes from your clock.

webhook-signature*string

v1, followed by the base64 HMAC-SHA-256 of {webhook-id}.{webhook-timestamp}.{body}, keyed with the decoded secret (the base64 after whsec_). Two space-separated signatures during a secret rotation.

Request Body

The stored body bytes, UTF-8 JSON of at most 64 KiB, signed as {webhook-id}.{webhook-timestamp}.{body}.

application/json
  1. body

The webhook body (6.8.2): the message id, the event type, when the change was committed, and the event data.

id*string

The message id, equal to the webhook-id header.

Match^msg_[0-9a-f]{32}$
type*string

The event type.

Value in"subscription.activated""subscription.plan_changed""subscription.quantity_changed""subscription.suspended""subscription.reinstated""subscription.renewed""subscription.cancelled""subscription.expired"
timestamp*string

When the change was committed (UTC), not when this attempt was sent.

Formatdate-time
data*

The data of every subscription.* event.

Response Body

Acknowledged. Any 2xx within 10 seconds counts as delivered; answer before doing the work.

Example Requests

The webhook body (6.8.2): the message id, the event type, when the change was committed, and the event data.

POST/subscription.plan_changed

The quantity (licences) changed. previousQuantity carries the quantity before the change.

Header Parameters

webhook-id*string

The message id: msg_ followed by 32 hexadecimal characters. The same on every attempt and every replay; deduplicate by it.

webhook-timestamp*string

Unix time in seconds at this attempt (each attempt is signed afresh). Reject a timestamp more than 5 minutes from your clock.

webhook-signature*string

v1, followed by the base64 HMAC-SHA-256 of {webhook-id}.{webhook-timestamp}.{body}, keyed with the decoded secret (the base64 after whsec_). Two space-separated signatures during a secret rotation.

Request Body

The stored body bytes, UTF-8 JSON of at most 64 KiB, signed as {webhook-id}.{webhook-timestamp}.{body}.

application/json
  1. body

The webhook body (6.8.2): the message id, the event type, when the change was committed, and the event data.

id*string

The message id, equal to the webhook-id header.

Match^msg_[0-9a-f]{32}$
type*string

The event type.

Value in"subscription.activated""subscription.plan_changed""subscription.quantity_changed""subscription.suspended""subscription.reinstated""subscription.renewed""subscription.cancelled""subscription.expired"
timestamp*string

When the change was committed (UTC), not when this attempt was sent.

Formatdate-time
data*

The data of every subscription.* event.

Response Body

Acknowledged. Any 2xx within 10 seconds counts as delivered; answer before doing the work.

Example Requests

The webhook body (6.8.2): the message id, the event type, when the change was committed, and the event data.

POST/subscription.quantity_changed

The subscription was suspended: by Microsoft, or after failed payments and the grace period.

Header Parameters

webhook-id*string

The message id: msg_ followed by 32 hexadecimal characters. The same on every attempt and every replay; deduplicate by it.

webhook-timestamp*string

Unix time in seconds at this attempt (each attempt is signed afresh). Reject a timestamp more than 5 minutes from your clock.

webhook-signature*string

v1, followed by the base64 HMAC-SHA-256 of {webhook-id}.{webhook-timestamp}.{body}, keyed with the decoded secret (the base64 after whsec_). Two space-separated signatures during a secret rotation.

Request Body

The stored body bytes, UTF-8 JSON of at most 64 KiB, signed as {webhook-id}.{webhook-timestamp}.{body}.

application/json
  1. body

The webhook body (6.8.2): the message id, the event type, when the change was committed, and the event data.

id*string

The message id, equal to the webhook-id header.

Match^msg_[0-9a-f]{32}$
type*string

The event type.

Value in"subscription.activated""subscription.plan_changed""subscription.quantity_changed""subscription.suspended""subscription.reinstated""subscription.renewed""subscription.cancelled""subscription.expired"
timestamp*string

When the change was committed (UTC), not when this attempt was sent.

Formatdate-time
data*

The data of every subscription.* event.

Response Body

Acknowledged. Any 2xx within 10 seconds counts as delivered; answer before doing the work.

Example Requests

The webhook body (6.8.2): the message id, the event type, when the change was committed, and the event data.

POST/subscription.suspended

A suspended subscription became Active again.

Header Parameters

webhook-id*string

The message id: msg_ followed by 32 hexadecimal characters. The same on every attempt and every replay; deduplicate by it.

webhook-timestamp*string

Unix time in seconds at this attempt (each attempt is signed afresh). Reject a timestamp more than 5 minutes from your clock.

webhook-signature*string

v1, followed by the base64 HMAC-SHA-256 of {webhook-id}.{webhook-timestamp}.{body}, keyed with the decoded secret (the base64 after whsec_). Two space-separated signatures during a secret rotation.

Request Body

The stored body bytes, UTF-8 JSON of at most 64 KiB, signed as {webhook-id}.{webhook-timestamp}.{body}.

application/json
  1. body

The webhook body (6.8.2): the message id, the event type, when the change was committed, and the event data.

id*string

The message id, equal to the webhook-id header.

Match^msg_[0-9a-f]{32}$
type*string

The event type.

Value in"subscription.activated""subscription.plan_changed""subscription.quantity_changed""subscription.suspended""subscription.reinstated""subscription.renewed""subscription.cancelled""subscription.expired"
timestamp*string

When the change was committed (UTC), not when this attempt was sent.

Formatdate-time
data*

The data of every subscription.* event.

Response Body

Acknowledged. Any 2xx within 10 seconds counts as delivered; answer before doing the work.

Example Requests

The webhook body (6.8.2): the message id, the event type, when the change was committed, and the event data.

POST/subscription.reinstated

The validity was extended for a new term: a Marketplace or online renewal, a key redeemed on an existing subscription, a manual extension.

Header Parameters

webhook-id*string

The message id: msg_ followed by 32 hexadecimal characters. The same on every attempt and every replay; deduplicate by it.

webhook-timestamp*string

Unix time in seconds at this attempt (each attempt is signed afresh). Reject a timestamp more than 5 minutes from your clock.

webhook-signature*string

v1, followed by the base64 HMAC-SHA-256 of {webhook-id}.{webhook-timestamp}.{body}, keyed with the decoded secret (the base64 after whsec_). Two space-separated signatures during a secret rotation.

Request Body

The stored body bytes, UTF-8 JSON of at most 64 KiB, signed as {webhook-id}.{webhook-timestamp}.{body}.

application/json
  1. body

The webhook body (6.8.2): the message id, the event type, when the change was committed, and the event data.

id*string

The message id, equal to the webhook-id header.

Match^msg_[0-9a-f]{32}$
type*string

The event type.

Value in"subscription.activated""subscription.plan_changed""subscription.quantity_changed""subscription.suspended""subscription.reinstated""subscription.renewed""subscription.cancelled""subscription.expired"
timestamp*string

When the change was committed (UTC), not when this attempt was sent.

Formatdate-time
data*

The data of every subscription.* event.

Response Body

Acknowledged. Any 2xx within 10 seconds counts as delivered; answer before doing the work.

Example Requests

The webhook body (6.8.2): the message id, the event type, when the change was committed, and the event data.

POST/subscription.renewed

The subscription ended as Cancelled: a cancellation, a Marketplace unsubscribe, the end of a period with renewal off, or non-payment.

Header Parameters

webhook-id*string

The message id: msg_ followed by 32 hexadecimal characters. The same on every attempt and every replay; deduplicate by it.

webhook-timestamp*string

Unix time in seconds at this attempt (each attempt is signed afresh). Reject a timestamp more than 5 minutes from your clock.

webhook-signature*string

v1, followed by the base64 HMAC-SHA-256 of {webhook-id}.{webhook-timestamp}.{body}, keyed with the decoded secret (the base64 after whsec_). Two space-separated signatures during a secret rotation.

Request Body

The stored body bytes, UTF-8 JSON of at most 64 KiB, signed as {webhook-id}.{webhook-timestamp}.{body}.

application/json
  1. body

The webhook body (6.8.2): the message id, the event type, when the change was committed, and the event data.

id*string

The message id, equal to the webhook-id header.

Match^msg_[0-9a-f]{32}$
type*string

The event type.

Value in"subscription.activated""subscription.plan_changed""subscription.quantity_changed""subscription.suspended""subscription.reinstated""subscription.renewed""subscription.cancelled""subscription.expired"
timestamp*string

When the change was committed (UTC), not when this attempt was sent.

Formatdate-time
data*

The data of every subscription.* event.

Response Body

Acknowledged. Any 2xx within 10 seconds counts as delivered; answer before doing the work.

Example Requests

The webhook body (6.8.2): the message id, the event type, when the change was committed, and the event data.

POST/subscription.cancelled

A non-Marketplace subscription passed its end date.

Header Parameters

webhook-id*string

The message id: msg_ followed by 32 hexadecimal characters. The same on every attempt and every replay; deduplicate by it.

webhook-timestamp*string

Unix time in seconds at this attempt (each attempt is signed afresh). Reject a timestamp more than 5 minutes from your clock.

webhook-signature*string

v1, followed by the base64 HMAC-SHA-256 of {webhook-id}.{webhook-timestamp}.{body}, keyed with the decoded secret (the base64 after whsec_). Two space-separated signatures during a secret rotation.

Request Body

The stored body bytes, UTF-8 JSON of at most 64 KiB, signed as {webhook-id}.{webhook-timestamp}.{body}.

application/json
  1. body

The webhook body (6.8.2): the message id, the event type, when the change was committed, and the event data.

id*string

The message id, equal to the webhook-id header.

Match^msg_[0-9a-f]{32}$
type*string

The event type.

Value in"subscription.activated""subscription.plan_changed""subscription.quantity_changed""subscription.suspended""subscription.reinstated""subscription.renewed""subscription.cancelled""subscription.expired"
timestamp*string

When the change was committed (UTC), not when this attempt was sent.

Formatdate-time
data*

The data of every subscription.* event.

Response Body

Acknowledged. Any 2xx within 10 seconds counts as delivered; answer before doing the work.

Example Requests

The webhook body (6.8.2): the message id, the event type, when the change was committed, and the event data.

POST/subscription.expired

A seat was given to a user.

Header Parameters

webhook-id*string

The message id: msg_ followed by 32 hexadecimal characters. The same on every attempt and every replay; deduplicate by it.

webhook-timestamp*string

Unix time in seconds at this attempt (each attempt is signed afresh). Reject a timestamp more than 5 minutes from your clock.

webhook-signature*string

v1, followed by the base64 HMAC-SHA-256 of {webhook-id}.{webhook-timestamp}.{body}, keyed with the decoded secret (the base64 after whsec_). Two space-separated signatures during a secret rotation.

Request Body

The stored body bytes, UTF-8 JSON of at most 64 KiB, signed as {webhook-id}.{webhook-timestamp}.{body}.

application/json
  1. body

The webhook body (6.8.2): the message id, the event type, when the change was committed, and the event data.

id*string

The message id, equal to the webhook-id header.

Match^msg_[0-9a-f]{32}$
type*string

The event type.

Value in"seat.assigned""seat.revoked"
timestamp*string

When the change was committed (UTC), not when this attempt was sent.

Formatdate-time
data*

The data of seat.assigned and seat.revoked.

Response Body

Acknowledged. Any 2xx within 10 seconds counts as delivered; answer before doing the work.

Example Requests

The webhook body (6.8.2): the message id, the event type, when the change was committed, and the event data.

POST/seat.assigned

A seat was taken back, including when a subscription ends.

Header Parameters

webhook-id*string

The message id: msg_ followed by 32 hexadecimal characters. The same on every attempt and every replay; deduplicate by it.

webhook-timestamp*string

Unix time in seconds at this attempt (each attempt is signed afresh). Reject a timestamp more than 5 minutes from your clock.

webhook-signature*string

v1, followed by the base64 HMAC-SHA-256 of {webhook-id}.{webhook-timestamp}.{body}, keyed with the decoded secret (the base64 after whsec_). Two space-separated signatures during a secret rotation.

Request Body

The stored body bytes, UTF-8 JSON of at most 64 KiB, signed as {webhook-id}.{webhook-timestamp}.{body}.

application/json
  1. body

The webhook body (6.8.2): the message id, the event type, when the change was committed, and the event data.

id*string

The message id, equal to the webhook-id header.

Match^msg_[0-9a-f]{32}$
type*string

The event type.

Value in"seat.assigned""seat.revoked"
timestamp*string

When the change was committed (UTC), not when this attempt was sent.

Formatdate-time
data*

The data of seat.assigned and seat.revoked.

Response Body

Acknowledged. Any 2xx within 10 seconds counts as delivered; answer before doing the work.

Example Requests

The webhook body (6.8.2): the message id, the event type, when the change was committed, and the event data.

POST/seat.revoked

A new customer organisation was created in your workspace. Delivered only to endpoints of API clients with access to all your products.

Header Parameters

webhook-id*string

The message id: msg_ followed by 32 hexadecimal characters. The same on every attempt and every replay; deduplicate by it.

webhook-timestamp*string

Unix time in seconds at this attempt (each attempt is signed afresh). Reject a timestamp more than 5 minutes from your clock.

webhook-signature*string

v1, followed by the base64 HMAC-SHA-256 of {webhook-id}.{webhook-timestamp}.{body}, keyed with the decoded secret (the base64 after whsec_). Two space-separated signatures during a secret rotation.

Request Body

The stored body bytes, UTF-8 JSON of at most 64 KiB, signed as {webhook-id}.{webhook-timestamp}.{body}.

application/json
  1. body

The webhook body (6.8.2): the message id, the event type, when the change was committed, and the event data.

id*string

The message id, equal to the webhook-id header.

Match^msg_[0-9a-f]{32}$
type*"customer.created"

The event type.

Value in"customer.created"
timestamp*string

When the change was committed (UTC), not when this attempt was sent.

Formatdate-time
data*

The data of customer.created.

Response Body

Acknowledged. Any 2xx within 10 seconds counts as delivered; answer before doing the work.

Example Requests

The webhook body (6.8.2): the message id, the event type, when the change was committed, and the event data.

POST/customer.created

You pressed Send test event in the Admin Portal; sent to that endpoint only.

Header Parameters

webhook-id*string

The message id: msg_ followed by 32 hexadecimal characters. The same on every attempt and every replay; deduplicate by it.

webhook-timestamp*string

Unix time in seconds at this attempt (each attempt is signed afresh). Reject a timestamp more than 5 minutes from your clock.

webhook-signature*string

v1, followed by the base64 HMAC-SHA-256 of {webhook-id}.{webhook-timestamp}.{body}, keyed with the decoded secret (the base64 after whsec_). Two space-separated signatures during a secret rotation.

Request Body

The stored body bytes, UTF-8 JSON of at most 64 KiB, signed as {webhook-id}.{webhook-timestamp}.{body}.

application/json
  1. body

The webhook body (6.8.2): the message id, the event type, when the change was committed, and the event data.

id*string

The message id, equal to the webhook-id header.

Match^msg_[0-9a-f]{32}$
type*"webhook.test"

The event type.

Value in"webhook.test"
timestamp*string

When the change was committed (UTC), not when this attempt was sent.

Formatdate-time
data*

The data of webhook.test.

Response Body

Acknowledged. Any 2xx within 10 seconds counts as delivered; answer before doing the work.

Example Requests

The webhook body (6.8.2): the message id, the event type, when the change was committed, and the event data.

POST/webhook.test

Ordering and versions

Delivery is at least once and unordered. Each subscription event carries subscription.version, which increases with every committed change of the subscription; entitlement.version carries the same number for the tenant and product. Keep the highest version you have applied per subscription and ignore lower ones, or re-read the entitlement.

Versioning

Event payloads change only additively: new members, new enumeration values and new event types (delivered only to endpoints that subscribe to them). Ignore unknown members and treat unknown accessLevel or licenceOutcome values as the most restrictive. A breaking change publishes a new event type (for example subscription.plan_changed.v2) next to the old one, which keeps being delivered for at least 12 months.

On this page