Event management

Event management

Every Cobuntu event flows through one of four attendance lifecycles depending on two switches the organiser sets:

  1. Pricing — is the tier free or paid?
  2. Approval — does a host need to review applications before confirming?

The cross-product is four distinct flows. This page is the reference for each one — what statuses an attendance can carry, how capacity is enforced, and what webhooks fire so you can integrate from your own system instead of (or alongside) Cobuntu's email delivery.

Mental model. Each attendance carries one of five statuses: PENDING, PENDING_PAYMENT, APPROVED, REJECTED, CANCELLED. Together with cancellationReason when the row is cancelled, they describe the full lifecycle. Everything below is how those statuses get set and what fires when they do.

1. The four flows

Free + no approval

The simplest path. Member clicks Register and they're in.

[Member clicks Register]


   APPROVED ✓

       ├──► event.attendance.registered

       └──► [System] Event registration confirmation
            (default automation — emails the member)

No host action. Seat reserved immediately.

Free + approval required

Member submits an application; host approves or rejects.

[Member clicks Apply]


   PENDING

       ├──► event.attendance.application_received      (applicant)
       └──► event.attendance.application_awaiting_review (organiser)

       ├── host approves ──► APPROVED ✓
       │                       ├──► event.attendance.approved
       │                       └──► event.attendance.registered

       └── host rejects ───► REJECTED
                               └──► event.attendance.rejected

Two webhooks fire on application: one applicant-facing, one organiser-facing. The organiser-facing webhook payload includes a notifiedRecipients array (host + community leaders) so you can fan out from your own system if you've disabled the default automation.

Paid + no approval

Stripe-driven. The member completes Checkout; on payment success the attendance is created and the receipt webhook fires.

[Member clicks Buy ticket]


   Stripe Checkout

       ├── payment.succeeded ──► APPROVED ✓ + sale row
       │                          ├──► sale.completed
       │                          ├──► event.ticket.purchased
       │                          └──► event.attendance.registered

       └── payment.failed / abandoned ──► (no row)

The attendance row only exists after Stripe confirms payment — there's no holding mechanism during the checkout session itself.

Paid + approval required

Three steps: apply → approve → pay. The host approves the application first; the member then has 48 hours to complete Stripe Checkout before the seat is released.

[Member clicks Apply]


   PENDING

       ├──► event.attendance.application_received      (applicant)
       └──► event.attendance.application_awaiting_review (organiser)

       ├── host approves ──► (is the applicant a host of this event?)
       │                       │
       │                       ├── YES ──► APPROVED ✓
       │                       │             └──► event.attendance.approved
       │                       │
       │                       └── NO  ──► PENDING_PAYMENT
       │                                     ├──► event.attendance.payment_pending
       │                                     │     (carries paymentUrl + paymentExpiresAt)
       │                                     │
       │                                     ├── member pays ──► APPROVED ✓
       │                                     │                     └──► event.attendance.registered
       │                                     │
       │                                     ├── 48h elapses ───► CANCELLED
       │                                     │                     (PAYMENT_TIMEOUT)
       │                                     │                     └──► event.attendance.cancelled
       │                                     │
       │                                     └── host un-approves ─► CANCELLED
       │                                                              (ADMIN_REMOVAL)
       │                                                              └──► event.attendance.cancelled

       └── host rejects ────► REJECTED
                                └──► event.attendance.rejected

Host carve-out. Any user who is a CREATOR or CO_HOST of the event bypasses PENDING_PAYMENT and lands in APPROVED on host approval. Hosts don't pay for their own event. To give a comp seat to someone else without putting them through the payment flow, add them as a CO_HOST first and then approve their application.

Pay-now link. The event.attendance.payment_pending webhook payload carries paymentUrl (a single-use signed Stripe Checkout link) and paymentExpiresAt. Subscribers can use these to email the member from their own system if they've disabled the default automation.

2. Status semantics

