Event management
Every Cobuntu event flows through one of four attendance lifecycles depending on two switches the organiser sets:
- Pricing — is the tier free or paid?
- 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 withcancellationReasonwhen 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.rejectedTwo 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.rejectedHost 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
| Status | Means | Holds seat? | Sale on file? |
|---|---|---|---|
PENDING | Application submitted, host reviewing | yes | no |
PENDING_PAYMENT | Host approved; member has 48h to pay | yes | no |
APPROVED | Confirmed attendance — free, or paid + paid | yes | yes (paid events) |
REJECTED | Host declined during application review | no | no |
CANCELLED | Exited from APPROVED or PENDING_PAYMENT | no | refunded 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_TIMEOUT—PENDING_PAYMENTrow 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:
PENDINGPENDING_PAYMENTAPPROVED
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
| Event | When it fires |
|---|---|
event.published | Event was made publicly visible |
event.cancelled | Event itself was cancelled |
event.attendance.application_received | Member applied to a requires-approval event (applicant-facing) |
event.attendance.application_awaiting_review | Same moment — organiser-facing, includes notifiedRecipients |
event.attendance.registered | Attendance entered APPROVED state |
event.attendance.approved | Host approved a PENDING row for a free event (or host carve-out) |
event.attendance.payment_pending | Host approved a PENDING row for a paid event; member must pay within 48h. Carries paymentUrl, paymentExpiresAt, tierName, tierPriceCents, tierCurrency. |
event.attendance.rejected | Host rejected a PENDING row |
event.attendance.cancelled | Attendance exited to CANCELLED. Carries cancellationReason distinguishing USER_REQUEST / ADMIN_REMOVAL / PAYMENT_TIMEOUT. |
event.reminder.sent | A 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:
- Admin → Automations
- Find the
[System]flow (e.g.[System] Event payment pending) - Set its status to
DRAFTorPAUSED
The lifecycle event continues to fire (so your webhook still receives the payload). Only Cobuntu's email side-effect is suppressed.
| Default automation | Listens to |
|---|---|
[System] Event registration confirmation | event.attendance.registered (free events) |
[System] Event application received | event.attendance.application_received |
[System] Event application awaiting review | event.attendance.application_awaiting_review |
[System] Event payment pending | event.attendance.payment_pending |
[System] Event application approved | event.attendance.approved (free events) |
[System] Event application rejected | event.attendance.rejected |
[System] Event attendance cancelled | event.attendance.cancelled |
[System] Event ticket purchased | event.ticket.purchased |
[System] Event reminder | event.reminder.sent |
[System] Event cancelled — attendees | event.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.
| Category | What you can do |
|---|---|
| Discovery & browse | Members browse your event listings; you control which events are public vs. members-only |
| Registration flows | Offer free instant registration, free with approval, or paid checkout — one per tier |
| Pricing & tiers | Create price tiers; each tier can be one-time, pay-what-you-want, or an installment plan |
| Member pricing | Apply lower rates to members holding a specific membership tier |
| Founding-member rates | Grant a founding cohort a locked-in price for the first N buyers |
| Donations | Allow buyers to add an optional donation alongside a ticket |
| Per-tier registration forms | Collect custom data at signup — different questions per tier |
| Capacity rules | Set limits per tier; the seat is reserved at registration time |
| Multi-community listing | A member can register for events across multiple communities they belong to |
| Invitations | Invite attendees by email one-at-a-time or upload a CSV |
| Magic-link guest management | Send guests a one-click RSVP link; no account required |
| Reminders | Trigger automated emails before the event; optional WhatsApp reminders |
| Calendar exports | Attendees can add your event to Google Calendar or Apple Calendar; .ics support |
| Agenda & itinerary | Publish a timeline of sessions / talks / activities with titles and times |
| Sharing & embeds | Members share events on social media; embed event cards on your landing page |
| Multi-event cart | Buyers can add multiple event tickets in one checkout session |
| Approvals & moderation | For events requiring approval, hosts review and approve or reject applications |
| Cancellations & refunds | Members can cancel; paid attendees receive refunds subject to the refund window |
| Webhooks & integrations | Every lifecycle event (register, approve, pay, cancel) fires a webhook for your system |
| Duplicate event | Clone an event and edit details; no re-entering speaker bios or agenda from scratch |
| Cancel event | Notify all attendees if you need to cancel; refunds are issued to paid attendees |
| Format flexibility | Host online-only, in-person, or hybrid events with distinct registration UX per format |
| Time-zone handling | You 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
| Flow | Tier pricing | Approval | Result |
|---|---|---|---|
| Instant | Free | None | Attendee registered immediately; confirmation email sent |
| Approval | Free | Host reviews, approves/rejects | Apply request sent to host; attendee waits for decision; approval email on success |
| Checkout | Paid | None | Guest enters email (if not authenticated), Stripe collects payment, attendee registered on success |
| Apply then Pay | Paid | Host reviews first | Apply 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:
- Registration form — the applicant fills out the tier's form and submits it. They see a confirmation that their application is under review.
- Approval email — once the host approves, the applicant receives an email confirming acceptance with a payment link.
- 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
1500means €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: 50A 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 appearsmode—'fixed'or'pwyw'currency— the currency code (e.g.,'EUR','USD'); must match the event's pricing currencyamounts— array of smallest-unit integers; required whenmode='fixed'(max 8 amounts)minAmount— optional smallest-unit floor; only valid whenmode='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:
| Axis | Where you set it | Values |
|---|---|---|
| Events page | Admin → Settings → Pages → /events | PUBLIC (anyone) · MEMBERS_ONLY (signed-in members) |
| Individual event | Admin → Events → [event] → Visibility | PUBLIC (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
/eventspage toMEMBERS_ONLYand the one event toPUBLIC. Anonymous visitors with the event URL land directly on the detail page and can register; everyone else who navigates to/eventsfirst 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 reviewPENDING_PAYMENT— paid + approval applicants in the 48-hour pay-now windowAPPROVED— 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:
| State | Meaning | Resendable | Host action |
|---|---|---|---|
| PENDING | Sent, not yet clicked | Yes | Resend or cancel |
| ACCEPTED | Recipient clicked link and confirmed | No | View details |
| EXPIRED | Link validity expired (timeout elapsed) | Yes | Resend to reactivate |
| CANCELLED | Host cancelled | Yes | Resend to re-invite |
The invitation flow:
When a host sends invitations:
- The system creates an invitation record for each email.
- An email is sent with a one-click acceptance link that verifies the recipient's email and event enrollment.
- The recipient clicks the link; the system checks invitation status and identity.
- For existing Cobuntu members: the member is signed in automatically and redirected to the event page.
- For new users: they are redirected to a signup surface; once signed up with the invited email, the invitation can be accepted.
- The authenticated user clicks "I'll be there" → creates the attendance record, marks the invitation
ACCEPTED. - 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-cancelledguest_request— guest self-cancelled via magic-linkhost_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 type | Host sets | Member sees |
|---|---|---|
| In-person | location | "View on Maps" link (opens Apple Maps / Google Maps) |
| Online | online URL | Meeting link hidden until approved; RSVP confirmation shows the URL |
| Hybrid | both | Both 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.cancelledwebhook. - Emails every approved attendee via the
[System] Event cancelled — attendeesautomation. - 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.cancelledevent.attendance.application_received/event.attendance.application_awaiting_reviewevent.attendance.approved/event.attendance.rejectedevent.attendance.payment_pendingevent.attendance.registeredevent.attendance.cancelled(carriescancellationReason+eventName)event.ticket.purchasedevent.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.