Skip to content
Insy
For developers
Esc
navigateopen⌘Jpreview
On this page

Events

The shared webhook envelope and the payload of all five Insy event types — membership, payment and digital product purchase events.

Insy sends five event types, and every active webhook of an eligible user receives all of them. Each delivery uses the same envelope; only the contents of data differ per type. Branch on event_type and ignore anything you do not handle.

Event type Fires when
membership.created A membership is created for a user in a community
membership.updated An existing membership’s active state flips
payment.success A payment completes
payment.failed A payment does not complete
digital.product.purchase A digital product is purchased

The envelope

Every event has the same four top-level fields.

{
  "event_type": "membership.created",
  "event_id": "insy_event_1756742400000_k3f9d2a1x8b",
  "data": {
    "membershipId": "4f9a1c2e-7b3d-4e58-9c01-6d2f8a5b3e17",
    "metadata": { "userProfile": {} }
  },
  "timestamp": "1756742400000"
}
Field Type Description
event_type string One of the five event types. Also sent as the X-Insy-Event-Type header.
event_id string The event identifier, in the same insy_event_<epochMillis>_<random> form as the X-Insy-Event-Id header. Use it as your deduplication key.
data object The event payload. Always contains metadata, plus the type-specific fields documented below.
timestamp string Epoch milliseconds, as a JSON string.

data.metadata.userProfile

Every payload carries data.metadata.userProfile, the Insy profile of the user the event is about. The examples below abbreviate it as {}. Read the properties you need defensively and ignore the rest; the object is additive.

The data fields listed in the tables below are in addition to metadata.

membership.created

Fires when a membership record is created for a user in a community. Delivered to the webhooks of that community’s owner, admin and moderator members.

Creation is not activation: a membership backed by a not-yet-active Stripe subscription is created with isActive: false, so membership.created can arrive for a member without access. Grant access only when isActive is true, not on the event itself.

Field Type Description
membershipId string Identifier of the membership. Stable across later updates, so it is the right key to store.
isActive boolean Whether the membership is active now.
wasActive boolean Whether it was active before this change.
role string The role the user holds in the community. The values are owner, admin, moderator and member.
validFrom string Start of the membership window, ISO-8601.
validUntil string End of the membership window, ISO-8601. Do not assume it is always populated — a lifetime membership has no end date.
lastPaymentMethod string The payment method most recently used for this membership.
{
  "event_type": "membership.created",
  "event_id": "insy_event_1756742400000_k3f9d2a1x8b",
  "data": {
    "membershipId": "4f9a1c2e-7b3d-4e58-9c01-6d2f8a5b3e17",
    "isActive": true,
    "wasActive": false,
    "role": "member",
    "validFrom": "2025-09-01T16:00:00.000Z",
    "validUntil": "2025-10-01T16:00:00.000Z",
    "lastPaymentMethod": "stripe",
    "metadata": { "userProfile": {} }
  },
  "timestamp": "1756742400000"
}

membership.updated

Fires when a membership’s active state flips — an activation or a lapse. The payload has the same shape as membership.created.

A role change, or a renewal that only moves validUntil while the membership stays active, does not produce a webhook. Poll or re-read the membership if you need those.

Field Type Description
membershipId string Identifier of the membership being updated.
isActive boolean The state after the change.
wasActive boolean The state before the change.
role string The role the user holds in the community.
validFrom string Start of the membership window, ISO-8601.
validUntil string End of the membership window, ISO-8601.
lastPaymentMethod string The payment method most recently used for this membership.

The pair wasActive and isActive tells you which direction the membership moved:

wasActive isActive Meaning
false true Activation — grant access.
true false Lapse — revoke access.

wasActive is always the negation of isActive — every membership event is a transition. There is no “nothing changed” delivery.

Deliveries are unordered and can repeat, so do not toggle a flag on receipt — compare against the state you hold and treat isActive and validUntil from the payload as authoritative.

{
  "event_type": "membership.updated",
  "event_id": "insy_event_1756831245880_9qz4m7t2v1c",
  "data": {
    "membershipId": "4f9a1c2e-7b3d-4e58-9c01-6d2f8a5b3e17",
    "isActive": false,
    "wasActive": true,
    "role": "member",
    "validFrom": "2025-09-01T16:00:00.000Z",
    "validUntil": "2025-09-02T16:00:00.000Z",
    "lastPaymentMethod": "stripe",
    "metadata": { "userProfile": {} }
  },
  "timestamp": "1756831245880"
}

payment.success

Fires when a payment completes. Delivered to the webhooks of the community’s owner, admin and moderator members.