StatusMeansHolds seat?Sale on file?
PENDINGApplication submitted, host reviewingyesno
PENDING_PAYMENTHost approved; member has 48h to payyesno
APPROVEDConfirmed attendance — free, or paid + paidyesyes (paid events)
REJECTEDHost declined during application reviewnono
CANCELLEDExited from APPROVED or PENDING_PAYMENTnorefunded if previously paid

CANCELLED rows carry an additional cancellationReason:

  • USER_REQUEST — member self-cancelled (or used the magic-link unsubscribe).
  • ADMIN_REMOVAL — host removed them post-approval.
  • PAYMENT_TIMEOUTPENDING_PAYMENT row sat unpaid for 48 hours.

REJECTED and CANCELLED are distinct on purpose: they reach the member via different webhook events and the host sees them in different tabs of the admin attendees list.

3. Capacity

Capacity is enforced per tier. There is no event-level capacity — an event with multiple tiers is considered "full" only when every non-unlimited tier has hit its limit.

The following statuses hold seats against capacity:

  • PENDING
  • PENDING_PAYMENT
  • APPROVED

REJECTED and CANCELLED rows do not count toward capacity — the seat is released for the next applicant.

Capacity is enforced atomically, so two members racing for the last seat will see one succeed and the other receive a "tier is at full capacity" error. No oversell.

Guest registrations (those keyed by guestEmail instead of userId) count toward capacity exactly like authenticated registrations.

4. Webhooks

EventWhen it fires
event.publishedEvent was made publicly visible
event.cancelledEvent itself was cancelled
event.attendance.application_receivedMember applied to a requires-approval event (applicant-facing)
event.attendance.application_awaiting_reviewSame moment — organiser-facing, includes notifiedRecipients
event.attendance.registeredAttendance entered APPROVED state
event.attendance.approvedHost approved a PENDING row for a free event (or host carve-out)
event.attendance.payment_pendingHost approved a PENDING row for a paid event; member must pay within 48h. Carries paymentUrl, paymentExpiresAt, tierName, tierPriceCents, tierCurrency.
event.attendance.rejectedHost rejected a PENDING row
event.attendance.cancelledAttendance exited to CANCELLED. Carries cancellationReason distinguishing USER_REQUEST / ADMIN_REMOVAL / PAYMENT_TIMEOUT.
event.reminder.sentA scheduled pre-event reminder went out

Every webhook payload follows the standard envelope — see Verify signatures and Retry & delivery. Subscribe to whichever events you need under admin → Integrations → Webhooks.

For the per-event payload schemas, see the webhook catalog.

5. Replacing Cobuntu's emails with your own

Every customer-facing email Cobuntu sends is delivered by a [System] automation that listens to one of the webhook events above. The same data the email template uses is also in the webhook payload, so you can disable the automation and dispatch the email from your own ESP / CRM / Slack workflow instead.

To disable a Cobuntu-sent email:

  1. Admin → Automations
  2. Find the [System] flow (e.g. [System] Event payment pending)
  3. Set its status to DRAFT or PAUSED

The lifecycle event continues to fire (so your webhook still receives the payload). Only Cobuntu's email side-effect is suppressed.

Default automationListens to
[System] Event registration confirmationevent.attendance.registered (free events)
[System] Event application receivedevent.attendance.application_received
[System] Event application awaiting reviewevent.attendance.application_awaiting_review
[System] Event payment pendingevent.attendance.payment_pending
[System] Event application approvedevent.attendance.approved (free events)
[System] Event application rejectedevent.attendance.rejected
[System] Event attendance cancelledevent.attendance.cancelled
[System] Event ticket purchasedevent.ticket.purchased
[System] Event reminderevent.reminder.sent
[System] Event cancelled — attendeesevent.cancelled

6. Capabilities — what hosts and members can do

Cobuntu events span discovery, registration, multi-tier pricing, member discounts, donations, custom forms, capacity management, sale windows, invitations, agenda, reminders, and refunds. This section is a flat catalog of what you can build. Each capability is live on the Cobuntu platform and available via the admin panel or REST API.

