Membership Club — Training Manual

The complete manual in one document: every persona, then every workflow by area. Filtered to one area. Generated 2026-08-31T04:09:48Z · content revision 2026-08-30T21:54:00Z.

Back to training

Contents

Part 1 — By persona

Part 2 — By area

Appendices

Customer (Guest)

Anyone on the public site who has not logged in yet.

You are the public. You have no account, no groups and no session — and a surprising amount of the platform is deliberately open to you: the event catalogue, an event's tier and price breakdown, the resale exchange window, the host application form, a comp-invite claim link and a contract signing link.

That is a design decision, not an oversight. Every gate in this platform is enforced at the moment of the action, never by hiding the page. A customer must be able to see what is on sale, and a prospective promoter must be able to apply, before either of them has an account.

The moment you need to own something — a ticket, house credit, a listing — you become a Member. Registering is the boundary.

Core workflows (4)

Takes part in (2)

What this persona cannot do

Without a session you cannot:

  • hold a reservation, check out or pay — the cart and checkout pages require the member group;
  • see anyone's tickets, orders, credit balance or wallet passes;
  • open any /admin/* surface or the door scanner.

Anonymous requests that need a session get an HTML redirect to /login?next=…, or a 401 envelope for JSON.

Member

A registered customer: buys, holds tickets, house credit and resale listings.

You are the platform's centre of gravity. Registering grants the member group automatically, and with it every customer-owned surface: the cart and checkout, your orders and tickets, your wallet passes, your house credit balance and statement, and the resale exchange as both a seller and a buyer.

Your hub is /my. Everything you own hangs off it — /my/tickets, /my/orders, /my/credit, /account/passes. Note the shape of those URLs: the member portal lives under /my/* and /account/*, and every one of those routes scopes its query to your user id. A member cannot read another member's order even by guessing the id — the lookup 404s rather than 403s, so ids cannot be probed.

Core workflows (13)

Takes part in (18)

  • Browse events as a guest — owned by Customer (Guest), your step 9. Shows the same pages with a session, so the difference is visible rather than described.
  • Create your member account — owned by Customer (Guest), your steps 3–6. The account you just made — everything after the redirect is theirs, including the verification the first purchase waits on.
  • Create and launch an event — owned by Host / Promoter, your step 15. The reason the whole thing exists — they are who finally sees it on sale.
  • Buy a ticket — owned by Customer (Guest), your steps 6–13. Everything from the cart onwards needs a session — the buying half is theirs.
  • Understand your event's tiers, prices and releases — owned by Host / Promoter, your step 11. The buyer the whole release schedule is designed for.
  • Refund a ticket or an order — owned by Admin, your steps 1–8. It is their money and their ticket — and where it lands depends on the policy, not on what they ask for.
  • Read the scanner screen — owned by Door Staff, your steps 9–10. Presents the credential — and can show you what the rotating QR looks like from their side.
  • Diagnose a red scan — owned by Door Staff, your step 9. Is the guest at the window, and usually holds the fix on their own phone.
  • Use your membership card at the bar (VIP) — owned by VIP Member, your steps 1–2. Holds the same card without Zone B — the contrast is the lesson.
  • Open or close the resale exchange — owned by Admin, your step 5. They are the seller and the buyer: the bounds you set are the prices they are allowed to type.
  • Process a payout run — owned by Admin, your steps 1–3. It is their money leaving the platform: they request it and they are the one the identity gate applies to.
  • Post a house credit adjustment — owned by Admin, your step 5. It is their balance that moves, and the correction lands on their statement in full view.
  • Claim your comp invite and name your plus-ones — owned by Customer (Guest), your steps 5–7. The same invite addressed to an account is claimed from the member portal instead of a token link.
  • Build a trigger rule — owned by Venue Manager, your step 9. Is the person whose purchase crosses the threshold that fires the rule.
  • Build a role from capabilities, and tune a membership tier — owned by Admin, your steps 18–19. Is the person a tier is for — and the only one who can confirm the benefit actually landed.
  • Grant and revoke roles — owned by Admin, your step 4. It is their account: the grant is what changes what they can buy and where they can walk.
  • Deactivate a user — owned by Admin, your step 3. It is their account: they are the one who suddenly cannot log in, and they are told nothing.
  • Run the feedback backlog as a build list — owned by Venue Manager, your step 1. Supplies the backlog, and is the only person besides an admin who may edit their own words.

What this persona cannot do

You cannot:

  • reach any /admin/* page, the door scanner, or the debug console;
  • refund your own order at will — self-cancellation is only offered when the event's refund policy sets a cancellation deadline;
  • see or edit another member's data, in any module;
  • issue yourself a comp, a door override or a role.

VIP Member

A member with a house tab and Zone B access. Implies everything a member can do.

You are a member with two extra things: a VIP tab (a credit limit you can spend against and settle later) and a wider door credential — your membership card opens Zone B, not just the general zone.

The group logic matters here. vip_member implies member: the platform's group check expands vip_member to include member, so you are never granted both and never lose member surfaces. Everything on the Member page is yours too — this page only covers the difference.

The tab is deliberately not a wallet. It is an obligation recorded on the double-entry ledger: spending against it raises what you owe, and a scheduled monthly settlement sweeps your prepaid balance first and then charges your card for the remainder. Nothing about it is a discount.

Core workflows (2)

Takes part in (7)

What this persona cannot do

VIP status does not grant staff powers. You cannot:

  • set or raise your own tab limit — only an admin can, and revoking VIP freezes the tab while the balance stays owed;
  • skip the door — a VIP card is scanned like every other credential;
  • reach any admin, scanner or ledger surface.

Host / Promoter

An outside promoter running an event at the venue. Scoped to that one event.

You are an outside party who wants to put on a night at the venue. You are not staff, and the platform is emphatic about that: your grant is a scoped one. A host grant carries an event_id, and the permission check only passes for that event. Two promoters with host grants cannot see each other's events at all.

Your journey starts before you have an account: the application form is public. Approval is what creates your user and your scoped grant — in that order, and never by hand.

Inside your event you are effectively read-only. You can see your event and its sales, you sign the contract, you invite guests against the comp buckets allocated to you. You do not price it, transition it, refund it or touch its money. That separation is the whole point of the contract: it is the record of what the venue agreed to do on your behalf.

Core workflows (11)

Takes part in (11)

What this persona cannot do

Even on your own event you cannot:

  • create the event yourself — it is converted from your approved application by staff;
  • change tiers, prices, capacity or the event's status;
  • issue refunds, read the ledger, or take a payout outside the contracted royalty;
  • see any other promoter's event, or any venue-wide report.

Door Staff

Works the door: scans credentials, searches the guest list, checks people in.

You are the last mile. Your job is two screens — the scanner and the guest list — and a single question per person: green or red.

The design intent is that you never have to decide. The scan endpoint returns a verdict and a reason, and the screen is deliberately loud and colour-coded so it reads at arm's length in the dark. A red scan is not an accusation; it is a reason code (already used, wrong event, outside the door window, listed for resale, revoked) and each one has a different answer.

You are one of the three employee groups. That is a pseudo-group — a gate meaning "door staff or venue manager or admin" — and it is what opens the scanner. It is not a grantable group: nobody is ever given "employee", they are given door_staff, venue_manager or admin.

Core workflows (5)

Takes part in (14)

  • Find your way around as staff — owned by Venue Manager, your steps 8–9. Learns the two screens their role actually opens, and what a 403 there means.
  • Monitor live sales on the night — owned by Venue Manager, your step 9. Supplies the other half of the picture — how many of the sold tickets actually walked in.
  • Get through the door — owned by Member, your steps 5–6. Scans the credential and reads the verdict out loud.
  • Use your membership card at the bar (VIP) — owned by VIP Member, your step 5. Scans the card at the bar and reads the credit summary off the verdict.
  • Run the door offline — owned by Venue Manager, your steps 5–7. Actually works the queue in offline mode and presses Sync now when the network returns.
  • Resell a ticket you can't use — owned by Member, your step 6. Shows the consequence of listing: the seller's own credential now scans red.
  • Buy a ticket on the resale exchange — owned by Member, your step 9. Confirms the new pass works and the seller's old one does not.
  • Allocate comps to a promoter — owned by Venue Manager, your step 9. Turns the names in the bucket into people in the room, and lives with your plus-one decisions.
  • Claim your comp invite and name your plus-ones — owned by Customer (Guest), your steps 8–9. Finds the guest by name on the night and checks them in with their plus-ones.
  • Invite a guest and track plus-ones — owned by Host / Promoter, your steps 11–12. Finds the name on the night and counts the plus-ones in.
  • Revoke a comp — owned by Venue Manager, your step 6. Meets the consequence at the door if the revocation lands after the guest arrives.
  • Grant and revoke roles — owned by Admin, your step 8. A door_staff grant is the thing that opens the scanner at all.
  • Map groups to door zones — owned by Admin, your steps 4–7. The map decides which reader turns green for them and for the guests they scan.
  • Deactivate a user — owned by Admin, your step 4. The revoked credential fails at their reader and they must not mistake it for a broken scanner.

What this persona cannot do

Your access is narrow on purpose. You cannot:

  • open any admin dashboard, ledger, payout or tax surface;
  • download the offline sync bundle or the push feed from a browser — those carry every pass secret, so they need a venue manager session or a provisioned reader's device token;
  • create or revoke passes, refund anything, or change a role;
  • allocate comps — you check in the guests a manager or promoter listed.

Venue Manager

Runs the floor and the calendar: intake review, live sales, the door, marketing.

You run the venue day to day. venue_manager implies door_staff, so everything on the Door Staff page is yours as well — plus the surfaces a shift lead actually needs: the host application queue, the live sales dashboards, the guest list allocations, the door overrides, the access-control readers view and the marketing tools.

What you will notice is where the platform stops you, and it is worth understanding why rather than filing a ticket about it. You are trusted with operations; you are not trusted with money and identity. Payouts, refunds, the ledger, the tax office, role management and the universal data suite are all admin-only. That boundary means a manager can be hired, trained and given the floor on day one without ever being able to move a cent or grant themselves a group.

Core workflows (17)

Takes part in (24)

What this persona cannot do

You are a 403 on all of these, by design:

  • the ledger, trial balance and adjustments;
  • refunds and the payout queue;
  • the tax office;
  • role grants and revocations;
  • the universal admin data suite and its audit log;
  • transitioning an event's status and editing tiers or prices.

You can read most of the dashboards those things feed — the boundary is on writing, and on the surfaces where money is decided.

Admin

Owns money, identity and configuration. Implies every other group.

Admin is not "manager plus a bit". The permission check special-cases it: admin implies everything, including every event-scoped host grant. There is no surface in this platform you cannot open.

What is genuinely yours alone is the set of decisions that are hard to reverse: money out (refunds, payouts, ledger adjustments), identity (who is in which group), the contract counter-signature that binds the venue, the tax filings that go to an agency in the post, and the universal data suite that can edit any row in the database.

Two habits the design assumes of you. First, the ledger is append-only — enforced by database triggers, not convention — so you correct a mistake by posting a compensating entry, never by editing history. Second, everything you do through the data suite and the debug console is audited with a full before/after row snapshot. Both exist so that "the admin fixed it by hand" is always a readable event rather than a mystery.

Core workflows (24)

Takes part in (43)

What this persona cannot do

There is no group above you, so the limits are structural rather than permission-based:

  • you cannot UPDATE or DELETE an append-only table — the ledger, the audit logs and the sealed contract documents refuse the write at the database level, even from the data suite;
  • you cannot un-send a mailed tax filing or un-seal a signed contract;
  • you cannot grant the pseudo-group employee — it is a gate, not a grantable group.

Events & Ticketing

From a promoter's application to a customer holding a ticket.

The buying side of the platform: the event catalogue, tiered releases, reservation holds, checkout and refunds.

Create and launch an event

From a public application to a signed contract to a live, on-sale event.

Owned by Host / Promoter · 15 steps · about 25 minutes

Why this exists

This is the workflow the whole platform is shaped around, so it is worth understanding the design before the clicks.

An event is not created by staff. There is no "new event" form a manager fills in on a promoter's behalf. The event is converted from the promoter's own application, and the promoter's user account and event-scoped grant are created by that conversion. The reason is accountability: every event traces back to a specific application, submitted by a specific person, with the numbers they themselves committed to. Staff approve, price and publish — they do not invent.

The contract is the gate, not a formality. Approval drafts a contract automatically; locking it freezes a canonical rendering and takes its SHA-256 hash; the promoter signs a tokenised link; an admin counter-signs. Only the counter-signature flips the event from draft to announced. So an event cannot go public before both parties have signed the exact document that was hashed — which is why locking is irreversible-ish and why the seal stores the hash rather than a promise.

Built behaviour worth knowing: the counter-signature moves the event draft to announced. There is no contract_pending or approved event status in this platform — announced simply means publicly visible. Going on sale is a separate, deliberate admin action afterwards.

Before you start

  • Nothing at all to start: the application form is public and needs no login.
  • A venue manager or admin session to review and approve the application.
  • An admin session to lock and counter-sign the contract, and to put the event on sale.

Practise with

PersonaEmailPasswordNote
host_promoterhost@club.test host123the seeded promoter; already holds a scoped host grant on demo-event-0001
venue_managermanager@club.test manager123can review and approve applications, but cannot lock or counter-sign
adminadmin@club.test admin123the only persona that can counter-sign and transition the event

Steps 1–3 — Host / Promoter

their manual →
  1. 1
    Open the public host application form. No account, no login, nothing to set up first.
    /host/apply intake
    Expected result The form loads for anyone. If you happen to be logged in already, it prefills what it knows about you.
    Watch out for Returning hosts get suggested values highlighted in red — the platform will not accept them until you explicitly confirm each one, and that confirm gate is enforced on the server, not just in the browser.
  2. 2
    Submit the application: the date you want, your expected headcount range, your projected average ticket price, and the flags for live music, alcohol, a late finish and whether you want a ticket royalty.
    Expected result You get an application id back and the status is new_submission.
    Watch out for Those flags are not decoration. Each one automatically injects a rider into the contract later, so a wrong flag here becomes a wrong clause in a document you will be asked to sign.
  3. 3
    Land on the thank-you page and keep the link. It is your window into the application while staff review it.
    /apply/thanks/{app_id} intake
    Expected result You can see the status, and an edit link if staff ask you for more detail.

Steps 4–7 — Venue Manager

their manual →

Reviews the application, chases missing detail, and approves it.

  1. 4
    Open the intake queue. New applications sort to the top with their viability score.
    /admin/intake intake
    Expected result A list of applications with status and score.
    Watch out for The score is a scorecard, not a decision. It weights date, headcount and projected revenue — it does not know that the promoter's last night was a disaster.
  2. 5
    Open the application and read the whole scorecard: the proposed date against the calendar, the headcount against the room, the rider flags, and any attachments.
    /admin/intake/{app_id} intake
    Expected result The full application with its status history, notes and the rider summary the contract will be built from.
  3. 6
    Approve it.
    /api/intake/applications/{app_id}/approve POST intake
    Expected result Status moves to approved and — in the same transaction — a draft contract is created automatically, with the riders your flags implied and the headcount, ticket price and event date snapshotted into it.
    Watch out for Approval is the moment the contract exists. If the contract engine were to fail here, the approval itself rolls back: there is deliberately no state where an application is approved but has no paperwork.
  4. 7
    Convert the approved application into a draft event. This is also what creates the promoter's user account (if they did not have one) and grants them a host role scoped to this event id.
    /api/intake/applications/{app_id}/convert POST intake
    Expected result A draft event, and a promoter who can now log in and see exactly one event: theirs.
    Watch out for The grant carries the event id. The promoter is not 'a host' in general — they are a host of this event only.

Steps 8–9 — Admin

their manual →

Locks and counter-signs the contract, then puts the event on sale.

  1. 8
    Open the drafted contract. Check the auto-injected riders and fill in any variable still blank — headcount, ticket price and event date are rendered inline in blue.
    /admin/contracts/{contract_id} contracts
    Expected result A section-by-section editable document; every edit you make is recorded as a redline.
    Watch out for A missing variable blocks the lock. That is the intended failure: an unpriced contract must never reach a signature.
  2. 9
    Lock the contract.
    /contracts/{contract_id}/lock POST contracts
    Expected result The document is rendered canonically, hashed with SHA-256, and a tokenised signing link is emailed to the promoter.
    Watch out for Locking is the point of no return for the wording. Everything after this signs the hashed document — if the wording is wrong, unlock and re-lock rather than editing around it.

Steps 10–11 — Host / Promoter

their manual →
  1. 10
    Open the signing link from your email. It needs no login — the token is the credential.
    /sign/{token} contracts
    Expected result The full agreement, exactly as locked, with your rider clauses and your numbers.
    Watch out for The link expires. If it has, ask for it to be resent rather than hunting for another way in.
  2. 11
    Sign it.
    /sign/{token} POST contracts
    Expected result Your signature is recorded against the locked hash and the contract moves to awaiting counter-signature.
    Watch out for You are signing the hash. If the document you are reading were altered afterwards, the seal would not verify — which is exactly the guarantee the hash exists to give you.

Steps 12–14 — Admin

their manual →

Locks and counter-signs the contract, then puts the event on sale.

  1. 12
    Counter-sign on behalf of the venue.
    /contracts/{contract_id}/countersign POST contracts
    Expected result The contract seals into an immutable hash-verified document, and the linked event flips from draft to announced in the same transaction.
    Watch out for Announced means publicly visible, not on sale. Nobody can buy yet — that is the next step, and it is separate on purpose.
  2. 13
    Open the event dashboard and set up what the customer will actually see: tiers, prices, capacity and the release schedule.
    /admin/events/{event_id} events
    Expected result The event with its tiers, zone mapping and availability.
  3. 14
    Transition the event to on_sale.
    /api/events/{event_id}/transition POST events
    Expected result The event is live; the storefront shows it as buyable and the marketing rules watching for it can fire.
    Watch out for This endpoint is admin-only. A venue manager gets a 403 here even though they can see the dashboard — putting inventory on sale is a money decision.

Steps 15 — Member

their manual →

The reason the whole thing exists — they are who finally sees it on sale.

  1. 15
    Look at the event as a customer does, and buy one. Nothing in the manual is finished until you have seen the thing you built from the outside.
    /events/{event_id} events
    Expected result The public event page with live tiers and a working buy button.

Buy a ticket

Browse as a stranger, register, hold a reservation, pay, and hold a ticket.

Owned by Customer (Guest) · 14 steps · about 15 minutes

Why this exists

This workflow belongs to the customer, which is why it is owned by the guest persona rather than by a member: it starts with a stranger who has no account, and the account is something that happens during the purchase, not before it.

The design idea that everything else hangs off is the hold. Adding tickets to your cart reserves nothing; the inventory is only taken when a reservation is created, and that reservation has a short TTL. Between those two moments the tier's reserved count goes up, the available count goes down, and if you wander off, a sweeper returns the inventory. Overselling is therefore impossible by construction rather than by luck: the check and the decrement happen in one transaction, and whoever wins the write lock wins the ticket.

Payment is deliberately the last and smallest step. By the time you pay, the price, the tax and the inventory are already pinned down and snapshotted on the order. Paying converts a hold into sold inventory and issues the ticket rows; it does not decide anything.

Before you start

  • An event that is on_sale with at least one tier that has availability.
  • Nothing else — the browsing half of this workflow needs no account.

Practise with

PersonaEmailPasswordNote
membermember@club.test member123an ordinary member; use this once you reach the checkout half
vip_membervip@club.test vip123has a VIP tab, so the house-credit payment option is available
adminadmin@club.test admin123to see the same order from the staff side

Steps 1–5 — Customer (Guest)

their manual →
  1. 1
    Start where a customer starts: the home page. Do this logged out.
    / frontend
    Expected result Upcoming events, plus any site announcement targeted at the public.
  2. 2
    Open the catalogue and find an event that is on sale.
    /events events
    Expected result Only events that are announced or on sale appear. Drafts are invisible to the public.
  3. 3
    Open the event and read the tier breakdown: what each tier costs, what is left, and which zones it opens.
    /events/{event_id} events
    Expected result Live availability per tier.
    Watch out for A tier can be visible but locked — tiers unlock on a schedule or when the previous tier sells out. Sold out is not the same as closed.
  4. 4
    Register. This is the boundary between browsing and buying.
    /register rbac
    Expected result The registration form.
  5. 5
    Submit the registration.
    /auth/register POST rbac
    Expected result You get an account, a session, and the member group automatically. You are a Member from this point on.
    Watch out for The member group is granted at registration — nobody has to approve you. Every other group in the platform is granted by an admin or by a system trigger.

Steps 6–10 — Member

their manual →

Everything from the cart onwards needs a session — the buying half is theirs.

  1. 6
    Open the event's checkout page and choose your tier and quantity.
    /events/{event_id}/checkout payments
    Expected result The tiers with live availability, your cart for this event, and the refund policy that will apply.
    Watch out for There is a per-tier maximum per order. It is a platform setting, not a suggestion.
  2. 7
    Add the tickets to your cart.
    /api/cart/items POST payments
    Expected result A cart line with the price snapshotted at the moment you added it.
    Watch out for A cart reserves NOTHING. Someone else can still buy the last ticket while it sits there. The price snapshot exists so that a price change between adding and checking out is caught and shown to you rather than silently applied.
  3. 8
    Check out. This is the moment that matters: it creates the reservation that actually holds the inventory, and an order to pay for it.
    /api/checkout POST payments
    Expected result An order awaiting payment, with a countdown.
    Watch out for This is where you find out you were too slow. If the tier sold out between your cart and here, checkout fails with an inventory error and nothing is charged — that is the oversell guard doing its job.
  4. 9
    Land on the payment page and watch the hold countdown.
    /checkout/{order_id} payments
    Expected result The order total with tax broken out, your available house credit, and the payment methods.
    Watch out for This page also accepts a reservation id: the events buy button sends you here with the reservation, and the page adopts it into an order for you. Both URLs are legitimate.
  5. 10
    Pay with a card.
    /api/orders/{order_id}/pay POST payments
    Expected result The hold is committed into sold inventory, the payment is recorded, a balanced ledger transaction is posted, and your ticket rows are issued.
    Watch out for If the hold expired while you were finding your wallet, this fails with a conflict and the inventory has already gone back on sale. Start again — nothing was charged.

Steps 11–12 — VIP Member

their manual →

Can settle the same order from house credit or the VIP tab instead of a card.

  1. 11
    As a VIP, check your purchasing power before paying: prepaid credit, earned resale balance, and your remaining tab headroom.
    /my/credit ledger
    Expected result The three balances and the tab limit.
    Watch out for Purchasing power is not one pot. Prepaid is money you put in, earned is money you made reselling, and the tab is money you have not paid yet — the platform spends them in that order.
  2. 12
    Pay the same order with house_credit instead of a card.
    /api/orders/{order_id}/pay POST payments
    Expected result The order settles from promo credit first, then prepaid, then earned, then against your tab headroom — as one balanced ledger transaction.
    Watch out for If the total exceeds what all four can cover, the payment is refused outright rather than partially settled. There is no half-paid order.

Steps 13 — Member

their manual →

Everything from the cart onwards needs a session — the buying half is theirs.

  1. 13
    Look at what you now own.
    /my/tickets payments
    Expected result Your ticket, ready to be turned into a wallet pass.
    Watch out for A ticket and a pass are different things. The ticket is the entitlement; the pass is the door credential minted from it. Getting through the door is its own workflow.

Steps 14 — Admin

their manual →

Sees the finished order from the staff side, and is the only one who can refund it.

  1. 14
    Open the same order from the staff side to see what the customer's purchase looks like to you: line items, tax, the payment, and the refund controls.
    /admin/orders/{order_id} payments
    Expected result The order with its full event history.
    Watch out for Refunding is admin-only, and it is the one action here that moves money outward. A venue manager can read this page but not refund from it.

Hold a checkout reservation (and the timer that kills it)

Understand the hold: what reserves inventory, how long you get, and what happens when it lapses.

Owned by Member · 13 steps · about 15 minutes

Why this exists

This is the mechanism the whole ticketing side is built on, and it is worth ten minutes of anyone's time because almost every confusing checkout error traces back to it.

A cart reserves nothing. Adding an item writes a cart line with the price snapshotted at that moment, and takes zero inventory. Someone else can buy the last ticket while your cart sits open, and they should be able to — inventory belongs to whoever is actually trying to complete a purchase, not to whoever browsed first.

Checkout creates the hold. The reservation is where the tier's reserved count goes up and its available count goes down, all inside one write transaction with a database CHECK that makes overselling impossible even under contention: the check and the decrement cannot be separated, and whoever wins the write lock wins the ticket. The hold carries a TTL from the event (between 5 and 10 minutes; 10 by default) and the order shows it as a countdown.

An expired hold returns the inventory, and the return is committed before you are told. That ordering is deliberate: by the moment you see "hold expired", the seats are genuinely back on sale for everyone else rather than sitting in limbo while your browser catches up. You were not charged, because payment is the last step and it re-checks the hold before touching a card.

Finally, holds are rationed. Each user may keep only a small number of active holds per event (two by default), so a script cannot quietly sit on the room.

Before you start

  • A member session.
  • An event that is on sale with availability in at least one tier.
  • An admin session for the last three steps only.

Practise with

PersonaEmailPasswordNote
membermember@club.test member123the buyer
adminadmin@club.test admin123can see live holds and force the sweeper from the debug console

Steps 1–10 — Member

their manual →
  1. 1
    Open the event's Get tickets page and pick a tier and quantity.
    /events/{event_id}/checkout payments
    Expected result Live availability per tier, your cart for this event, and the refund policy that will apply to what you buy.
    Watch out for The quantity ceiling is the smaller of the tier's own per-order maximum and the platform-wide setting. The dropdown will not offer you more than is left.
  2. 2
    Add the tickets. Then wait a moment and reload the page.
    /api/cart/items POST payments
    Expected result A cart line at the current price. The tier's available count is unchanged.
    Watch out for Nothing is held. The price on the line is a snapshot, and it exists so that a price change between adding and checking out is caught and shown to you rather than silently charged.
  3. 3
    Open the cart page to see every event you have lines for, not just this one.
    /cart payments
    Expected result All open cart lines with their events.
  4. 4
    Check out. This is the moment inventory is actually taken.
    /api/checkout POST payments
    Expected result One reservation per event in the cart, an order awaiting payment, and a hold expiry stamped on it.
    Watch out for Three specific refusals live here and none of them charge anything: an empty cart; a price that changed since you added it (the error lists exactly which lines); and insufficient inventory (the error carries the real availability per tier). All three mean 'start again', not 'retry'.
  5. 5
    Learn the other route to the same place: the Reserve button on the public event page creates a hold directly and skips the cart entirely.
    /api/events/{event_id}/reservations POST events
    Expected result A reservation with the seconds remaining on it, then a redirect to the payment page carrying the reservation id.
    Watch out for A 429 here means you already hold the maximum number of active reservations for this event. Release one or let it lapse — a second browser tab does not get you a second allocation.
  6. 6
    Land on the payment page and watch the countdown.
    /checkout/{order_id} payments
    Expected result The order total with tax broken out, your available house credit, the payment methods, and the hold countdown.
    Watch out for This page accepts an order id OR a reservation id. Handed a reservation, it creates the order for you and redirects. Both URLs are legitimate — you are not lost.
  7. 7
    Read the hold itself while it is alive.
    /api/reservations/{reservation_id} events
    Expected result Status active, the per-tier lines with their snapshotted prices, and the seconds remaining.
  8. 8
    Change your mind properly: release the hold instead of walking away.
    /api/reservations/{reservation_id} DELETE events
    Expected result The inventory goes back immediately and the tier can leave sold-out state.
    Watch out for Releasing an already-committed or already-expired hold is a 409. That is not an error you need to fix — it means somebody or something got there first.
  9. 9
    Cancel the unpaid order (send no ticket ids). This is the order-level equivalent of releasing the hold.
    /api/orders/{order_id}/cancel POST payments
    Expected result The order is cancelled and its reservations are released.
    Watch out for If a payment attempt is in flight the cancel is refused with a conflict rather than racing it. The platform will not let you cancel an order that is mid-charge, because the money would be stranded.
  10. 10
    Now let a hold lapse on purpose and try to pay it.
    /api/orders/{order_id}/pay POST payments
    Expected result A 410 hold expired. Nothing was charged and the inventory is already back on sale.
    Watch out for This is the single most common support question on the platform. The answer is always the same: start again, you were not charged, and the seats you wanted may now be someone else's.

Steps 11–13 — Admin

their manual →

Watches the same hold from the inventory side and can force it to expire on demand.

  1. 11
    As an admin, look at what the sweeper would expire right now.
    Expected result The holds that are past their TTL and the inventory that would return.
    Watch out for Debug routes are admin-only and vanish entirely when debug endpoints are switched off. Do not build any habit that depends on them.
  2. 12
    Force the expiry so you can demonstrate the timer without waiting ten minutes.
    Expected result A list of cancelled order ids, and the tiers back on sale.
    Watch out for The sweeper and a live payment can race. That race is settled by the write lock rather than by luck: one of them wins, and there is no state where a hold is both committed and expired.
  3. 13
    Close the loop from the inventory side: list the active holds on the event with the members who own them.
    /api/admin/events/{event_id}/reservations events
    Expected result Live holds with expiry times and buyer emails.
    Watch out for Reserved and sold are different counters. A tier can read sold out purely because of holds and un-sell itself minutes later — that is normal, and it is why 'sold out' on this platform is a state, not a fact.

Pay for an order with house credit

Settle a checkout from your balance instead of a card, and see which pot it comes from.

Owned by Member · 8 steps · about 12 minutes

Why this exists

House credit is a real liability of the venue to you, held on a double-entry ledger, not a loyalty-points balance. That is why paying with it looks nothing like a discount code: it posts a balanced transaction that moves the amount out of the venue's obligation to you and into revenue and tax liability, in one step, with no card involved.

Your purchasing power is deliberately three separate pots spent in a fixed order: prepaid credit you paid in, then earned credit from resale, then — for VIPs only — headroom on the tab, which is money you have not paid at all. The order is not configurable. It exists so the tab is always the last resort and never quietly used while you have real money sitting there.

The rule that catches everyone is that there is no partial settlement. If the total is one cent more than everything you can bring, the payment is refused outright rather than half-paying and asking for a card for the rest. A half-paid order is a support ticket forever; a refused one is a top-up and a retry.

One consequence worth knowing before you buy: refunding an order that was paid with credit returns the money to credit. For a credit-paid order, "refund to the original method" IS house credit — there is no path back to a card that never got charged.

Before you start

  • A member session with some house credit (top it up first if you have none).
  • An unpaid order sitting on its hold.

Practise with

PersonaEmailPasswordNote
memberalice.credit@demo.club alice-pass-123seeded with $150 of prepaid credit and nothing else — the cleanest demo
vip_membervip@club.test vip123$250 tab limit with $80 already used, so the tab path is visible
adminadmin@club.test admin123reads the resulting ledger transaction

Steps 1–4 — Member

their manual →
  1. 1
    Check what you can actually spend before you start.
    /my/credit ledger
    Expected result Prepaid, earned and (if you are VIP) the tab, plus a single purchasing-power figure.
    Watch out for Purchasing power is not money you have. For a VIP it includes tab headroom, which is money you will owe.
  2. 2
    On the payment page, choose the House credit option instead of Card.
    /checkout/{order_id} payments
    Expected result The card fields disappear and the available balance is shown next to the option.
    Watch out for House credit needs no card token. If the form still asks you for one, you have not actually changed the selected method.
  3. 3
    Pay. Send the method as house credit and no token.
    /api/orders/{order_id}/pay POST payments
    Expected result One balanced ledger transaction, the hold committed into sold inventory, and your tickets issued.
    Watch out for A 402 insufficient credit means nothing was posted and nothing was spent — the order is untouched and still on its hold, so top up quickly and retry before the timer runs out.
  4. 4
    Read the movement on your statement and identify which pot it came out of.
    Expected result A debit against promo first if you hold any, then prepaid; earned only once both have run out.
    Watch out for You cannot choose the pot, and the order is promo → prepaid → earned → tab. If you were saving earned credit for a cash-out, spending it here is exactly what the platform will do once promo and prepaid are empty.

Steps 5–6 — VIP Member

their manual →

The only persona whose tab headroom can absorb what prepaid and earned cannot.

  1. 5
    As a VIP, read the balance payload and find the tab block: limit, used, headroom and status.
    Expected result Three pots plus the tab, and a purchasing power that includes the headroom.
    Watch out for A frozen tab contributes zero headroom no matter what the limit says. Freezing is a credit decision — the balance you already owe does not go away.
  2. 6
    Pay an order bigger than your prepaid and earned balances combined.
    /api/orders/{order_id}/pay POST payments
    Expected result Prepaid is consumed, then earned, and only the remainder lands on the tab.
    Watch out for This is a VIP-only path. A plain member with no tab simply gets the 402 at the point prepaid and earned run out — there is no borrowing without a tab, and VIP status alone does not create one. An admin has to set the limit.

Steps 7–8 — Admin

their manual →

Confirms the posting balances and can see the member's balances from the staff side.

  1. 7
    As an admin, open that member's credit page and read the same balances from the staff side.
    /admin/credit/users/{user_id} ledger
    Expected result Their pots, their tab and an adjustment composer.
    Watch out for This page is admin-only. A venue manager gets a 403 on every ledger surface, deliberately — running the calendar and moving money are different jobs.
  2. 8
    Find the transaction the payment posted and check the two sides.
    Expected result Debits equal credits, with the member's house-credit liability on one side and revenue plus tax on the other.
    Watch out for Try to edit it. The database refuses — the append-only triggers fire even from the admin data suite. Corrections are new balanced entries, never edits.

Get into a member-only drop

How tier releases actually work here, what 'member-only' really means, and what VIP does and does not buy you.

Owned by Member · 11 steps · about 15 minutes

Why this exists

Read this one before you promise anything to a customer, because the built platform and the phrase "member-only drop" do not line up the way most people assume.

What a drop is here. A drop is a tier release. A tier either opens on a schedule (it sits in a countdown until its sale start) or it cascades (it stays locked until the tier before it has sold a configured percentage), and the release fires from the scheduler tick, which also runs lazily whenever anyone loads the catalogue or reads availability. So a drop happens on time without a cron job, and the first person to look is the person who triggers the check.

What "member-only" actually is. There is no group restriction on a tier. Tiers have no audience field, so nobody is ever refused a tier for the groups they hold — and "becoming a member" is a 30-second registration rather than an approval. If you need a genuinely restricted allocation — a real invite-only release — the built tool for that is a guest-list comp bucket, not a tier flag.

But the purchase path does read your groups, for one thing: when. A group can carry an early-access head start, and the gate that applies it sits inside the reservation itself, not in the page. early_access_minutes on the membership-benefits table is joined to your groups; if it is greater than zero, a scheduled tier becomes buyable for you that many minutes before its sale start, and for nobody else. The tier's status never flips — a flip would be global, which is the exact opposite of a benefit — so the same tier is buyable for you and refused for the next person, in the same second. Everyone else waits for the scheduler to open it at sale start. Early access widens exactly one gate: locked stays locked and sold out stays sold out.

What VIP changes. The sale window, if the venue has configured it — see above; that is the one place a group changes what you can buy and when. It is a setting on the benefits table, not a property of being VIP, so check the row rather than assuming: a build with every early-access value at zero behaves exactly as though the feature were absent — and that is what ships. Measured 2026-08-27: member, lama_family and vip_member all carry early_access_minutes = 0, so on a stock install nobody has a head start and every buyer meets the same sale start. Somebody has to set the number before any of this is visible. Beyond that, VIP does not buy a lower price on a ticket. CORRECTED 2026-08-30 — it does buy a reduced FEE. VIP carries a 50% discount on the resale venue fee, and house and vendor discounts of 20% and 10%; Lama Family carries a house discount of its own. Those are rows in membership_tier_benefits, read by live consumers, and shown on the tier card. What VIP does not buy is a lower ticket price or a head start. Beyond the fees it buys Zone B on the door credential, a tab that can fund an instant purchase when a drop is a race, and the venue's own comp and announcement targeting. A tier named "VIP Lounge" is an ordinary tier whose zone mapping happens to include Zone B — the name is marketing, the zone is the mechanism.

The one genuinely audience-aware feature is site announcements, which are targeted at an audience: public, member, VIP, host or staff. That is how a drop is announced to members only, even though the sale itself is open to anyone with an account.

Before you start

  • A member session for the buying half.
  • An admin session for the setup half.
  • An event with a tier that is scheduled or cascade-locked (the seeded demo event has both).

Practise with

PersonaEmailPasswordNote
membermember@club.test member123the buyer waiting on the drop
vip_membervip@club.test vip123for the side-by-side: same sale window, different zones and a tab
adminadmin@club.test admin123schedules the tier and targets the announcement

Steps 1–4 — Admin

their manual →

Creates the tier release and is the only persona who can target an announcement at members.

  1. 1
    As an admin, open the event and read the tier ladder in sort order. The order is the cascade order.
    /admin/events/{event_id} events
    Expected result Each tier with its unlock mode, sale window, cap and counts.
    Watch out for The first tier can never be a cascade tier — it has nothing to cascade from, and the API refuses it.
  2. 2
    Create the drop: either a scheduled tier with a sale start at the drop time, or a cascade tier that unlocks when the previous one is a given percentage sold.
    /api/events/{event_id}/tiers POST events
    Expected result A tier in scheduled or locked state, visible to the public but not buyable.
    Watch out for A scheduled tier needs a sale start; a cascade tier needs a predecessor. Both are 422s with named reasons rather than silent defaults.
  3. 3
    Open the announcements editor — this is the only place the platform is audience-aware.
    Expected result The list of announcements with their level, audience and window.
  4. 4
    Publish the drop notice targeted at members (or VIPs) rather than the public.
    /admin/announcements POST frontend
    Expected result The announcement renders for that audience and is simply absent for everyone else.
    Watch out for This targets who sees the message, not who may buy. Anyone with an account can still purchase the tier when it opens — do not write copy that promises otherwise.

Steps 5–8 — Member

their manual →
  1. 5
    As a member, open the hub and read the announcement aimed at you.
    /my frontend
    Expected result The member-targeted notice with the drop time.
    Watch out for If you cannot see a notice a colleague can, compare groups before you suspect a bug. Audience targeting is doing its job.
  2. 6
    Open the event and watch the tier you are waiting for.
    /events/{event_id} events
    Expected result An amber countdown for a scheduled tier, or Locked with the name of the tier it is waiting on for a cascade tier.
    Watch out for A locked tier deliberately publishes no availability number. You cannot tell how many are behind it, and neither can a bot.
  3. 7
    Poll availability as the drop time approaches, and notice what opens the tier.
    /api/events/{event_id}/availability events
    Expected result Availability null while locked, then a real number the moment it opens.
    Watch out for Reading this runs the scheduler check. The tier opens because someone looked — there is no background thread, and a drop nobody is watching opens on the first visit after its time.
  4. 8
    At the drop, go straight to checkout, add your quantity and check out immediately.
    /events/{event_id}/checkout payments
    Expected result A hold, and an order with a countdown.
    Watch out for In a real drop the cart is your enemy: a cart holds nothing. The seconds that matter are between checkout and payment, not between browsing and adding.

Steps 9–10 — VIP Member

their manual →

Shows precisely which VIP differences are real and which are folklore.

  1. 9
    Log in as the VIP and open the same page at the same moment.
    /events/{event_id} events
    Expected result Exactly the same tiers, the same countdown and the same prices.
    Watch out for This is the point of the exercise. There is no VIP presale window in this build. If the venue wants one, the honest implementation today is a separate event or a comp bucket, not a promise about VIP.
  2. 10
    Pay from the tab to see the one advantage that is real in a race: instant settlement without reaching for a card.
    /checkout/{order_id} payments
    Expected result The order settles from promo, then prepaid, then earned, then tab headroom.
    Watch out for The tab is a limit on what you may owe and it is settled monthly. Winning a drop on the tab still means paying for it.

Steps 11 — Admin

their manual →

Creates the tier release and is the only persona who can target an announcement at members.

  1. 11
    Rehearse the drop before the night: force the cascade and watch the next tier open.
    /debug/events/tiers/{tier_id}/force-cascade POST events
    Expected result The locked tier opens and the release event is emitted, flagged as forced.
    Watch out for Marketing rules listen for that release event, so a rehearsal on a live database can genuinely publish a social post. Rehearse on a scratch database, not on production.

Understand your event's tiers, prices and releases

Read the tier ladder on your own event, and learn who can change what — and why it is not you.

Owned by Host / Promoter · 12 steps · about 20 minutes

Why this exists

Your application's tier estimates became real tiers when the event was converted, and from that point the ladder belongs to the venue. This workflow teaches you to read it fluently, because you will be asked about it all night, and it teaches you who to ask when it needs to change.

Why a host cannot price their own event. Tiers are inventory, and inventory is money: a price change moves revenue, a cap change can oversell the room, and a status change can put unsold stock on the market. Those are venue decisions, so every write here is admin-only and the whole dashboard is read-only for you. What was agreed is written down in the contract; if the ladder needs to change, that conversation goes through the venue, not through the UI.

How a tier ladder actually works. Each tier has a cap, a sold count and a reserved count; available is never stored, it is cap minus sold minus reserved, computed every time. A tier unlocks in one of three ways: on a schedule (it opens at its sale start time), by cascade (it sits locked until the tier before it has sold a set percentage, or is closed, or passes its sale end), or manually. Cascade thresholds count tickets actually sold — holds do not count toward a cascade. The first tier in the ladder can never be a cascade tier, because there is nothing for it to cascade from.

Sold out is automatic and two-sided: the moment sold plus reserved equals the cap the tier flips to sold_out — holds do count here — and it flips back automatically when those holds expire or inventory is returned, unless the sale window has closed. So "sold out" on this platform can un-sell-out, and that is correct behaviour, not a glitch.

Before you start

  • A host grant on the event whose dashboard you are reading.
  • An admin session for any of the configuration steps.

Practise with

PersonaEmailPasswordNote
host_promoterhost@club.test host123scoped host on demo-event-0001, which ships a fired cascade to look at
adminadmin@club.test admin123the only persona that can create, price or open a tier
venue_managermanager@club.test manager123can see every event including drafts, but cannot price or transition them
membermember@club.test member123experiences the release schedule from the buyer's side

Steps 1–3 — Host / Promoter

their manual →
  1. 1
    Open your event and read the Tiers table properly: number, name, price, status, Sold / Rsv / Cap, the sale window, and the zones each tier opens.
    /admin/events/{event_id} events
    Expected result One row per tier in cascade order, with your application's tier names on it.
    Watch out for There are no action buttons on those rows for you. That is the read-only view, not a rendering bug.
  2. 2
    Pull the same picture as the buyer's page computes it, so you can see the fields underneath the table.
    /api/events/{event_id}/availability events
    Expected result Per-tier availability and the server time, with the scheduler run lazily first so the numbers are fresh without a cron job.
    Watch out for A locked tier reports its availability as null rather than a number — the platform will not leak how much of an unreleased tier exists.
  3. 3
    Learn the six tier statuses so you can answer questions on the night: locked (not released yet), scheduled (opens at a set time), on_sale, paused (deliberately stopped by staff), sold_out, closed (the window has ended).
    Expected result Every tier is in exactly one of those states, and the dashboard shows it.
    Watch out for Sold out and closed are not the same thing, and the difference matters to a customer: sold out can reopen when holds expire, closed cannot.

Steps 4–8 — Admin

their manual →

Owns every write here: creates tiers, sets prices and caps, maps zones and puts the event on sale.

  1. 4
    Add a tier: name, price, cap, sort order, unlock mode, and either a sale start (scheduled) or a cascade threshold percentage.
    /api/events/{event_id}/tiers POST events
    Expected result 201 with the new tier, slotted into the ladder at its sort order.
    Watch out for 422 cascade_needs_predecessor if you try to make the first tier a cascade tier, 422 sale_start_required for a scheduled tier without a start, and 409 sort_order_taken because sort order is unique per event and it is the cascade order.
  2. 5
    Adjust a tier: price, cap, max per order, window, or status (open now, pause, resume, close).
    /api/tiers/{tier_id} PATCH events
    Expected result The updated tier, with the change reflected immediately in availability.
    Watch out for 422 cap_below_committed — you can never shrink a cap below what is already sold. 409 invalid_tier_transition for an illegal status move. Deleting a tier that has sales is refused outright with 409 tier_has_sales.
  3. 6
    Map the tier to the door zones its ticket should open. This is a full replace, not an add.
    /api/tiers/{tier_id}/zones PUT events
    Expected result The tier's zone set, by code or by id.
    Watch out for This is what a scanner reads at the door: the zone codes are embedded in the pass when the ticket is issued. Change zones after tickets are issued and the already-issued passes keep the zones they were minted with.
  4. 7
    Put the event on sale once the ladder is right.
    /api/events/{event_id}/transition POST events
    Expected result The event moves to on_sale, the storefront shows it as buyable, and the outbox emits the event so marketing rules can fire.
    Watch out for Venue manager and admin (capability events.publish). CANCELLING is the exception and still needs admin: it releases every live hold and fans out to refunds, so the route asks for events.cancel separately and answers 403 to a venue manager. Cancelling also needs a reason (422 reason_required).
  5. 8
    In a training or demo environment, force the next locked tier open so a room can watch a cascade happen without selling out a real tier.
    /debug/events/tiers/{tier_id}/force-cascade POST events
    Expected result The next locked tier unlocks and an outbox row is emitted flagged as forced.
    Watch out for Admin-only debug, and the whole /debug tree 404s when debug endpoints are disabled. In production the scheduler tick and the lazy evaluation on reads do this by themselves.

Steps 9–10 — Venue Manager

their manual →

Sees the same dashboard including drafts, but cannot price or transition anything.

  1. 9
    For an event the club already voted in, skip the host application entirely: fill the backload form once — name, room, times, tiers — pick the status to land at, and submit. Paste a whole season into the box below it if you have one.
    Expected result A real event at the status you asked for. Pick on_sale and it is selling when the page reloads; pick completed for a night that already happened.
    Watch out for It skips the intake workflow, not the rules: an event with no tier cannot go on sale, and a tier ladder over the room's capacity is refused the same way it would be from the manual path. The paste box reports bad lines one by one instead of dropping the batch — preview first. Every backloaded event carries 'backload' as the reason on its status log, which is how you find them later.
  2. 10
    As the venue manager, use the events list to see every event including drafts and their sold-versus-cap totals.
    /admin/events events
    Expected result All events, with tier counts and totals.
    Watch out for Since plan 41 this is a working surface for a venue manager, not a window: create, price, zone and publish are all yours (capabilities events.create / update / publish / tiers.manage). Deleting an event and cancelling one are not — those two are admin, because they destroy a record and trigger mass refunds respectively.

Steps 11 — Member

their manual →

The buyer the whole release schedule is designed for.

  1. 11
    As a buyer, open the public event page and watch the ladder from the outside.
    /events/{event_id} events
    Expected result Live availability per tier, countdowns on scheduled tiers, and a headline when a cascade fires along the lines of 'Early Bird Sold Out — General Admission Now Live!'.
    Watch out for A visible tier can still be locked. Locked, sold out and closed all look like 'you cannot buy this' to a customer and mean three different things to you.

Steps 12 — Host / Promoter

their manual →
  1. 12
    Come back to your dashboard and read the status log and recent outbox rows to see the release history of your own event.
    /admin/events/{event_id} events
    Expected result An append-per-transition log, plus the tier unlock, milestone and sold-out events the platform emitted.
    Watch out for Those outbox rows are what the marketing automation consumes. If a post went out about your event, this is where the trigger came from.

Draw a venue map

Upload a floor plan or start on a blank grid, draw each zone, and give every map in the app its labels.

Owned by Venue Manager · 7 steps · about 15 minutes

Why this exists

The map is an illustration of the zone roster, never its replacement — nothing the door reads comes from the drawing. Zones are drawn as polygons over a floor-plan image or a blank grid; points are stored normalised (0–1), so the same shape survives any plan image size.

Two rules the editor lives by since plan 72: everything it draws is visible while you draw it, and every failure says so — a page that silently ignores a refused save is indistinguishable from a broken one.

Before you start

  • A venue with zones defined
  • venue_manager (or admin) access

Steps 1–7 — Venue Manager

their manual →
  1. 1
    Open the layout editor for a venue with no plan yet.
    /admin/venues/{venue_id}/layout venues
    Expected result The upload form and a “Start on a blank grid” button. No canvas yet.
    Watch out for Nothing to draw on until a layout exists — that is a state, not a failure.
  2. 2
    Start on a blank grid.
    /api/venues/{venue_id}/layout POST venues
    Expected result A visible 50px grid appears.
    Watch out for Before plan 72 the grid painted with an unresolvable stroke and the box looked empty — var() works in CSS declarations, never in SVG presentation attributes.
  3. 3
    Pick a zone and click four corners.
    /admin/venues/{venue_id}/layout venues
    Expected result A dashed outline follows each click, with a corner count under the canvas.
    Watch out for That feedback is the whole difference between drawing and guessing.
  4. 4
    Double-click to close the shape.
    /api/venues/{venue_id}/shapes POST venues
    Expected result The shape saves and reloads as a filled polygon with its zone name.
    Watch out for The double-click only closes — it no longer leaves a stray corner at the closing position.
  5. 5
    Draw a second zone, then delete the first from the list.
    /admin/venues/{venue_id}/layout venues
    Expected result The list and the map agree.
    Watch out for Saving the same zone again replaces its shape — one shape per zone per layout.
  6. 6
    View the public map.
    /venues/{venue_id}/map venues
    Expected result Both zones, labelled.
    Watch out for The label fix lands here too — zone names were invisible on every map surface, and nobody had reported it.
  7. 7
    Try to upload a file that is too large or the wrong type.
    /api/venues/{venue_id}/layout POST venues
    Expected result A clear error beside the form, and nothing changes.
    Watch out for Silent failure was half the original bug: the editor could not distinguish “worked” from “refused”.

Watch your event sell, live

Read the sales dashboard for your own event and understand reserved versus sold.

Owned by Host / Promoter · 7 steps · about 12 minutes

Why this exists

This is the one staff-grade surface a scoped host gets in full, and it exists because a promoter who cannot see their own sales cannot promote. The sales endpoint is explicitly opened to admins, venue managers and the host scoped to that event — and to nobody else, for no other event.

What it will not show you is customer identity. You get counts and money; you do not get who bought what. The list of live holds, with buyer emails, is staff only. A host grant is a grant over an event, not over its audience.

The number people misread is reserved. Adding tickets to a cart reserves nothing at all. Inventory is only taken when a reservation is created at checkout, and that reservation has a short time-to-live (the event's hold TTL, between 5 and 10 minutes, default 10). During that window the tier's reserved count is up and its available count is down, and if the buyer wanders off the hold expires and the inventory comes straight back. So reserved is "someone is at the till", not "sold". Gross revenue on this dashboard counts only committed reservations — money that was actually paid.

This is also why an event can flip to sold out and then quietly un-sell-out twenty minutes later: sold out counts holds, and expiring holds reverse it.

Before you start

  • A host grant on an event that is on sale.
  • Ideally at least one live hold and one paid order to look at (the seed ships both on demo-event-0001).

Practise with

PersonaEmailPasswordNote
host_promoterhost@club.test host123scoped host on demo-event-0001: partly sold, one active hold
venue_managermanager@club.test manager123can see who is holding inventory right now
adminadmin@club.test admin123sees the orders and the money behind the same counts

Steps 1–4 — Host / Promoter

their manual →
  1. 1
    Open your event dashboard and find the Live Sales panel.
    /admin/events/{event_id} events
    Expected result Gross revenue and per-tier sold, reserved, available and percentage sold. The panel refreshes itself every 10 seconds.
    Watch out for Leave it open on a laptop during the on-sale. It is the fastest honest read you have.
  2. 2
    Pull the same dashboard as JSON when you want the raw numbers for your own spreadsheet.
    /api/admin/events/{event_id}/sales events
    Expected result Per-tier sold, reserved, available, percentage and gross in cents, plus the active hold count, the recent outbox rows and the event status log.
    Watch out for This is the single admin-shaped API a scoped host may call, and only for their own event. Put another promoter's event id in the URL and you get 403 forbidden.
  3. 3
    Read reserved and sold as different things: reserved is inventory held by someone mid-checkout on a short timer, sold is paid. Gross counts only what was paid.
    Expected result Reserved numbers that rise and fall on their own through the evening.
    Watch out for Do not announce a sell-out off the back of reserved. A tier at cap because of holds can drop back below cap minutes later when those holds expire, and the platform will reopen it automatically.
  4. 4
    Look at your own event the way your audience does, on the public page, while it is selling.
    /events/{event_id} events
    Expected result Live availability, countdowns, and the cascade headline when a tier unlocks.
    Watch out for This is the page your short links and social posts point at. If it looks wrong to you it looks wrong to everyone.

Steps 5 — Venue Manager

their manual →

Can see the live holds with buyer emails — a host deliberately cannot.

  1. 5
    As staff, look at who is holding inventory right now, with their email addresses and expiry times.
    /api/admin/events/{event_id}/reservations events
    Expected result Active holds with their line items and countdowns.
    Watch out for Admin and venue manager only — a scoped host gets 403 here. Buyer identity is not part of a host grant, and that is the deliberate line between 'your event' and 'our customers'.

Steps 6–7 — Admin

their manual →

Owns the orders, the refunds and the invariant checks behind the same numbers.

  1. 6
    As an admin, open the orders behind those counts: line items, tax, payment and refund controls.
    /admin/orders payments
    Expected result Every order for the event with its full history.
    Watch out for Refunding is admin-only and it is the one action here that moves money outward. A host asking for a refund to be issued is asking an admin, always.
  2. 7
    When the numbers look impossible, dump the event state and check the invariants: expected versus actual reserved counts recomputed from live reservation items.
    /debug/events/{event_id}/state events
    Expected result A full dump with an invariants block that says ok or names the drifting tier.
    Watch out for Admin-only debug. Reserved-count drift is repairable; do not fix it by editing the tier row by hand.

Cancel your own ticket and get refunded

Use the self-cancel window on your order, and understand the fee, the deadline and the credit-only rule.

Owned by Member · 9 steps · about 12 minutes

Why this exists

Self-cancellation is a pre-authorised refund: the venue decides the terms once, per event, and the platform then lets members act inside those terms without asking anyone. That is why the refund it issues is recorded as initiated by the system rather than by a person — nobody approved it, because the policy already did.

Three policy numbers do all the work, and all three are set per event. The cancellation deadline is a number of hours before doors; leave it empty and self-cancellation is simply off for that event. The non-refundable fee is withheld per refunded ticket, and it is snapshotted onto your order line at purchase time — so changing the policy later never rewrites what you were promised when you bought. Credit-only forces the refund to house credit rather than back to the card.

Cancellation is per ticket, not per order. You may cancel two of your four and keep the rest; each ticket may be refunded exactly once, enforced by a unique constraint rather than by the UI hiding a button. Refunding a ticket revokes it and kills its door pass, and the inventory goes straight back on sale.

The demo event ships with a $5 non-refundable fee and a 48-hour deadline, so you can see all of it working without configuring anything.

Before you start

  • A paid order with at least one issued ticket.
  • An event whose refund policy sets a cancellation deadline, and a clock still inside it.
  • An admin session for the participant steps.

Practise with

PersonaEmailPasswordNote
membernova@demo.club nova-pass-123holds a paid order with two tickets on the demo event
adminadmin@club.test admin123sets the policy and is the only persona who can refund outside it

Steps 1–6 — Member

their manual →
  1. 1
    Read the event's policy before you buy, not after. It is public.
    /api/events/{event_id}/refund-policy payments
    Expected result The non-refundable fee, whether refunds are credit-only, and the computed self-cancel deadline.
    Watch out for A null deadline means self-cancellation is disabled for that event entirely. No amount of clicking will produce a cancel button.
  2. 2
    Open your orders and pick the paid one.
    /my/orders payments
    Expected result Orders with their status and totals.
  3. 3
    Open the order. Tick the specific tickets you want to cancel and read the refund preview.
    /my/orders/{order_id} payments
    Expected result A per-ticket checkbox, a live preview of what you would get back, and the policy restated at the bottom of the page.
    Watch out for Tickets that cannot be cancelled have no checkbox at all — already refunded, past the deadline, or sold on the exchange.
  4. 4
    Cancel the selected tickets. Send their ids.
    /api/orders/{order_id}/cancel POST payments
    Expected result A refund for the ticket value plus its exact share of tax, minus the non-refundable fee per ticket, and the inventory returned to the tier.
    Watch out for Three refusals to recognise. Past the deadline is a 403. An event with no deadline configured is a 403 saying self-cancel is disabled. And a ticket you already sold on the resale exchange is a 409 saying it was transferred — you cannot refund a ticket that now belongs to someone else.
  5. 5
    Look at your wallet immediately afterwards.
    Expected result The pass for the cancelled ticket is revoked and shows as void.
    Watch out for Revocation is terminal and it blacklists the serial. Do not cancel a ticket you intend to use — there is no undo, and re-buying gets you a different ticket.
  6. 6
    If the event was credit-only, or you paid with credit in the first place, check where the money landed.
    /my/credit ledger
    Expected result Your prepaid balance has gone up by the refunded amount.
    Watch out for For an order paid with house credit, 'refund to the original method' means house credit. There is no card to refund, so asking for one is not an option the platform offers.

Steps 7–9 — Admin

their manual →

Owns the refund policy that decides whether you can self-cancel at all, and handles everything outside it.

  1. 7
    As an admin, open the policy editor for the event and read the three settings together.
    /admin/events/{event_id}/refund-policy payments
    Expected result Fee, credit-only flag and deadline hours, plus the refund-everyone danger zone.
  2. 8
    Change the policy and observe what it does and does not affect.
    /api/admin/events/{event_id}/refund-policy PUT payments
    Expected result New purchases carry the new fee.
    Watch out for It does not rewrite history. The fee is snapshotted onto existing order lines, so orders placed under the old policy keep the old terms. That is the point of snapshotting, not an oversight.
  3. 9
    Handle the case the policy does not cover: refund from the staff side, with a reason, optionally waiving fees or overriding the policy.
    /admin/orders/{order_id} payments
    Expected result A refund row recording the mode, the reason, who did it, and a snapshot of the policy at the time.
    Watch out for Refunding is admin-only. A venue manager can read this page and cannot refund from it — moving money outward is a deliberately narrower permission than running the night.

Monitor live sales on the night

Read the sales dashboard, the open holds and the order list without being able to touch prices.

Owned by Venue Manager · 10 steps · about 14 minutes

Why this exists

The sales board is a read surface for a manager, and that is a deliberate design decision rather than an oversight. You are the person who has to answer 'are we going to fill it', 'how many are inside', and 'why is that tier still showing available when the room is full' — and you can answer all three from here. What you cannot do from here is change a price, move a tier, or transition an event's status, because those decisions change what customers have already been sold.

The number that confuses people is reserved. Inventory is held during checkout: when a customer starts paying, their seats are moved out of available and into reserved for the hold's lifetime, and only converted to sold on payment. Holds exist so that two people cannot buy the last ticket simultaneously, and they expire on their own. So a tier can read zero available with nothing actually sold, simply because fifteen people are mid-checkout — and five minutes later it opens back up. Do not announce a sell-out off the availability figure alone.

The other thing to hold in your head is that a comp is a sale as far as capacity is concerned. Guest-list entries and door overrides both spend real inventory, so the sold figure includes people who paid nothing. That is intentional: capacity is a physical fact and the room does not care how anyone got in.

Pair this screen with the door. Sold tells you how many could come; the scan log tells you how many did. The gap between them is the single most useful operational number a venue manager has, and neither screen shows it on its own.

Before you start

  • A venue manager or admin session.
  • An event that is on sale or in progress. The seed ships demo-event-0001.

Practise with

PersonaEmailPasswordNote
venue_managermanager@club.test manager123shift lead: implies door_staff, plus intake, sales, guest list, marketing
adminadmin@club.test admin123the only account that can touch money, identity and contracts
door_staffdoor@club.test door123door shift account: scanner + guest list, nothing else

Steps 1–8 — Venue Manager

their manual →
  1. 1
    Open the events list. This is your board for the week — every event with its status and its headline sales figures.
    /admin/events events
    Expected result Events with status chips and sales columns.
    Watch out for Loading this page runs the event lifecycle tick, so statuses you see here are current rather than stale. That is also why it is a slightly heavier page than it looks.
  2. 2
    Open tonight's event. Read the dashboard block: per-tier sold, reserved and available, plus the totals.
    /admin/events/{event_id} events
    Expected result A tier table and a sales summary.
    Watch out for The edit controls are absent for you. The page computes an edit flag that is true only for admins, so you get the numbers without the levers — deliberately.
  3. 3
    Refresh the sales figures directly when you want the numbers without the page furniture — useful on a phone behind the bar.
    /api/admin/events/{event_id}/sales events
    Expected result The same dashboard payload as JSON.
    Watch out for This endpoint is also readable by the event's own scoped host, which is why a promoter can watch their night without you sending screenshots.
  4. 4
    When available looks wrong, list the active holds. Each is a customer part-way through checkout with an expiry.
    /api/admin/events/{event_id}/reservations events
    Expected result Open reservations with their items and expiry times.
    Watch out for This one is manager-and-admin only, unlike the sales figures — a promoter cannot see who is mid-checkout. Holds expiring is normal and self-healing; do not go hunting for a bug because a number moved.
  5. 5
    Move to the order list when a specific customer is the question rather than a specific tier.
    /admin/orders payments
    Expected result Orders with status, totals and customer.
    Watch out for Read-only for you. Refunds, refund policy and the dispute resolution controls are admin-only — you can see a chargeback, you cannot resolve it.
  6. 6
    Open a single order to answer a door question — did this person actually pay, and are these the tickets they are holding.
    /admin/orders/{order_id} payments
    Expected result The order, its tickets and its payment history.
    Watch out for This is the screen that resolves a Ticket Not Paid red at the door. A fully refunded order is exactly what that reason code means.
  7. 7
    Look at the public event page once before doors, the way a customer sees it.
    /events/{event_id} events
    Expected result The live catalogue page with what is actually on sale.
    Watch out for If a tier looks wrong to a customer it is wrong here too. Report it to an admin with the tier name — do not try to route around it.
  8. 8
    Understand the boundary you are about to bump into. Transitioning an event — announcing it, putting it on sale, cancelling it — and editing tiers, prices and zones are admin-only writes. So is refunding everybody after a cancellation.
    Expected result A clear escalation path rather than a 403 at 11pm.
    Watch out for If an event needs cancelling on the night, the manager job is to get an admin and to stop the door. Do not start checking people in on a cancelled event — the guest-list check-in refuses it anyway, with event_not_active.

Steps 9 — Door Staff

their manual →

Supplies the other half of the picture — how many of the sold tickets actually walked in.

  1. 9
    From the door, report the actual admitted count — green in scans for the event — so the manager can compare it against sold.
    Expected result The scan stats block with green and red counts.
    Watch out for Green out scans are people leaving, not entering. Count entries, not scans, or you will double-count everyone who stepped outside for air.

Steps 10 — Admin

their manual →

Owns every write on this page: transitions, tiers, prices and refunds.

  1. 10
    As an admin, make the status change the manager cannot: announce, open sales, mark in progress, complete or cancel.
    /api/events/{event_id}/transition POST events
    Expected result The event moves, and the change emits outbox events that marketing and resale react to.
    Watch out for Transitions are not just labels. Cancelling emits an event that downstream refund and exchange logic listens for — never fake one by editing a row.

Refund a ticket or an order

Set the policy, refund the right tickets, choose the destination, and read the posting it makes.

Owned by Admin · 11 steps · about 22 minutes

Why this exists

A refund on this platform is never just "send money back". One call does the processor refund, the balanced ledger posting, the refund records, the ticket revocation, the inventory return to the event and the audit — in a single transaction. Either all of that happened or none of it did, because a refunded ticket that still opens a door, or a returned seat that was never returned, are both worse than a failed refund.

The rule that catches people is that refunds are scoped to tickets the original purchaser still owns. If a ticket was resold, it belongs to somebody else now, and refunding the original order for it would pay the seller twice and strand the buyer. So the refund engine skips it, and a full refund of an order whose tickets have all moved on answers 409 nothing_to_refund. That is correct behaviour, not a bug.

Policy comes before the refund, not after. Each event may carry a non-refundable fee, a credit-only flag, and a self-cancellation deadline. Credit-only means exactly what it says: the money goes back as house credit and an attempt to refund to the card is refused with 422 policy_credit_only unless you deliberately override the policy. That refusal is a feature — it stops a well-meaning refund from quietly contradicting the terms the customer agreed to.

Note also that "original" for an order that was paid with house credit is house credit. There is no path that turns credit into cash through the refund engine; that is what payouts are for, and payouts have their own identity gate.

Refunding is admin-only. A venue manager can read every order and every dispute and cannot refund a penny. Refunds are the one routine action that moves money outward, so they sit with the group that carries that responsibility.

Before you start

  • An admin session. Venue managers are read-only here and members cannot see these pages at all.
  • A paid order with at least one live ticket the purchaser still owns.
  • The event's refund policy decided before you start — the refund snapshots it.

Practise with

PersonaEmailPasswordNote
adminadmin@club.test admin123the only persona that can issue a refund
membernova@demo.club nova-pass-123holds a paid order with tickets on the demo event
venue_managermanager@club.test manager123use it to prove the read-only boundary for yourself

Steps 1 — Member

their manual →

It is their money and their ticket — and where it lands depends on the policy, not on what they ask for.

  1. 1
    As the customer, open your order and see what you actually bought: line items, tax, and which tickets are still live.
    /my/orders/{order_id} payments
    Expected result The order with its tickets and, if the event allows self-cancellation, a self-cancel control.
    Watch out for If self-cancellation is available to you, use it — it is the same engine and it does not need anyone's morning. Past the deadline it is refused with 403 past_cancellation_deadline and it becomes an admin's job.

Steps 2 — Venue Manager

their manual →

They field the request and can read every order, but cannot issue the refund.

  1. 2
    As a manager, find the order the customer is asking about, by event, status or a text search.
    /admin/orders payments
    Expected result The order list with statuses and totals.
    Watch out for You can open everything here and refund nothing. The refund panel on the order page is admin-only, and the page now says so rather than leaving the space blank. Under that sentence is Ask an admin to refund: write why, press it once, and the order carries 'Asked . An admin has it' for you and the reason for them (plan 607). That is the escalation — not a message somewhere else — so the platform has a record the request was made. Do that rather than promising the customer a timeframe you do not control.

Steps 3–7 — Admin

their manual →
  1. 3
    Before refunding, read the event's policy: the non-refundable fee, whether refunds are credit-only, and the self-cancellation deadline.
    /admin/events/{event_id}/refund-policy payments
    Expected result The effective policy, with a note if it is the platform default rather than an event-specific one.
    Watch out for No policy row means defaults: no fee, not credit-only, and self-cancellation disabled. Silence is a policy too.
  2. 4
    Set the policy for the event if it needs one: fee in cents, credit-only on or off, deadline in hours before doors, or no deadline at all to disable self-cancellation.
    /api/admin/events/{event_id}/refund-policy PUT payments
    Expected result The saved policy.
    Watch out for Changing a policy does not change refunds already issued — each refund snapshots the policy it was made under. That snapshot is what you will be reading back in six months when somebody disputes it.
  3. 5
    Open the order and use the refund panel. Choose the mode deliberately: the whole order, specific tickets, or a bare amount.
    /admin/orders/{order_id} payments
    Expected result The panel showing what is refundable, with the fee the policy will withhold.
    Watch out for Refunding specific tickets is almost always the honest choice, because it is the only mode that also revokes exactly those tickets and returns exactly that inventory.
  4. 6
    Issue the refund with a real reason. Pick the destination — back to the original rail, or to house credit — and decide whether to waive the fee.
    /api/admin/orders/{order_id}/refunds POST payments
    Expected result 201 with the refund id, the amount, the fee withheld, the ledger transaction id, the revoked ticket ids and the order's new status.
    Watch out for A missing reason is 422. A disputed payment is 409 charge_disputed — you cannot refund around a chargeback. A ticket that is already refunded, has been transferred, or is mid-resale-settlement each has its own 409, and each is telling you something specific about who owns what.
  5. 7
    Deliberately try to refund a credit-only event back to the card, once, so you recognise the refusal.
    /api/admin/orders/{order_id}/refunds POST payments
    Expected result 422 policy_credit_only, explaining that the event allows refunds to house credit only.
    Watch out for There is an override, and using it is a decision you should be able to defend. It is recorded on the refund as a policy override.

Steps 8 — Member

their manual →

It is their money and their ticket — and where it lands depends on the policy, not on what they ask for.

  1. 8
    As the customer, check where the money landed when the destination was house credit.
    /my/credit ledger
    Expected result The prepaid balance is higher and the statement shows the refund.
    Watch out for House credit is spending power inside the club, not cash. If the customer wanted cash they wanted the original rail, and that decision was made at refund time.

Steps 9–11 — Admin

their manual →
  1. 9
    Find the posting behind the refund, filtering by the refund reference or the kind.
    Expected result A balanced transaction reversing revenue and tax pro-rata, crediting either the cash clearing account or the customer's house credit.
    Watch out for Tax is reversed proportionally and is capped so cumulative reversals can never exceed the original accrual. That cap is why a refund of a refund does not become a tax rebate.
  2. 10
    Pull the refund register when you need the shape of the last month rather than one case.
    Expected result Refunds filterable by event, initiator and mode.
    Watch out for Admin-only, unlike the order list. Refund volume by initiator is a management report, not a customer service tool.
  3. 11
    Know the big red button exists: cancelling an event refunds every order on it, with fees waived.
    /api/admin/events/{event_id}/refund-all POST payments
    Expected result A count of refunded orders, plus the resale settlements that were unwound first, plus any per-order failures collected rather than aborting the run.
    Watch out for It unwinds resales BEFORE it refunds primaries, so a ticket that changed hands walks back to its original buyer and is refunded exactly once. Where a reseller already spent their proceeds, the shortfall is written off to a dedicated account and flagged as unrecovered — the platform absorbs it so the resale buyer is always made whole.

Appendix A — Persona ↔ RBAC group

PersonaRBAC groupAuthenticatedScoped OwnsTakes part in
Customer (Guest) no no 4 2
Member member yes no 13 18
VIP Member vip_member yes no 2 7
Host / Promoter host yes yes 11 11
Door Staff door_staff yes no 5 14
Venue Manager venue_manager yes no 17 24
Admin admin yes no 24 43

Appendix B — Route index

Every application route this manual references, and the workflows that use it. A test asserts each of these resolves against the mounted router, so the manual cannot silently rot when a route moves.

RouteUsed by
DELETE /api/admin/resale/events/{event_id}/lock open-or-close-the-exchange
DELETE /api/guestlist/buckets/{bucket_id} allocate-comps-to-a-promoter
DELETE /api/reservations/{reservation_id} hold-a-checkout-reservation
DELETE /debug/mock-behaviors/{behavior_id} script-a-mock-failure
GET / browse-events-as-a-guest, buy-a-ticket
GET /account convert-an-application-into-an-event, grant-and-revoke-roles, build-a-role-from-capabilities, create-your-member-account, manage-your-account-and-sessions
GET /account/passes read-the-scanner-screen, diagnose-a-red-scan, map-groups-to-door-zones, self-cancel-a-ticket, add-your-ticket-to-your-wallet, use-your-membership-card-at-the-bar, resell-a-ticket-you-cant-use, buy-a-resale-ticket, claim-your-comp-invite, get-through-the-door
GET /admin find-your-way-around-as-staff, edit-any-row-with-admin-crud, find-anything-in-admin
GET /admin/access read-the-scanner-screen, diagnose-a-red-scan, run-the-door-offline, map-groups-to-door-zones, manage-your-account-and-sessions, add-your-ticket-to-your-wallet, get-through-the-door, scan-guests-at-the-door, find-anything-in-admin
GET /admin/annotations leave-feedback-on-the-app, run-the-feedback-backlog, preview-the-site-as-a-guest
GET /admin/annotations/export leave-feedback-on-the-app, run-the-feedback-backlog
GET /admin/annotations/{annotation_id} run-the-feedback-backlog, preview-the-site-as-a-guest
GET /admin/announcements get-into-a-member-only-drop
GET /admin/audit grant-and-revoke-roles, deactivate-a-user, create-your-member-account
GET /admin/capabilities build-a-role-from-capabilities
GET /admin/contract-riders author-a-rider-so-a-host-can-ask-for-it
GET /admin/contract-riders/new author-a-rider-so-a-host-can-ask-for-it
GET /admin/contract-riders/recent author-a-rider-so-a-host-can-ask-for-it
GET /admin/contract-riders/{rider_key} author-a-rider-so-a-host-can-ask-for-it
GET /admin/contracts review-the-riders-approval-created, sign-and-countersign-a-contract, approve-a-host-application
GET /admin/contracts/{contract_id} review-the-riders-approval-created, sign-and-countersign-a-contract, create-and-launch-an-event, approve-a-host-application
GET /admin/credit/users/{user_id} post-a-house-credit-adjustment, pay-with-house-credit
GET /admin/data edit-any-row-with-admin-crud
GET /admin/data/audit edit-any-row-with-admin-crud, delete-rows-safely
GET /admin/data/ledger_entries edit-any-row-with-admin-crud, read-the-trial-balance
GET /admin/data/qa_invariant_violations run-the-qa-invariant-sweep
GET /admin/data/sandbox_parents edit-any-row-with-admin-crud
GET /admin/data/{table}/new edit-any-row-with-admin-crud
GET /admin/data/{table}/{pk} edit-any-row-with-admin-crud, delete-rows-safely
GET /admin/events find-your-way-around-as-staff, monitor-live-sales-on-the-night, understand-your-events-tiers-and-releases
GET /admin/events/backload understand-your-events-tiers-and-releases
GET /admin/events/{event_id} monitor-live-sales-on-the-night, convert-an-application-into-an-event, grant-and-revoke-roles, get-into-a-member-only-drop, get-your-host-account-and-event-access, understand-your-events-tiers-and-releases, watch-your-event-sell-live, create-and-launch-an-event
GET /admin/events/{event_id}/record keep-the-permanent-event-record
GET /admin/events/{event_id}/record/documents/{doc_id} keep-the-permanent-event-record
GET /admin/events/{event_id}/refund-policy refund-a-ticket-or-order, self-cancel-a-ticket
GET /admin/groups find-your-way-around-as-staff, map-groups-to-door-zones, build-a-role-from-capabilities
GET /admin/groups/new build-a-role-from-capabilities
GET /admin/groups/{slug} build-a-role-from-capabilities
GET /admin/guestlist find-your-way-around-as-staff, allocate-comps-to-a-promoter
GET /admin/guestlist/{event_id} check-in-a-guest-from-the-list, issue-a-vip-walk-in-override, allocate-comps-to-a-promoter, revoke-a-comp, get-your-host-account-and-event-access, invite-a-guest-and-track-plus-ones
GET /admin/intake find-your-way-around-as-staff, convert-an-application-into-an-event, apply-to-host-an-event, build-your-run-of-show-backwards, create-and-launch-an-event, approve-a-host-application, run-the-staff-channel-on-live-telegram
GET /admin/intake/rider-requests author-a-rider-so-a-host-can-ask-for-it
GET /admin/intake/{app_id} fast-track-from-telegram, review-the-riders-approval-created, convert-an-application-into-an-event, apply-to-host-an-event, re-apply-with-autofill, respond-to-a-request-for-more-information, author-a-rider-so-a-host-can-ask-for-it, build-your-run-of-show-backwards, create-and-launch-an-event, approve-a-host-application, run-the-staff-channel-on-live-telegram
GET /admin/ledger read-the-trial-balance, reconcile-tax-accruals, top-up-your-house-credit, read-your-credit-statement, run-the-quarterly-tax-filing
GET /admin/ledger/transactions refund-a-ticket-or-order, read-the-trial-balance, process-a-payout-run, run-the-vip-tab-settlement, reconcile-tax-accruals, pay-with-house-credit, use-and-settle-your-vip-tab
GET /admin/ledger/transactions/{txn_id} post-a-house-credit-adjustment, read-the-trial-balance, top-up-your-house-credit
GET /admin/marketing find-your-way-around-as-staff, compose-and-publish-a-social-post, configure-marketing-channels-and-rules
GET /admin/marketing/events/{event_id} compose-and-publish-a-social-post, build-a-trigger-rule, configure-marketing-channels-and-rules
GET /admin/marketing/events/{event_id}/rules/new build-a-trigger-rule, configure-marketing-channels-and-rules
GET /admin/marketing/posts/new compose-and-publish-a-social-post
GET /admin/marketing/posts/{post_id} compose-and-publish-a-social-post, build-a-trigger-rule
GET /admin/marketing/reports trace-a-click-to-revenue, report-marketing-attribution
GET /admin/marketing/rules/{rule_id}/edit build-a-trigger-rule
GET /admin/orders monitor-live-sales-on-the-night, refund-a-ticket-or-order, watch-your-event-sell-live
GET /admin/orders/{order_id} diagnose-a-red-scan, monitor-live-sales-on-the-night, trace-a-click-to-revenue, refund-a-ticket-or-order, self-cancel-a-ticket, buy-a-ticket
GET /admin/payouts process-a-payout-run, get-paid-your-host-royalty
GET /admin/qa run-the-qa-invariant-sweep
GET /admin/records keep-the-permanent-event-record
GET /admin/records/devices keep-the-permanent-event-record
GET /admin/records/metrics keep-the-permanent-event-record
GET /admin/resale open-or-close-the-exchange
GET /admin/resale/{event_id} open-or-close-the-exchange, resell-a-ticket-you-cant-use, get-paid-your-host-royalty, buy-a-resale-ticket, understand-your-credit-and-the-exchange
GET /admin/tab-settlements script-a-mock-failure, run-the-vip-tab-settlement, use-and-settle-your-vip-tab
GET /admin/tax approve-a-filing-from-telegram, reconcile-tax-accruals, run-the-quarterly-tax-filing
GET /admin/tax/periods/{period_id} approve-a-filing-from-telegram, run-the-quarterly-tax-filing
GET /admin/tax/rates maintain-the-tax-rate-matrix
GET /admin/tax/settings approve-a-filing-from-telegram
GET /admin/telegram run-the-staff-channel-on-live-telegram
GET /admin/users grant-and-revoke-roles
GET /admin/users/{user_id} convert-an-application-into-an-event, grant-and-revoke-roles, deactivate-a-user, build-a-role-from-capabilities, create-your-member-account, manage-your-account-and-sessions, use-your-membership-card-at-the-bar, get-your-host-account-and-event-access
GET /admin/venues set-up-a-venue-and-its-door-zones
GET /admin/venues/new set-up-a-venue-and-its-door-zones
GET /admin/venues/{slug} set-up-a-venue-and-its-door-zones
GET /admin/venues/{venue_id}/layout draw-a-venue-map
GET /api/access/passes/mine add-your-ticket-to-your-wallet
GET /api/access/passes/{pass_id}/gpass add-your-ticket-to-your-wallet
GET /api/access/passes/{pass_id}/pkpass add-your-ticket-to-your-wallet, get-through-the-door
GET /api/access/passes/{pass_id}/qr read-the-scanner-screen, add-your-ticket-to-your-wallet, use-your-membership-card-at-the-bar, get-through-the-door
GET /api/access/scan-logs read-the-scanner-screen, diagnose-a-red-scan, run-the-door-offline, monitor-live-sales-on-the-night, get-through-the-door, scan-guests-at-the-door
GET /api/access/sync/bundle run-the-door-offline, scan-guests-at-the-door
GET /api/access/sync/push run-the-door-offline
GET /api/access/tickets/{ticket_id}/secret add-your-ticket-to-your-wallet
GET /api/admin/data/{table}/rows/{pk}/dependencies delete-rows-safely
GET /api/admin/events/{event_id}/reservations monitor-live-sales-on-the-night, hold-a-checkout-reservation, watch-your-event-sell-live
GET /api/admin/events/{event_id}/sales monitor-live-sales-on-the-night, watch-your-event-sell-live
GET /api/admin/ledger/accounts read-the-trial-balance
GET /api/admin/ledger/transactions read-the-trial-balance
GET /api/admin/ledger/trial-balance read-the-trial-balance
GET /api/admin/ledger/users/{user_id} post-a-house-credit-adjustment
GET /api/admin/refunds refund-a-ticket-or-order
GET /api/admin/resale/settlements get-paid-your-host-royalty
GET /api/annotations/list run-the-feedback-backlog
GET /api/annotations/pages run-the-feedback-backlog
GET /api/contracts/riders/selectable author-a-rider-so-a-host-can-ask-for-it
GET /api/credit/balance pay-with-house-credit, top-up-your-house-credit, read-your-credit-statement, get-paid-your-host-royalty, use-and-settle-your-vip-tab
GET /api/credit/statement read-your-credit-statement
GET /api/events/{event_id}/availability browse-events-as-a-guest, get-into-a-member-only-drop, understand-your-events-tiers-and-releases
GET /api/events/{event_id}/refund-policy self-cancel-a-ticket
GET /api/events/{event_id}/zones set-up-a-venue-and-its-door-zones
GET /api/guestlist/door/{event_id}/search check-in-a-guest-from-the-list, allocate-comps-to-a-promoter, claim-your-comp-invite
GET /api/guestlist/events/{event_id}/buckets allocate-comps-to-a-promoter, revoke-a-comp, invite-a-guest-and-track-plus-ones
GET /api/guestlist/events/{event_id}/entries invite-a-guest-and-track-plus-ones
GET /api/guestlist/events/{event_id}/overrides issue-a-vip-walk-in-override
GET /api/guestlist/events/{event_id}/settings issue-a-vip-walk-in-override
GET /api/intake/public/applications/{app_id}/edit respond-to-a-request-for-more-information
GET /api/marketing/channels configure-marketing-channels-and-rules
GET /api/marketing/links trace-a-click-to-revenue, report-marketing-attribution
GET /api/marketing/reports/channels trace-a-click-to-revenue, report-marketing-attribution
GET /api/marketing/reports/events/{event_id} trace-a-click-to-revenue, report-marketing-attribution
GET /api/marketing/rules/{rule_id}/firings build-a-trigger-rule, configure-marketing-channels-and-rules
GET /api/rbac/groups grant-and-revoke-roles
GET /api/rbac/matrix build-a-role-from-capabilities
GET /api/rbac/users/{user_id}/capabilities build-a-role-from-capabilities
GET /api/records/compare keep-the-permanent-event-record
GET /api/records/{event_id}/seal-check keep-the-permanent-event-record
GET /api/records/{event_id}/verify keep-the-permanent-event-record
GET /api/resale/events/{event_id} buy-a-resale-ticket
GET /api/resale/my/purchases buy-a-resale-ticket
GET /api/reservations/{reservation_id} hold-a-checkout-reservation
GET /api/tax/agencies maintain-the-tax-rate-matrix
GET /api/tax/documents/{doc_id} approve-a-filing-from-telegram
GET /api/tax/liability reconcile-tax-accruals, run-the-quarterly-tax-filing
GET /api/tax/periods reconcile-tax-accruals
GET /api/tax/quote maintain-the-tax-rate-matrix
GET /api/tax/rates maintain-the-tax-rate-matrix
GET /api/venues/{venue_id}/zones set-up-a-venue-and-its-door-zones
GET /apply/edit/{token} fast-track-from-telegram, respond-to-a-request-for-more-information, author-a-rider-so-a-host-can-ask-for-it, build-your-run-of-show-backwards, approve-a-host-application
GET /apply/thanks/{app_id} apply-to-host-an-event, re-apply-with-autofill, build-your-run-of-show-backwards, create-and-launch-an-event
GET /auth/me manage-your-account-and-sessions
GET /cart hold-a-checkout-reservation
GET /checkout/{order_id} hold-a-checkout-reservation, pay-with-house-credit, get-into-a-member-only-drop, buy-a-ticket, use-and-settle-your-vip-tab
GET /clips/{workflow_slug} leave-feedback-on-the-app
GET /contracts/{contract_id}/preview review-the-riders-approval-created
GET /contracts/{contract_id}/redlines review-the-riders-approval-created, sign-and-countersign-a-contract
GET /contracts/{contract_id}/sealed sign-and-countersign-a-contract
GET /debug use-the-debug-console
GET /debug/access/totp/{ticket_id} diagnose-a-red-scan
GET /debug/adminsuite/dependencies delete-rows-safely
GET /debug/adminsuite/policies edit-any-row-with-admin-crud
GET /debug/annotations/audit run-the-feedback-backlog
GET /debug/annotations/orphans run-the-feedback-backlog
GET /debug/audit use-the-debug-console, script-a-mock-failure
GET /debug/events/reservations/sweep-preview hold-a-checkout-reservation
GET /debug/events/{event_id}/state watch-your-event-sell-live
GET /debug/frontend/preview preview-the-site-as-a-guest
GET /debug/frontend/preview/gate preview-the-site-as-a-guest
GET /debug/guestlist/audit/{event_id} check-in-a-guest-from-the-list
GET /debug/intake/deeplinks run-the-staff-channel-on-live-telegram
GET /debug/intake/rate-limits re-apply-with-autofill
GET /debug/intake/timeline build-your-run-of-show-backwards
GET /debug/intake/timeline-render/{app_id} build-your-run-of-show-backwards
GET /debug/intake/tz build-your-run-of-show-backwards
GET /debug/ledger/integrity post-a-house-credit-adjustment, read-the-trial-balance
GET /debug/marketing/links/{short_code} trace-a-click-to-revenue, report-marketing-attribution
GET /debug/marketing/outbound configure-marketing-channels-and-rules
GET /debug/mock-behaviors script-a-mock-failure
GET /debug/outbound-calls fast-track-from-telegram, use-the-debug-console, script-a-mock-failure, approve-a-filing-from-telegram, apply-to-host-an-event, run-the-staff-channel-on-live-telegram
GET /debug/qa/health run-the-qa-invariant-sweep
GET /debug/qa/invariants run-the-qa-invariant-sweep
GET /debug/qa/invariants/runs run-the-qa-invariant-sweep
GET /debug/qa/journeys run-the-qa-invariant-sweep
GET /debug/rbac/graph build-a-role-from-capabilities
GET /debug/rbac/pass-refresh-queue build-a-role-from-capabilities
GET /debug/rbac/state grant-and-revoke-roles, deactivate-a-user
GET /debug/rbac/zones map-groups-to-door-zones
GET /debug/record/verify-all keep-the-permanent-event-record
GET /debug/record/{event_id} keep-the-permanent-event-record
GET /debug/resale/integrity/{event_id} open-or-close-the-exchange
GET /debug/resale/split-preview open-or-close-the-exchange, resell-a-ticket-you-cant-use
GET /debug/scheduler/runs use-the-debug-console
GET /debug/settings use-the-debug-console
GET /debug/state use-the-debug-console
GET /debug/tax/rate-resolution maintain-the-tax-rate-matrix
GET /debug/tax/reconcile reconcile-tax-accruals
GET /debug/telegram/webhook-info run-the-staff-channel-on-live-telegram
GET /debug/venues/zone-map set-up-a-venue-and-its-door-zones
GET /events build-a-role-from-capabilities, browse-events-as-a-guest, leave-feedback-on-the-app, preview-the-site-as-a-guest, buy-a-ticket
GET /events/{event_id} monitor-live-sales-on-the-night, build-a-trigger-rule, browse-events-as-a-guest, create-your-member-account, get-into-a-member-only-drop, sign-and-countersign-a-contract, get-your-host-account-and-event-access, understand-your-events-tiers-and-releases, watch-your-event-sell-live, preview-the-site-as-a-guest, create-and-launch-an-event, buy-a-ticket, understand-your-credit-and-the-exchange
GET /events/{event_id}/buy browse-events-as-a-guest
GET /events/{event_id}/checkout hold-a-checkout-reservation, get-into-a-member-only-drop, buy-a-ticket
GET /events/{event_id}/exchange open-or-close-the-exchange, buy-a-resale-ticket, get-paid-your-host-royalty, understand-your-credit-and-the-exchange
GET /exchange browse-events-as-a-guest, buy-a-resale-ticket
GET /guestlist/claim/{token} claim-your-comp-invite, invite-a-guest-and-track-plus-ones
GET /host/apply browse-events-as-a-guest, apply-to-host-an-event, re-apply-with-autofill, author-a-rider-so-a-host-can-ask-for-it, build-your-run-of-show-backwards, create-and-launch-an-event
GET /l/{short_code} trace-a-click-to-revenue, report-marketing-attribution
GET /login deactivate-a-user, browse-events-as-a-guest, create-your-member-account, get-your-host-account-and-event-access
GET /me/comps claim-your-comp-invite
GET /my create-your-member-account, get-into-a-member-only-drop, preview-the-site-as-a-guest
GET /my/annotations leave-feedback-on-the-app, run-the-feedback-backlog
GET /my/credit refund-a-ticket-or-order, run-the-vip-tab-settlement, open-or-close-the-exchange, pay-with-house-credit, self-cancel-a-ticket, use-your-membership-card-at-the-bar, resell-a-ticket-you-cant-use, top-up-your-house-credit, read-your-credit-statement, get-paid-your-host-royalty, buy-a-ticket, use-and-settle-your-vip-tab, understand-your-credit-and-the-exchange
GET /my/credit/payouts process-a-payout-run, get-paid-your-host-royalty, understand-your-credit-and-the-exchange
GET /my/credit/statement post-a-house-credit-adjustment, pay-with-house-credit, top-up-your-house-credit, read-your-credit-statement, get-paid-your-host-royalty, use-and-settle-your-vip-tab
GET /my/orders self-cancel-a-ticket
GET /my/orders/{order_id} refund-a-ticket-or-order, self-cancel-a-ticket
GET /my/tickets add-your-ticket-to-your-wallet, resell-a-ticket-you-cant-use, buy-a-ticket, get-through-the-door, understand-your-credit-and-the-exchange
GET /register create-your-member-account, buy-a-ticket
GET /resale/my/listings resell-a-ticket-you-cant-use, understand-your-credit-and-the-exchange
GET /resale/sell/{ticket_id} open-or-close-the-exchange, resell-a-ticket-you-cant-use, understand-your-credit-and-the-exchange
GET /scanner find-your-way-around-as-staff, read-the-scanner-screen, diagnose-a-red-scan, run-the-door-offline, revoke-a-comp, grant-and-revoke-roles, map-groups-to-door-zones, deactivate-a-user, get-through-the-door, scan-guests-at-the-door
GET /scanner/guestlist find-your-way-around-as-staff, diagnose-a-red-scan, check-in-a-guest-from-the-list, issue-a-vip-walk-in-override, invite-a-guest-and-track-plus-ones, scan-guests-at-the-door
GET /sign/{token} sign-and-countersign-a-contract, create-and-launch-an-event
GET /sign/{token}/status sign-and-countersign-a-contract
GET /training/role/host_promoter find-your-way-around-as-staff
GET /ui/preview preview-the-site-as-a-guest
GET /venues/{venue_id}/map draw-a-venue-map
PATCH /api/admin/data/{table}/rows/{pk} edit-any-row-with-admin-crud
PATCH /api/contracts/riders/{rider_key} author-a-rider-so-a-host-can-ask-for-it
PATCH /api/guestlist/buckets/{bucket_id} allocate-comps-to-a-promoter
PATCH /api/marketing/channels/{platform} compose-and-publish-a-social-post, configure-marketing-channels-and-rules
PATCH /api/marketing/posts/{post_id} compose-and-publish-a-social-post
PATCH /api/tax/rates/{rate_id} maintain-the-tax-rate-matrix
PATCH /api/tiers/{tier_id} understand-your-events-tiers-and-releases
PATCH /api/venues/{venue_id}/zones/{zone_id} set-up-a-venue-and-its-door-zones
PATCH /contracts/{contract_id}/sections/{section_id} review-the-riders-approval-created
PATCH /contracts/{contract_id}/variables review-the-riders-approval-created
POST /admin/annotations/{annotation_id} leave-feedback-on-the-app, run-the-feedback-backlog
POST /admin/annotations/{annotation_id}/delete run-the-feedback-backlog
POST /admin/annotations/{annotation_id}/reply run-the-feedback-backlog
POST /admin/annotations/{annotation_id}/restore run-the-feedback-backlog
POST /admin/announcements get-into-a-member-only-drop
POST /admin/venues/new set-up-a-venue-and-its-door-zones
POST /api/access/membership-card use-your-membership-card-at-the-bar
POST /api/access/readers run-the-door-offline, scan-guests-at-the-door
POST /api/access/readers/{reader_id}/rotate-token run-the-door-offline
POST /api/access/scan read-the-scanner-screen, use-your-membership-card-at-the-bar, resell-a-ticket-you-cant-use, buy-a-resale-ticket, get-through-the-door, scan-guests-at-the-door
POST /api/access/scan/batch run-the-door-offline
POST /api/admin/credit/users/{user_id}/kyc/revoke process-a-payout-run
POST /api/admin/data/{table}/delete delete-rows-safely
POST /api/admin/data/{table}/rows/{pk}/restore delete-rows-safely
POST /api/admin/events/{event_id}/refund-all refund-a-ticket-or-order
POST /api/admin/ledger/adjustments post-a-house-credit-adjustment, read-your-credit-statement
POST /api/admin/orders/{order_id}/refunds refund-a-ticket-or-order
POST /api/admin/payouts/{payout_id}/process process-a-payout-run, get-paid-your-host-royalty
POST /api/admin/resale/events/{event_id}/lock open-or-close-the-exchange
POST /api/admin/tab-settlements/run run-the-vip-tab-settlement, use-and-settle-your-vip-tab
POST /api/admin/tab-settlements/{settlement_id}/retry run-the-vip-tab-settlement
POST /api/admin/tab-settlements/{settlement_id}/waive run-the-vip-tab-settlement
POST /api/annotations leave-feedback-on-the-app, run-the-feedback-backlog, preview-the-site-as-a-guest
POST /api/annotations/mode leave-feedback-on-the-app
POST /api/cart/items hold-a-checkout-reservation, buy-a-ticket
POST /api/checkout hold-a-checkout-reservation, buy-a-ticket
POST /api/contracts/rider-versions/{template_id}/approve author-a-rider-so-a-host-can-ask-for-it
POST /api/contracts/rider-versions/{template_id}/reject author-a-rider-so-a-host-can-ask-for-it
POST /api/contracts/rider-versions/{template_id}/submit author-a-rider-so-a-host-can-ask-for-it
POST /api/contracts/riders/{rider_key}/promote author-a-rider-so-a-host-can-ask-for-it
POST /api/credit/kyc process-a-payout-run, get-paid-your-host-royalty
POST /api/credit/payouts process-a-payout-run, get-paid-your-host-royalty
POST /api/credit/topup top-up-your-house-credit, understand-your-credit-and-the-exchange
POST /api/events/{event_id}/capacity-override set-up-a-venue-and-its-door-zones
POST /api/events/{event_id}/reservations hold-a-checkout-reservation
POST /api/events/{event_id}/tiers get-into-a-member-only-drop, understand-your-events-tiers-and-releases
POST /api/events/{event_id}/transition monitor-live-sales-on-the-night, understand-your-events-tiers-and-releases, create-and-launch-an-event
POST /api/events/{event_id}/zones set-up-a-venue-and-its-door-zones
POST /api/events/{event_id}/zones/{zone_ref}/activate set-up-a-venue-and-its-door-zones
POST /api/events/{event_id}/zones/{zone_ref}/retire set-up-a-venue-and-its-door-zones
POST /api/guestlist/buckets/{bucket_id}/entries allocate-comps-to-a-promoter, invite-a-guest-and-track-plus-ones
POST /api/guestlist/claim/{token} claim-your-comp-invite, invite-a-guest-and-track-plus-ones
POST /api/guestlist/claim/{token}/plus-ones claim-your-comp-invite
POST /api/guestlist/door/{event_id}/entries/{entry_id}/checkin check-in-a-guest-from-the-list, claim-your-comp-invite, invite-a-guest-and-track-plus-ones, scan-guests-at-the-door
POST /api/guestlist/door/{event_id}/entries/{entry_id}/issue-and-checkin check-in-a-guest-from-the-list
POST /api/guestlist/door/{event_id}/override issue-a-vip-walk-in-override
POST /api/guestlist/entries/{entry_id}/resend revoke-a-comp, invite-a-guest-and-track-plus-ones
POST /api/guestlist/entries/{entry_id}/revoke revoke-a-comp, invite-a-guest-and-track-plus-ones
POST /api/guestlist/events/{event_id}/buckets allocate-comps-to-a-promoter, invite-a-guest-and-track-plus-ones
POST /api/guestlist/my/entries/{entry_id}/claim claim-your-comp-invite
POST /api/intake/applications/{app_id}/approve fast-track-from-telegram, respond-to-a-request-for-more-information, create-and-launch-an-event, approve-a-host-application
POST /api/intake/applications/{app_id}/author-rider author-a-rider-so-a-host-can-ask-for-it
POST /api/intake/applications/{app_id}/convert convert-an-application-into-an-event, get-your-host-account-and-event-access, create-and-launch-an-event
POST /api/intake/applications/{app_id}/decline fast-track-from-telegram
POST /api/intake/applications/{app_id}/notes fast-track-from-telegram
POST /api/intake/applications/{app_id}/recompute-viability fast-track-from-telegram
POST /api/intake/applications/{app_id}/request-info respond-to-a-request-for-more-information, approve-a-host-application
POST /api/intake/public/applications apply-to-host-an-event, re-apply-with-autofill, author-a-rider-so-a-host-can-ask-for-it, build-your-run-of-show-backwards, create-and-launch-an-event, run-the-staff-channel-on-live-telegram
POST /api/intake/public/prefill/start re-apply-with-autofill
POST /api/intake/public/prefill/verify re-apply-with-autofill
POST /api/intake/public/rider-requests author-a-rider-so-a-host-can-ask-for-it
POST /api/intake/public/timeline/preview build-your-run-of-show-backwards
POST /api/intake/rider-requests/{request_id}/author author-a-rider-so-a-host-can-ask-for-it
POST /api/intake/rider-requests/{request_id}/decline author-a-rider-so-a-host-can-ask-for-it
POST /api/intake/rider-requests/{request_id}/triage author-a-rider-so-a-host-can-ask-for-it
POST /api/marketing/events/{event_id}/rules build-a-trigger-rule, configure-marketing-channels-and-rules
POST /api/marketing/posts compose-and-publish-a-social-post
POST /api/marketing/posts/{post_id}/cancel compose-and-publish-a-social-post
POST /api/marketing/posts/{post_id}/retry compose-and-publish-a-social-post
POST /api/orders/{order_id}/cancel hold-a-checkout-reservation, self-cancel-a-ticket
POST /api/orders/{order_id}/pay hold-a-checkout-reservation, pay-with-house-credit, buy-a-ticket
POST /api/rbac/groups build-a-role-from-capabilities
POST /api/rbac/tiers/reorder build-a-role-from-capabilities
POST /api/rbac/users/{user_id}/deactivate deactivate-a-user
POST /api/rbac/users/{user_id}/groups grant-and-revoke-roles, build-a-role-from-capabilities, get-your-host-account-and-event-access
POST /api/rbac/users/{user_id}/groups/{grant_id}/revoke grant-and-revoke-roles
POST /api/rbac/users/{user_id}/reactivate deactivate-a-user
POST /api/rbac/users/{user_id}/sessions/revoke-all deactivate-a-user
POST /api/record/devices keep-the-permanent-event-record
POST /api/record/devices/{device_id}/rotate-token keep-the-permanent-event-record
POST /api/record/ingest keep-the-permanent-event-record
POST /api/record/metrics keep-the-permanent-event-record
POST /api/records/{event_id}/amendments keep-the-permanent-event-record
POST /api/records/{event_id}/measurements keep-the-permanent-event-record
POST /api/records/{event_id}/measurements/import keep-the-permanent-event-record
POST /api/records/{event_id}/open keep-the-permanent-event-record
POST /api/records/{event_id}/seal keep-the-permanent-event-record
POST /api/resale/listings resell-a-ticket-you-cant-use
POST /api/resale/listings/{listing_id}/purchase buy-a-resale-ticket
POST /api/tax/packages/{package_id}/approve run-the-quarterly-tax-filing
POST /api/tax/packages/{package_id}/retry-mail approve-a-filing-from-telegram
POST /api/tax/packages/{package_id}/send-approval approve-a-filing-from-telegram, run-the-quarterly-tax-filing
POST /api/tax/periods/{period_id}/regenerate reconcile-tax-accruals
POST /api/tax/rates maintain-the-tax-rate-matrix
POST /api/telegram/test-send run-the-staff-channel-on-live-telegram
POST /api/telegram/webhook/delete run-the-staff-channel-on-live-telegram
POST /api/telegram/webhook/register run-the-staff-channel-on-live-telegram
POST /api/venues/{venue_id}/archive set-up-a-venue-and-its-door-zones
POST /api/venues/{venue_id}/default set-up-a-venue-and-its-door-zones
POST /api/venues/{venue_id}/layout draw-a-venue-map
POST /api/venues/{venue_id}/shapes draw-a-venue-map
POST /api/venues/{venue_id}/zones set-up-a-venue-and-its-door-zones
POST /api/venues/{venue_id}/zones/{zone_id}/attach set-up-a-venue-and-its-door-zones
POST /auth/login get-your-host-account-and-event-access
POST /auth/password manage-your-account-and-sessions
POST /auth/register create-your-member-account, buy-a-ticket
POST /auth/sessions/{session_id}/revoke manage-your-account-and-sessions
POST /contracts/{contract_id}/countersign sign-and-countersign-a-contract, create-and-launch-an-event
POST /contracts/{contract_id}/lock review-the-riders-approval-created, sign-and-countersign-a-contract, create-and-launch-an-event
POST /contracts/{contract_id}/resync-riders review-the-riders-approval-created
POST /debug/access/simulate-scan diagnose-a-red-scan
POST /debug/adminsuite/sandbox/seed delete-rows-safely
POST /debug/adminsuite/sql edit-any-row-with-admin-crud
POST /debug/clock use-the-debug-console
POST /debug/db/reset use-the-debug-console
POST /debug/events/tiers/{tier_id}/force-cascade get-into-a-member-only-drop, understand-your-events-tiers-and-releases
POST /debug/guestlist/reset-override-cap/{event_id} issue-a-vip-walk-in-override
POST /debug/intake/press-button fast-track-from-telegram
POST /debug/intake/resend-telegram fast-track-from-telegram
POST /debug/ledger/attempt-mutation post-a-house-credit-adjustment
POST /debug/ledger/promote-payouts process-a-payout-run
POST /debug/ledger/run-tab-settlement script-a-mock-failure, run-the-vip-tab-settlement
POST /debug/ledger/simulate-ach process-a-payout-run
POST /debug/marketing/posts/{post_id}/force-publish compose-and-publish-a-social-post
POST /debug/marketing/rules/{rule_id}/force-fire build-a-trigger-rule, configure-marketing-channels-and-rules
POST /debug/marketing/rules/{rule_id}/reset build-a-trigger-rule, configure-marketing-channels-and-rules
POST /debug/marketing/simulate-click trace-a-click-to-revenue, report-marketing-attribution
POST /debug/marketing/simulate-conversion trace-a-click-to-revenue, report-marketing-attribution
POST /debug/marketing/tick build-a-trigger-rule, configure-marketing-channels-and-rules
POST /debug/mock-behaviors script-a-mock-failure
POST /debug/payments/expire-holds hold-a-checkout-reservation
POST /debug/qa/invariants/run run-the-qa-invariant-sweep, process-a-payout-run, reconcile-tax-accruals
POST /debug/rbac/simulate build-a-role-from-capabilities
POST /debug/scheduler/tick use-the-debug-console
POST /debug/tax/replay-telegram-callback approve-a-filing-from-telegram
POST /debug/tax/run-scheduler run-the-quarterly-tax-filing
POST /debug/tax/seed-accruals reconcile-tax-accruals
POST /debug/webhooks/simulate script-a-mock-failure
POST /debug/webhooks/{webhook_id}/replay script-a-mock-failure
POST /preview/enter preview-the-site-as-a-guest
POST /preview/exit preview-the-site-as-a-guest
POST /resale/listings/{listing_id}/delist resell-a-ticket-you-cant-use
POST /resale/sell/{ticket_id} resell-a-ticket-you-cant-use
POST /sign/{token} sign-and-countersign-a-contract, create-and-launch-an-event
POST /webhooks/telegram run-the-staff-channel-on-live-telegram
POST /webhooks/telegram/tax approve-a-filing-from-telegram
PUT /api/admin/credit/users/{user_id}/tab post-a-house-credit-adjustment, run-the-vip-tab-settlement, use-your-membership-card-at-the-bar, use-and-settle-your-vip-tab
PUT /api/admin/events/{event_id}/refund-policy refund-a-ticket-or-order, self-cancel-a-ticket
PUT /api/admin/resale/events/{event_id}/config open-or-close-the-exchange, resell-a-ticket-you-cant-use, get-paid-your-host-royalty
PUT /api/admin/resale/tiers/{tier_id}/mode open-or-close-the-exchange
PUT /api/intake/public/applications/{app_id} respond-to-a-request-for-more-information, build-your-run-of-show-backwards, approve-a-host-application
PUT /api/intake/settings apply-to-host-an-event
PUT /api/rbac/groups/{group_name}/zones map-groups-to-door-zones, build-a-role-from-capabilities
PUT /api/rbac/groups/{slug}/capabilities build-a-role-from-capabilities
PUT /api/rbac/groups/{slug}/implications build-a-role-from-capabilities
PUT /api/rbac/tiers/{slug}/benefits build-a-role-from-capabilities
PUT /api/tax/packages/{package_id}/allocations approve-a-filing-from-telegram, run-the-quarterly-tax-filing
PUT /api/tiers/{tier_id}/zones understand-your-events-tiers-and-releases
PUT /debug/settings/{key} use-the-debug-console

Appendix C — Demo accounts

Practice logins on the demo/test data set only. These render only outside production and only while training.show_demo_accounts is on.

PersonaEmailPasswordNote
Membermember@club.testmember123 A registered customer: buys, holds tickets, house credit and resale listings.
VIP Membervip@club.testvip123 A member with a house tab and Zone B access. Implies everything a member can do.
Host / Promoterhost@club.testhost123 An outside promoter running an event at the venue. Scoped to that one event.
Door Staffdoor@club.testdoor123 Works the door: scans credentials, searches the guest list, checks people in.
Venue Managermanager@club.testmanager123 Runs the floor and the calendar: intake review, live sales, the door, marketing.
Adminadmin@club.testadmin123 Owns money, identity and configuration. Implies every other group.