Field Type Description
paymentId string Identifier of the payment. Use it, together with the event id, to keep your bookkeeping idempotent.
membershipId string The membership this payment belongs to.
paymentStatus string Status of the payment. For payment.success this is always completed — the event type is derived from it.
paymentMethod string How the payment was made.
paymentType string What kind of payment it is. The values are subscription, one_time and refund.
amountCents integer Amount in minor units of currency. 4900 with "currency": "pln" is 49.00 PLN.
currency string Currency code for amountCents. Case is not normalised — Stripe-settled payments send the lowercase ISO code (pln, usd), crypto settlements may send uppercase. Compare case-insensitively.
periodStart string, optional Start of the paid period, ISO-8601.
periodEnd string, optional End of the paid period, ISO-8601.
durationMonths integer, optional Length of the paid period in months.
{
  "event_type": "payment.success",
  "event_id": "insy_event_1756742463127_7hd4p9s2n6r",
  "data": {
    "paymentId": "b81d5f60-3c2a-4d19-95e7-0af6c3241b8d",
    "membershipId": "4f9a1c2e-7b3d-4e58-9c01-6d2f8a5b3e17",
    "paymentStatus": "completed",
    "paymentMethod": "stripe",
    "paymentType": "subscription",
    "amountCents": 4900,
    "currency": "pln",
    "periodStart": "2025-09-01T16:00:00.000Z",
    "periodEnd": "2025-10-01T16:00:00.000Z",
    "metadata": { "userProfile": {} }
  },
  "timestamp": "1756742463127"
}

payment.failed

Fires when a payment does not complete. In practice this means one thing: a subscription renewal invoice Stripe could not charge, typically a declined card. Same field names as payment.success, but fewer of them — see the note below.

Field Type Description
paymentId string Identifier of the failed payment.
membershipId string The membership the payment was for.
paymentStatus string Status of the payment. For payment.failed this is always failed.
paymentMethod string How the payment was attempted. Today this is always stripe, since only a Stripe renewal charge produces the event.
paymentType string What kind of payment it was. Today this is always subscription, for the same reason.
amountCents integer Amount in minor units of currency.
currency string Currency code for amountCents. Case is not normalised — see the note under payment.success.

A failed payment is not, by itself, a loss of access. The membership state is carried by membership.updated: expect a lapse only when a later event reports wasActive: true with isActive: false.

{
  "event_type": "payment.failed",
  "event_id": "insy_event_1756831188441_c5w8j1r4y0k",
  "data": {
    "paymentId": "e02b7a94-6d15-42c8-b3f1-58ac0d97e264",
    "membershipId": "4f9a1c2e-7b3d-4e58-9c01-6d2f8a5b3e17",
    "paymentStatus": "failed",
    "paymentMethod": "stripe",
    "paymentType": "subscription",
    "amountCents": 4900,
    "currency": "pln",
    "metadata": { "userProfile": {} }
  },
  "timestamp": "1756831188441"
}

digital.product.purchase

Fires when a digital product is purchased. Unlike the other four, this event is delivered to the webhooks of the product’s creator, not to community moderators.

Field Type Description
membershipId string The membership created for the purchase.
productVariantId string The specific variant of the product that was bought.
paymentId string The payment behind the purchase.
amountCents integer Amount in minor units of currency.
currency string Currency code for amountCents. This event is always Stripe-settled, so the code is lowercase (pln, usd).
validFrom string Start of the access window, ISO-8601.
validUntil string End of the access window, ISO-8601.
{
  "event_type": "digital.product.purchase",
  "event_id": "insy_event_1756744812004_m2v6b3t9q7d",
  "data": {
    "membershipId": "a17c8e42-9f05-4b6d-8e31-27c9a4d05f6b",
    "productVariantId": "5d3f0b71-8a24-4c96-a0e5-1b7f62d84c39",
    "paymentId": "c94e2d18-7b60-4f53-8a2c-3e15d097ab42",
    "amountCents": 12900,
    "currency": "pln",
    "validFrom": "2025-09-01T16:40:12.004Z",
    "validUntil": "2026-09-01T16:40:12.004Z",
    "metadata": { "userProfile": {} }
  },
  "timestamp": "1756744812004"
}

Routing the five types

A single handler receives everything, so the dispatch is a switch with a default branch that does nothing but still acknowledges.

function handle(event) {
  switch (event.event_type) {
    case "membership.created":
      // A created membership is not always active — grant only when it is.
      return event.data.isActive ? grantAccess(event.data) : undefined;
    case "membership.updated":
      return event.data.isActive
        ? grantAccess(event.data)
        : revokeAccess(event.data);
    case "payment.success":
      return recordPayment(event.data);
    case "payment.failed":
      return flagDunning(event.data);
    case "digital.product.purchase":
      return fulfilProduct(event.data);
    default:
      // Unknown type: acknowledge and move on.
      return undefined;
  }
}

Signature verification comes first — see Verifying signatures.

Was this page helpful?