CategoryWhat you can do
Discovery & browseMembers browse your event listings; you control which events are public vs. members-only
Registration flowsOffer free instant registration, free with approval, or paid checkout — one per tier
Pricing & tiersCreate price tiers; each tier can be one-time, pay-what-you-want, or an installment plan
Member pricingApply lower rates to members holding a specific membership tier
Founding-member ratesGrant a founding cohort a locked-in price for the first N buyers
DonationsAllow buyers to add an optional donation alongside a ticket
Per-tier registration formsCollect custom data at signup — different questions per tier
Capacity rulesSet limits per tier; the seat is reserved at registration time
Multi-community listingA member can register for events across multiple communities they belong to
InvitationsInvite attendees by email one-at-a-time or upload a CSV
Magic-link guest managementSend guests a one-click RSVP link; no account required
RemindersTrigger automated emails before the event; optional WhatsApp reminders
Calendar exportsAttendees can add your event to Google Calendar or Apple Calendar; .ics support
Agenda & itineraryPublish a timeline of sessions / talks / activities with titles and times
Sharing & embedsMembers share events on social media; embed event cards on your landing page
Multi-event cartBuyers can add multiple event tickets in one checkout session
Approvals & moderationFor events requiring approval, hosts review and approve or reject applications
Cancellations & refundsMembers can cancel; paid attendees receive refunds subject to the refund window
Webhooks & integrationsEvery lifecycle event (register, approve, pay, cancel) fires a webhook for your system
Duplicate eventClone an event and edit details; no re-entering speaker bios or agenda from scratch
Cancel eventNotify all attendees if you need to cancel; refunds are issued to paid attendees
Format flexibilityHost online-only, in-person, or hybrid events with distinct registration UX per format
Time-zone handlingYou set the event's timezone; all times render correctly for attendees in any locale

6.1 Registration flows

Mental model. Every event tier defines who can register and under what conditions. Cobuntu offers four fundamental registration flows: (a) free, instant approval, (b) free with host approval, (c) paid, instant approval, and (d) paid with host approval. Both authenticated members and anonymous guests can use every flow.

The four flows

FlowTier pricingApprovalResult
InstantFreeNoneAttendee registered immediately; confirmation email sent
ApprovalFreeHost reviews, approves/rejectsApply request sent to host; attendee waits for decision; approval email on success
CheckoutPaidNoneGuest enters email (if not authenticated), Stripe collects payment, attendee registered on success
Apply then PayPaidHost reviews firstApply request sent to host; if approved, attendee receives a payment link; registration completes on payment

Registration participant types

Authenticated members Members signed into the community have their account data pre-filled. On registration, their account is linked to the attendance record. For approval flows, the host can see the member's name, email, and profile picture.

Anonymous guests Guests who are not logged in register via email address. On registration, they provide an email (and optionally fill a tier form if configured). They receive a confirmation email containing:

  • For free RSVP: a button to cancel their registration
  • For paid events: links to view their ticket and request a refund (if eligible)
  • For events with forms: their submitted form answers

Guest emails can later claim an account by signing up with the same email address — all prior registrations and purchases retroactively appear in their account.

The Apply form (free and paid approval flows)

When a tier requires host approval, the registration surface becomes a form submission. Tiers can capture custom fields per event using a configurable form (see §6.5). The form collects:

  • Name, email, and other contact fields (pre-filled for authenticated members)
  • Tier-specific custom fields (e.g., dietary preferences, accessibility needs, company affiliation)

The host receives the application in their admin panel and can approve, reject, or request more information.

The user journey for paid approval flows

An applicant who chooses a paid, approval-gated tier moves across three surfaces:

  1. Registration form — the applicant fills out the tier's form and submits it. They see a confirmation that their application is under review.
  2. Approval email — once the host approves, the applicant receives an email confirming acceptance with a payment link.
  3. Payment + confirmation — the applicant pays via Stripe Checkout and receives a final ticket confirmation. Their seat is held against the tier's capacity from this point forward.

If the applicant is rejected, they receive a rejection email and no further action is required.

Examples

Example 1: Free event, instant approval Visitor clicks "Register" → email modal appears (guests only) → confirmation email arrives → attendee list updated immediately. No host action needed.

