member.* events
Member (subscriber) lifecycle events.
A "member" in webhook events is the project-scoped subscriber (the ProjectUser model), not a team member. Fires on joining, churning, bans, and Telegram-side events driven by the bot.
Events
member.joined
Paid subscriber gains access to a project.
member.trial_joined
Trial subscriber joins a project.
member.converted
Trial converts to a paying subscription.
member.churned
Subscriber's last active subscription lapsed.
member.removed
Subscriber soft-deleted manually.
member.banned
Creator bans a subscriber.
member.unbanned
Ban lifted; subscriber returns to churned.
member.kicked
Subscriber ejected from the whole project without a ban.
member.resource_added
Subscriber gains access to a specific resource.
member.resource_removed
Subscriber loses access to a specific resource.
| Event | Fires when |
|---|---|
member.joined | An access code is redeemed against a non-trial plan. Not paid checkouts. |
member.trial_joined | A cardless trial starts, or a code is redeemed against a trial plan. |
member.converted | A trial converts to a paying subscription (Stripe only). |
member.churned | A subscriber's subscription ends without renewal. |
member.removed | A subscriber is removed without being banned (e.g., kicked for inactivity). |
member.banned | A creator bans a subscriber. |
member.unbanned | A ban is lifted. |
member.kicked | A subscriber is ejected from the project without a ban: status goes to churned, active subscriptions are cancelled, and every resource grant is revoked. |
member.resource_added | A subscriber is added to an additional resource (channel, group). |
member.resource_removed | A subscriber is removed from a specific resource. |
See the dedicated pages above for full payload, field reference, and caveats per event.
Example payload
Subscription-driven member events (joined, trial_joined, converted, churned, resource_added, resource_removed) carry flat identifier fields:
{
"id": "evt_01HX...",
"type": "member.joined",
"created_at": "2026-05-18T10:05:00Z",
"api_version": "2026-05-01",
"project_id": "7f3d1c92-8b45-4e6a-9d21-5c8e0a4b6f13",
"data": {
"subscription_id": "5b7e2d40-1a86-4c39-97f2-e83d0b16c5a4",
"project_id": "7f3d1c92-8b45-4e6a-9d21-5c8e0a4b6f13",
"plan_id": "c4e82f16-93a7-4d5b-b81c-6e0f27a94d3b",
"subscriber_id": "2a91c4e7-6f38-4b52-8e0d-9c1a7b3f5d80",
"access_code": "SLATE-SUNDAY-4F2A9"
}
}Moderation events (banned, unbanned, kicked, removed) instead nest a subscriber object:
{
"id": "evt_01HX...",
"type": "member.banned",
"created_at": "2026-05-18T10:05:00Z",
"api_version": "2026-05-01",
"project_id": "7f3d1c92-8b45-4e6a-9d21-5c8e0a4b6f13",
"data": {
"subscriber": {
"id": "2a91c4e7-6f38-4b52-8e0d-9c1a7b3f5d80",
"telegram_user_id": null,
"status": "banned"
},
"reason": "Spamming referral links"
}
}data.subscriber.telegram_user_id is always null — the member's Telegram id lives on the linked chat row, not on the member. Resolve it from data.subscriber.id.
Guarantees
member.joinedandmember.trial_joinedfire only for zero-payment acquisition — access-code redemptions and cardless trials. Paid checkouts emitsubscription.activatedwith nomember.*join event.member.trial_joinedaccompaniessubscription.trial_startedon the cardless-trial and access-code paths.member.convertedfires at most once per trial conversion, and only for Stripe.- No
member.*payload carries the subscriber's email. Resolve subscriber detail withGET /v1/projects/{project}/members/{member}usingsubscriber_id. - The members endpoint returns
email(member-chosen and verified) andbilling_email(taken from their payment provider, unverified). Both require a token withproject-user:view.
Required abilities
Tokens subscribing to member.* events must carry project-user:view.
How is this guide?