# Bookable events (Agent Skill)

> Capacity-limited events with Stripe payments in a Next.js app: Checkout tickets, a card hold on free events that is released on check-in and captured on no-show, staff check-in, refunds and event cancellation.

Page: https://timerise.ai/skills/bookable-events

Agent Skill `bookable-events`: https://github.com/timerise-ai/bookable-events. Category: Payments & compliance. Runs on Next.js; backend: Firestore or Postgres behind an `EventStore` seam.

Latest release: v0.1.3 (2026-09-28).

Install: npx skills add timerise-ai/bookable-events. Timerise builds it on a brief; the price comes from the brief.

Classes, workshops, tournaments, meetups and open days all have the same shape: a fixed number of seats, a list of people who want one, and money that changes hands. Paid events sell tickets. Free events lose seats to people who sign up and never come.

Events are the easy half. The hard half is money moving while seats are scarce. A seat is taken before payment lands. A payment lands after the seat expired. A card hold lapses before a no-show can be charged. A webhook arrives twice and refunds a ticket twice.

This skill gives a coding agent the events module the way we build it: every change goes through one state machine inside a database transaction, and Stripe is called only after commit. Free events can take a refundable card hold that is released when the guest checks in and captured when they do not show. Staff check people in, refund tickets and cancel events, and every retry replays the first result.

Build bookable events with Stripe payments and an anti-no-show card hold: capacity-limited registration for free and paid events, Stripe Checkout tickets, refundable deposits on free events that are released when the guest checks in and captured when they don't show, staff check-in, refunds and event cancellation. Use when: (1) a venue, club, studio, school or community runs classes, workshops, tournaments, meetups or open days with limited seats, (2) free events suffer no-shows and need a deposit or card hold, (3) an existing event-registration or ticketing module needs auditing, (4) the user mentions: event registration, ticketed events, RSVP with deposit, no-show fee, no-show deposit, card hold, hold funds, authorize and capture later, capture_method manual, uncaptured payment, capture_before, SetupIntent, off_session, checkout.session.expired, "release the deposit on check-in", "charge no-shows", escrow for attendance, Stripe Checkout tickets, refund on cancel, cancel event and refund everyone. Carries a pure participant state machine, the hold timing that survives Visa's shorter merchant-initiated window, any ISO currency with USD by default, refunds of the amount frozen at registration, and 50 tests that hold each rule. Next.js App Router; Firestore or Postgres behind an EventStore seam. Not a marketplace payout skill, not subscriptions, not seat maps or resale.

This skill was written by the engineer who has shipped this module. The earlier implementation it was audited against was the events module of a multi-location venue-booking system. The templates hold the properties such a module has to hold: a paid event is never free in a currency it has no price in; a declined attempt inside open Checkout keeps the seat, and only session expiry releases it; money that arrives for a released seat is refunded; a refund is the amount frozen at registration; a no-show hold is placed inside the card network's window and captured only after settlement; a redelivered webhook or a repeated action changes nothing. The three suites, 50 tests, state each one, and [`references/provenance.md`](references/provenance.md) has the record.

What it builds:
- Checkout tickets
- A refundable card hold on free events released on check-in and captured on no-show
- Staff check-in
- Refunds
- Event cancellation
- One pure state machine with Stripe called only after commit

When to use:
- Capacity-limited events (free or paid) with online registration and payment.
- Free events that need a refundable deposit to stop no-shows.
- Staff check-in at the door that must drive money (release or capture).
- Auditing or upgrading an existing event-registration module: [provenance.md](references/provenance.md) has the audit ledger and the order of work.

When not to use:
- **Splitting payments between sellers, payouts, marketplace escrow, recurring memberships or subscriptions**: use the sibling `stripe-connect-subscriptions` skill.
- **Time-slot resource booking (lanes, courts, rooms by the hour)**: a booking engine's job; this skill only notes where events block such slots.
- **Walk-up touchscreen registration**: the sibling `booking-kiosk` skill owns the unattended-terminal flow; it can call this engine.
- **Seated venues with seat maps, or ticket resale**: a different domain.

Non-negotiables:
- Transaction first, Stripe second. No Stripe call inside a transaction, and no state written after a Stripe call without a durable intent first, because transaction callbacks re-run and a failed write after a capture loses the record of money moved. The engine is the only writer of `payment`, `deposit` and `attendance`, and the lifecycle suite drives every flow through it.
- Identity never comes from the request body. Customers come from the session, guests from an HMAC manage link, staff from a location-scoped guard on every staff and admin `[eventId]` route, because a body field lets anyone attach a registration to someone else's account and collect its refund. A lifecycle test holds that a manage token verifies only for its own participant.
- Never capture before settlement. A hold that would lapse is renewed or released, never captured early, because check-in must stay possible until the event is over. The lifecycle suite holds that a no-show is captured after the grace period and not before.
- Cancelling an event is an action, not a status. It closes registration, then refunds and releases every seat; a status flip would leave every participant charged. A lifecycle test cancels an event and checks every hold is released.
- A webhook error is a 500. The handler releases its event-id claim and answers 500 so Stripe retries, because a swallowed error is money nobody recorded. A redelivered webhook is a no-op, which a lifecycle test holds.

Not this:
- Marketplace splits, payouts, connected accounts, subscriptions: The sibling [`stripe-connect-subscriptions`](https://github.com/timerise-ai/stripe-connect-subscriptions) skill
- Walk-up touchscreen registration: The sibling [`booking-kiosk`](https://github.com/timerise-ai/booking-kiosk) skill, which can call this engine
- Hourly resource booking (lanes, courts, rooms): The host's booking engine; events only block its slots
- Seat maps, ticket resale: A ticketing platform

Booking features we build with it:
- Deposits and online payments: /features/deposits-and-payments
- Tickets and events with capacity: /features/tickets-and-events

What to tell your agent:
- Add events to this Next.js app on Postgres: limited capacity, paid tickets through Stripe Checkout, staff check-in at the door, and refunds when we cancel an event.
- Our free workshops have a no-show problem. Put a card hold on each registration that is released when the guest checks in and captured when they don't show.
- Audit our event registration module: we have seen double refunds and overbooked events.

Agent eval results (newest per agent and prompt):
- Gemini CLI (gemini-3.8-flash) on v0.1.3, 2026-09-28: pass (Postgres)
- Codex CLI (gpt-6-astra) on v0.1.3, 2026-09-28: pass (Postgres)
- Claude Code (claude-opus-5-5) on v0.1.3, 2026-09-28: pass (Postgres)

Recent releases:
- v0.1.3 (2026-09-28): Fix release, from scoring the prompt-1 agent eval runs against 0.1.2. Apps built from 0.1.2 should copy in the new webhook.ts; apps built from any earlier version, also engine.ts and the success URL in host.ts.
- v0.1.2 (2026-09-28): Fix release, from scoring the prompt-1 agent eval runs against 0.1.1. Apps built from 0.1.0 or 0.1.1 should copy in the new participant-machine.ts, engine.ts, webhook.ts, tick.ts and the check-in list route.
- v0.1.1 (2026-09-28): Documentation-only release: the templates and references are unchanged from 0.1.0. The repository now carries the agent eval prompts and workflow the skill standard requires, and the front door describes them.