Example 2: Free event, host approval Visitor clicks "Apply" → form appears (optional, tier-specific) → host receives application in admin → host clicks "Approve" → attendee receives approval email → attendee listed as "Approved". If rejected, the applicant receives a rejection email.

Example 3: Paid event, instant approval Visitor clicks "Buy Ticket" → guest email modal (if not authenticated) → Stripe Checkout session opens → payment completes → confirmation email with ticket details → attendee registered immediately.

Example 4: Paid event, host approval required Visitor clicks "Apply" → form (if configured) → host receives application → host approves → attendee gets a payment email → attendee pays via Stripe → ticket confirmation sent → registration finalized.

6.2 Pricing & tiers

An event can have unlimited tiers — for example, "Early bird", "Standard", "VIP", "Member-only". Each tier carries: name, description, price, currency, capacity (or unlimited), display order, and a flag indicating whether sales are currently open.

Pricing types per tier:

  • One-time fixed price — buyer pays once for the ticket. The ticket is valid until the event date.
  • Pay what you want (PWYW) — buyer chooses an amount within a minimum and maximum range (e.g. €15–€50). Useful for sliding-scale pricing or suggested donations.
  • Installment plans — buyer commits to a total amount split across multiple charges (e.g., €90 total split into 3× €30 monthly). Useful for higher-priced events.
  • Free — set price to zero; functions like a free RSVP.

Currency support — choose from the same 19 currencies as memberships (see Memberships for the full list). All price values are in the smallest currency unit (cents for EUR/USD/GBP, yen for JPY).

Capacity and availability — each tier can specify a maximum attendee count or unlimited. The API reports both the configured maximum and the count already claimed. A tier with sales disabled does not accept new RSVPs.

Buyer experience — the picker on the event detail page shows one row per tier; if only one tier is configured, no picker is shown. Tiers display in the host's configured order. The buyer selects a tier, optionally answers any tier-level form questions, and proceeds to checkout (for paid tiers) or completes the RSVP (for free tiers).

Member-only tiers — a tier can be restricted to members of the community. When a logged-in member purchases via the checkout endpoint with their identity forwarded (see §7), member-only tiers become available.

Important: all price values are in the smallest currency unit (cents for EUR/USD/GBP, yen for JPY). A value of 1500 means €15.00, not €1,500. For zero-decimal currencies (JPY, KRW) the value equals the displayed amount.

6.3 Member-only pricing & founding rates

A tier can carry two price modifiers that stack independently: a discounted price for community members, and a limited-time "founding" rate for the first N buyers.

Member-only pricing

A tier can define a member price alongside its regular price. Members of the community automatically see and pay the member price; non-members see the regular price. No coupon or code is required — the system resolves the buyer's membership status when the picker renders and at checkout, so the correct price is shown and charged.

Example: an "Early Bird" tier with price: 2000 and memberPrice: 1500 (in cents) shows non-members "Pay €20" and members "Pay €15". Both paths use the same registration and Stripe Checkout UX; only the resolved price differs.

Member pricing applies to every active price point on a tier. If a tier offers multiple price points for different contexts, each can have an independent member-only variant.

For the technical detail on how member identity is forwarded at checkout time, see the REST API section below.

Founding-member rates

A tier can offer a founding rate — a discounted price for the first N buyers. Configure on the tier:

  • foundingPrice — the discounted price (in the smallest currency unit).
  • foundingMemberCap — max number of buyers eligible for the discount. Once reached, new buyers see the regular price.

Founding rates apply to the tier's standard price points. Once the cap is reached, new buyers automatically fall back to the regular (or member) rate.

Composing both modifiers

A tier can have both a member price and a founding rate. The picker resolves and shows the best rate the buyer qualifies for:

price:              2500   (€25, regular)
memberPrice:        1800   (€18, for members)
foundingPrice:      1200   (€12, for first 50 buyers)
foundingMemberCap:  50

A non-member within the first 50 buyers sees "Pay €12" (founding rate). After the cap is reached, new non-members see "Pay €25" (regular). A member (regardless of cap) sees "Pay €18" (member rate).

The resolution order is: if founding cap not reached, apply founding rate → else if member, apply member rate → else regular price.

6.4 Donations

Every event can carry an optional donation sidecar — independent of the ticket purchase itself. The host configures the donation behavior, and buyers opt in at checkout.

Donation modes

The host chooses one of three donation modes:

  • Fixed amounts — the host defines a chip group (e.g., €5 · €10 · €25). Buyers pick one.
  • Pay what you want — buyers enter any amount within an optional minimum and maximum range.
  • Disabled — no donation prompt surfaces to the buyer.

How donations work in checkout

For paid events, donations process in the same Stripe Checkout session as the ticket purchase. Both are billed in a single transaction, but recorded separately — the donation appears as its own line item so hosts can distinguish donation revenue from ticket revenue in their reports.

For free events, when a buyer attaches a donation to their RSVP (via the donation field on the public /rsvp endpoint), the RSVP routes through Stripe Checkout instead of registering instantly. The buyer pays the donation, the seat is reserved on payment completion, and the same confirmation email fires as for a regular free RSVP.

Donation amounts are always expressed in the smallest unit of the configured currency (cents for USD/EUR, sen for JPY, etc.), matching the ticket price format. Donations are optional — a buyer can complete a ticket purchase or a free RSVP without adding a donation.

Configuration

The donation config is stored on each event as a JSON blob:

  • enabled — boolean; gates whether the donation prompt appears
  • mode'fixed' or 'pwyw'
  • currency — the currency code (e.g., 'EUR', 'USD'); must match the event's pricing currency
  • amounts — array of smallest-unit integers; required when mode='fixed' (max 8 amounts)
  • minAmount — optional smallest-unit floor; only valid when mode='pwyw'
  • label — optional UI label (max 100 chars); defaults to "Add a donation" if omitted

Passing null or { enabled: false } disables the donation prompt. The host can toggle donations on/off without losing their configuration — the amounts and mode are preserved.

Today the host sets this config in the cobuntu admin (Events → edit event → Donations section). A public-API endpoint to configure donations programmatically is on the roadmap.

6.5 Custom registration forms

Each tier can have its own registration form. When a buyer selects a tier, the form appears before payment (or before submitting an application for free tier plus approval-gate flows). The form submission blocks checkout until all required fields are filled.

Supported field types: short text, long text, single-select dropdown, multi-select checkboxes, email, phone, URL, date, and consent checkbox. Each field is configurable as required or optional.

Where responses are stored: Form answers attach to the event attendance record. Hosts see responses inline on the attendee list and can export them in CSV. For approval-gated events, the host reviews responses when deciding whether to accept or reject each application.

Common use cases:

  • Dietary restrictions or allergies
  • Name for ticket pickup
  • Project links (hackathons, portfolios)
  • Consent acknowledgements (NDA, code of conduct)
  • Questions about participant experience level

Event duplication: When you duplicate an event, its forms copy over with new tier IDs. Form responses from the original event do not carry forward — each event maintains its own set of submissions.

6.6 Sale windows, hidden tiers, draft events, and time zones

Per-tier sale windows. Each tier can carry an optional sale start timestamp, sale end timestamp, or both. The tier is only buyable inside that window.

  • Before the sale start: the tier shows as "coming soon" with the start date.
  • Between start and end: the tier is buyable.
  • After the sale end: the tier shows as "sale closed".
  • Null on either side means no constraint on that side.
  • Sale windows can be cleared with an explicit clear flag — passing a blank value is treated as no-op (so partial-update PUTs don't unintentionally wipe a configured window).

Per-tier publication. A tier can be marked unpublished to hide it entirely. Useful for staging tiers in advance without exposing them on the picker.

Draft events. A new event starts as draft. Drafts are visible only to hosts and community leaders. Publishing emits the event.published webhook and makes the event visible to members and the public API.

Event visibility — two-axis model. Visibility is configured on two independent axes that compose at request time:

AxisWhere you set itValues
Events pageAdmin → Settings → Pages → /eventsPUBLIC (anyone) · MEMBERS_ONLY (signed-in members)
Individual eventAdmin → Events → [event] → VisibilityPUBLIC (anyone) · MEMBERS_ONLY (signed-in members)

Composition rule — an event can deviate upward, not downward. A community can lock the /events listing as MEMBERS_ONLY and still expose individual events as PUBLIC to anonymous visitors via direct URL. The reverse — a public listing with a members-only event — keeps the event on the listing but gates the detail page itself.

Common use case. A private community wants to keep their members directory and most pages gated, but expose a single networking event publicly so anyone can register. Set the /events page to MEMBERS_ONLY and the one event to PUBLIC. Anonymous visitors with the event URL land directly on the detail page and can register; everyone else who navigates to /events first sees the sign-in gate.

View gate vs action gate. When an event is MEMBERS_ONLY:

  • An anonymous visitor sees a sign-in / join-community CTA instead of the event detail (not a hard 404 — better conversion).
  • Registration and ticket purchase (including the REST API and webhook-driven flows) are blocked with a 403/404 unless the caller forwards a member token.

When an event is PUBLIC:

  • Anyone with the URL can view the detail and register as a guest. Members get member pricing automatically if a member-priced tier exists.

Time-zone handling. Every timestamp is stored in UTC. Each event carries a timezone field (an IANA time-zone identifier, e.g., Europe/Lisbon). The API serializes timestamps as UTC instants. Buyer-facing UI converts to the event's display time zone using the event's timezone field.

6.7 Capacity

Capacity is enforced per tier. There is no event-level capacity — an event with multiple tiers is "full" only when every non-unlimited tier has hit its limit.

Statuses that hold a seat against capacity:

  • PENDING — free + approval applicants under review
  • PENDING_PAYMENT — paid + approval applicants in the 48-hour pay-now window
  • APPROVED — confirmed attendees

REJECTED and CANCELLED rows release the seat for the next applicant.

Guest registrations (keyed by guest email) count toward capacity exactly like authenticated registrations.

Capacity is enforced at registration time — racing applicants for the last seat see one succeed and the other receive a "tier is at full capacity" response. See Status semantics for the full status list.

6.8 Invitations

Hosts can invite people directly to an event by email. Each invited person receives an email with a one-click "I'll be there" link that signs them in automatically — no signup required for free events. For paid events, invited members proceed through a magic-link checkout flow.

Invitations track state: pending, accepted, rejected, or cancelled. Hosts can view each invitation's status in the attendees interface. If an invitation hasn't been accepted after being sent, hosts can resend it from the attendees view without creating duplicates.

Bulk invitations via CSV are supported. The system normalizes emails (case-insensitive dedup) and skips people already attending the event. Bulk invitations are rate-limited to prevent abuse; if rate limits are exceeded, the system reports partial failures for retry.

Invitation state model:

StateMeaningResendableHost action
PENDINGSent, not yet clickedYesResend or cancel
ACCEPTEDRecipient clicked link and confirmedNoView details
EXPIREDLink validity expired (timeout elapsed)YesResend to reactivate
CANCELLEDHost cancelledYesResend to re-invite

The invitation flow:

When a host sends invitations:

  1. The system creates an invitation record for each email.
  2. An email is sent with a one-click acceptance link that verifies the recipient's email and event enrollment.
  3. The recipient clicks the link; the system checks invitation status and identity.
  4. For existing Cobuntu members: the member is signed in automatically and redirected to the event page.
  5. For new users: they are redirected to a signup surface; once signed up with the invited email, the invitation can be accepted.
  6. The authenticated user clicks "I'll be there" → creates the attendance record, marks the invitation ACCEPTED.
  7. If event capacity is full, acceptance is rejected.

Invitation email contents:

The sender's name appears on the invitation ("Invited by Alice") so the recipient knows who sent it. For paid events, the email shows the tier details. For free events, it shows "Free event" messaging. Event date, time, location, and community branding are included.

Re-sending invitations:

Expired invitations can be re-sent and are restored to PENDING status. CANCELLED invitations can also be re-sent, which re-invites the recipient. Re-sending increments the send counter and updates the timestamp, but does not create a duplicate invitation row. Re-sending is blocked once the event has started.

Bulk CSV upload:

Hosts can upload a CSV with email addresses (one per row). The system validates format, dedupes by email, and reports: success count, failed count, already-invited (PENDING), and already-attending. Failed rows are listed in the response for debugging.

Security:

Invitations are tied to a specific email address and cannot be transferred to another user. The link will only complete acceptance for the recipient whose email matches the invitation.

6.9 Reminders & calendar

After registering, members can configure reminder preferences per attendance directly from the event detail page:

Email reminders — enabled by default. Members receive reminders at 1 week, 2 days, 1 day, and 1 hour before the event. Toggle on/off without unregistering.

WhatsApp reminders — opt-in, requires a verified phone number on the member's profile. Same schedule as email (1 week, 2 days, 1 day, 1 hour before the event). Members toggle on/off from the event detail page.

The reminder times are fixed and set by the platform — not per-event by the host. The backend checks regularly for attendees who fall into each reminder window (7 days, 2 days, 1 day, 1 hour) and sends the corresponding message. Each reminder is sent only once per attendance per channel.

Calendar integrations. Every event detail page exposes "Add to Calendar" — produces an .ics (iCalendar) file compatible with Apple Calendar, Google Calendar, Outlook, Yahoo Calendar, and any RFC 5545–compliant calendar app. Direct one-click Google Calendar add is also available.

For members who have already registered for the event, the calendar entry includes the member's confirmation and access information (location, time, event URL).

6.10 Cancellations & refunds

Member self-cancellation

A logged-in attendee can cancel their event registration from the event detail page by clicking "Cancel registration". For free events, cancellation is immediate — their attendance is removed from the event and capacity becomes available for others.

For paid events, the cancellation triggers a refund eligibility check before proceeding:

  • In escrow and over 7 days before start date: the attendee receives a full refund via Stripe. The cancellation is then recorded on the event.
  • Less than 7 days before start date, or event has already started: refund is not available. The attendee is presented with two options: proceed with "forfeit ticket" (leave without refund), or cancel the operation.
  • Event date not yet set (TBD): the 7-day window is replaced with a 30-day purchase window. Refunds are available for 30 days from purchase date.

In all cases, the attendee receives a confirmation email summarizing the outcome.

Guest self-cancellation (magic-link)

Guests receive a one-click "manage my registration" link in their confirmation email. The link opens a confirmation page that displays the event details and their current attendance status. Guests can then cancel their registration.

The same refund eligibility logic applies:

  • Free events are immediately cancelled.
  • Paid events follow the escrow + timing rules above (7 days before start, or 30 days for TBD events).

The guest is verified via the magic-link token, which is tied to their email address and attendance. Expired tokens are rejected.

Host-side attendee removal

Event hosts can remove an attendee from the attendees view in the host dashboard. Removed attendees are notified by email and their seat is released back to the tier's capacity. Refunds for paid attendees follow the standard refund eligibility rules (escrow status and the 7-day window).

Hosts can see the full list of attendees, including cancelled ones, and can filter by status.

Cancellation reasons

Every cancellation — whether member-initiated, guest-initiated, or host-initiated — carries a reason that is recorded:

  • member_request — member self-cancelled
  • guest_request — guest self-cancelled via magic-link
  • host_removal — host removed the attendee

Hosts see the reason in the "Cancelled" tab of their attendees view. Webhook subscribers receive the reason in the event.attendance.cancelled webhook payload, allowing community automation to act on the reason (e.g., notify members, log churn metrics).

Attendance history

Cancellations are preserved as soft-flagged records — the attendance row retains all original data (name, email, tier, form answers) and is marked as cancelled. This allows hosts to review the full history of who registered, paid, and cancelled, including the cancellation reason and timestamp.

6.11 Approvals & moderation

Important: Approval flows are opt-in per event. Hosts must enable "Requires approval" when creating or editing an event.

For events requiring approval, hosts review applications from the attendees view. Pending applications show the applicant's name, email, custom form responses (if any), and submission timestamp.

Hosts can approve or reject each application:

  • Approve → sends "Your ticket is ready" email with a pay-now link (for paid tiers) or confirmation (for free).
  • Reject → sends a rejection email.

Hosts can also:

  • Resend pay-now email if the approved buyer lost the original message.
  • Manually add attendee by email, bypassing the apply flow entirely. The attendee receives an event-added notification.

6.12 Cart (multi-event checkout)

Members can add tickets from multiple events to a single cart and check out once. This reduces friction for members buying multiple experiences.

Cart behavior:

  • Each item shows event name, tier, date, and price.
  • Items can be removed individually before checkout.
  • Checkout creates a single Stripe session covering all tickets (and any donations across those events).
  • Refunds work per-ticket, not per-cart.

6.13 Online, in-person, or hybrid

Every event carries two optional location fields: location (street address for in-person) and online URL (meeting link).

Event typeHost setsMember sees
In-personlocation"View on Maps" link (opens Apple Maps / Google Maps)
Onlineonline URLMeeting link hidden until approved; RSVP confirmation shows the URL
HybridbothBoth options visible; member chooses how to attend

Important: For online events, the meeting URL is hidden until the member is approved. This prevents URL leakage from public event pages.

6.14 Multi-community listing

An event can be listed across multiple communities. A host in one community can list (with permission) on another community they belong to.

Each community listing is independent:

  • Hidden (draft, not visible to members).
  • Pending (awaiting host approval to go live).
  • Active (live and bookable).

A single event can be active in one community, pending in another, and hidden in a third.

6.15 Sharing

Every event has a copyable share URL that directs members to the event detail page.

On mobile, native share opens the operating system share sheet (Messages, WhatsApp, Mail, etc.). On desktop, hosts and members copy the link manually or use the social-share button.

Shared links include the event slug and community tag, so recipients land on the correct event page regardless of domain.

6.16 Duplicate event

Hosts can duplicate an existing event to seed a new one. Duplication copies:

  • Event metadata (name, description, location, agenda).
  • All tiers (with fresh ids).
  • All registration forms attached to those tiers.
  • The community-listing relationships, starting hidden.

Duplication does not copy attendees, sales, invitations, or broadcasts.

6.17 Cancel event

Hosts can cancel a published event. Cancellation:

  • Marks the event as cancelled (it stays visible on the detail page so attendees aren't confused by a 404).
  • Fires the event.cancelled webhook.
  • Emails every approved attendee via the [System] Event cancelled — attendees automation.
  • Leaves refund processing to the host. For paid events the host follows up from the attendees view to refund within the eligibility window.

6.18 Webhook integrations

Every customer-facing email Cobuntu sends has a paired webhook event. Subscribers can disable the Cobuntu email (via the matching automation) and drive their own notifications from the webhook payload.

Full event list:

  • event.published / event.cancelled
  • event.attendance.application_received / event.attendance.application_awaiting_review
  • event.attendance.approved / event.attendance.rejected
  • event.attendance.payment_pending
  • event.attendance.registered
  • event.attendance.cancelled (carries cancellationReason + eventName)
  • event.ticket.purchased
  • event.reminder.sent

Payloads follow the standard signed-envelope shape — see Verify signatures and Retry & delivery.

7. REST API

The REST API exposes the public read + write operations on events. See the full reference at REST API: Events.

Read — list events, fetch an event by id or slug, list its tiers, fetch a tier's registration form.

Write — free RSVPs for community members holding a publishable API key. The response carries the resulting status:

  • APPROVED — free + no approval, member is in.
  • PENDING — free + approval required, host will review.

Paid checkout goes through Stripe Checkout (a separate flow); see the REST API: Events reference for the checkout endpoint shape and the apply-then-pay sequence used by paid + requires-approval events.

Every endpoint requires an X-API-Key header — see Authentication for scope details.