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:04:07Z · 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.

Hosting & Contracts

Third-party host intake, review, and the dual e-signed venue agreement.

Approve a host application

Review the queue, chase what is missing, and approve — which drafts the contract.

Owned by Venue Manager · 9 steps · about 15 minutes

Why this exists

The intake queue is where the venue decides what its calendar looks like, so the design goal is that a manager can make that decision from one screen without trusting anyone's memory.

Two ideas are doing the work. The first is the viability scorecard: every application is scored on the same axes so that comparing two nights is comparing like with like. The second is that asking for more information is a first-class state, not an email. Requesting info sends the promoter a tokenised edit link and moves the application into a state that says, on the queue, that the ball is in their court. Nothing gets silently stuck in someone's inbox.

Approval is a bigger action than it looks: in the same transaction it drafts the contract, with riders derived from the flags the promoter set and the headcount, price and date snapshotted from what they submitted. Approving is therefore a statement that the numbers on the application are the numbers you are willing to put in a contract.

Before you start

  • A venue manager or admin session — the queue is gated to reviewers.
  • At least one application in the queue (the seed ships a viable one).

Practise with

PersonaEmailPasswordNote
venue_managermanager@club.test manager123the reviewer persona for this workflow
host_promoterhost@club.test host123the applicant; use them to answer a request for more information
adminadmin@club.test admin123picks the drafted contract up from here

Steps 1–3 — Venue Manager

their manual →
  1. 1
    Open the intake queue and filter to what needs a decision.
    /admin/intake intake
    Expected result Applications with status and viability score.
    Watch out for This page is gated to venue manager and admin. Door staff get a 403 — the calendar is not a door decision.
  2. 2
    Open one application and read it properly: the date against your calendar, the headcount against the room, the rider flags, the attachments and the notes.
    /admin/intake/{app_id} intake
    Expected result The full scorecard, status history and the rider summary a contract would be built from.
  3. 3
    If something is missing, request more information rather than declining. Say exactly what you need.
    /api/intake/applications/{app_id}/request-info POST intake
    Expected result The application moves to info_requested and the promoter is emailed a tokenised edit link.
    Watch out for Do not chase this by personal email. The token is what lets them edit without an account, and the status is what keeps the queue honest.

Steps 4–5 — Host / Promoter

their manual →

Answers a request for more information through their tokenised edit link.

  1. 4
    As the promoter, open the edit link you were sent and see your own application.
    /apply/edit/{token} intake
    Expected result Your application, editable, with no login required.
    Watch out for The token is the credential and it is scoped to this application. Treat it like a password.
  2. 5
    Answer the question and resubmit.
    /api/intake/public/applications/{app_id} PUT intake
    Expected result The application returns to the queue with your update recorded in its history.

Steps 6–7 — Venue Manager

their manual →
  1. 6
    Re-open the application and check the updated answer and the recomputed score.
    /admin/intake/{app_id} intake
    Expected result The new values, with the change visible in the status history.
  2. 7
    Approve.
    /api/intake/applications/{app_id}/approve POST intake
    Expected result Status approved, and a draft contract created in the same transaction with the riders the flags implied.
    Watch out for You can approve, but you cannot lock or counter-sign the contract that approval just drafted. That is an admin's job, and it is the deliberate split between running the calendar and binding the venue.

Steps 8–9 — Admin

their manual →

Takes the contract that approval drafted and turns it into a signed agreement.

  1. 8
    As an admin, pick the new draft up from the contracts list.
    /admin/contracts contracts
    Expected result The drafted contract against that application.
  2. 9
    Open it and continue into the contract workflow: fill the variables, lock, send for signature.
    /admin/contracts/{contract_id} contracts
    Expected result The editable draft with its auto-injected riders.

Author a rider so a host can ask for it

Turn a host's 'we do something you don't list' into an approved clause — and a checkbox that did not exist before.

Owned by Venue Manager · 22 steps · about 22 minutes

Why this exists

The application form is now governed by the contract library. Every feature a host can tick — live music, alcohol, late night, certified security staffing, aerial performance, a resale royalty — is a checkbox only because an approved rider stands behind it. Untick that relationship and the box does not exist. There is no list of features maintained anywhere else.

The reason is a single sentence: the form may only say things the venue has already agreed, in writing, to say. A checkbox is a promise. If a host can tick something the venue has no clause for, the venue has agreed to something nobody drafted, and it finds out at signature time.

So what happens when a host needs something new?

They write free text. That is the whole point of the free-text box, and it is deliberately a message, not a request for a checkbox. Free text can never become contract language by itself, however carefully it is worded, because the person who writes the clause must be the venue. What the host's words do is start a conversation and land a row in a queue.

A manager reads the message and authors a rider from it. The host's text sits beside the editor, read-only, and is never pre-filled into the body — if it were, the host would have authored contract language after all, just through a longer pipeline. The note is context. The manager writes the clause.

Two scopes, and why one-off is the default

A rider is either library — part of the standing catalogue every host sees — or one-off: written for one application, at one venue, and shown to nobody else. Answering a host in a hurry should never quietly extend what the whole world can ask for, so the answer-a-host path creates a one-off. A one-off that turns out to be generally useful can be promoted to the library later, as a separate, deliberate decision.

Nothing is selectable until a version is approved

Creating a rider creates its catalogue row and its first draft version. It is not selectable. The text goes draft → in review → approved, and only then can the catalogue row be made selectable — attempting it earlier is refused because there is no approved version to select.

Self-approval succeeds by default on this install. That is a deliberate trade-off, not an oversight: a two-person rule on a small team means riders never ship. The preventive control was replaced by three detective ones — a self-approved flag stored on the version and rendered as a chip wherever riders are listed, an append-only trail row pinning the exact bytes that were approved, and a notice to the chat the team already watches. One setting turns the two-person rule back on.

When a version is approved, every request linked to it flips to approved and the host who asked is emailed a working link back into their application. Without that last step the host never learns the thing they asked for now exists, and the whole loop is theatre.

Before you start

  • A venue_manager or admin session. Rider authoring and approval are staff surfaces.
  • An application carrying a host's free-text note, or an open rider request. The debug seed can make some.
  • The seeded catalogue: live music, alcohol, late night, certified security staffing, aerial performance, and a resale royalty with a basis-points parameter.

Practise with

PersonaEmailPasswordNote
venue_managermanager@club.test manager123authors riders, approves versions and triages the request queue
host_promoterhost@club.test host123owns the seeded applications; the person on the other end of the request

Steps 1–6 — Host / Promoter

their manual →

Ticks what the library already covers, and writes the free text that starts a new rider.

  1. 1
    Open the application form and find the features section. Each checkbox has a label and a line of help text — read the help, because it is what the clause commits you to, written for a host rather than for a lawyer.
    /host/apply intake
    Expected result Grouped checkboxes: Production & licensing, Commercial terms.
    Watch out for Ticking a box is not a preference. It attaches a clause to the contract you will be asked to e-sign. A careless tick is a wrong clause in a binding document.
  2. 2
    Understand where those boxes come from: the form draws exactly what this endpoint returns for your application and its venue.
    Expected result Groups of riders with their host-facing labels, help text, parameters and relationships.
    Watch out for Called without an application id it returns library riders only. That narrowness is on purpose — nobody can discover another host's one-off clause by probing this endpoint.
  3. 3
    Tick Aerial performance and watch Certified security staffing tick itself, with a line explaining which choice pulled it in.
    /host/apply contracts
    Expected result The implied box selected and annotated, not silently checked.
    Watch out for Some riders imply others and some exclude each other. Implication is enforced on the server too — the client is a convenience, and a payload that ticks a rider you may not select is refused as not selectable.
  4. 4
    A rider that needs a number asks for one. The resale royalty carries a basis-points parameter with a range, and it is required if you tick the box.
    /host/apply contracts
    Expected result A parameter row appearing under the checkbox you ticked.
    Watch out for Basis points, not percent: 250 is 2.5%. The bound is checked server-side, so a number outside the range comes back as a validation failure naming that rider.
  5. 5
    For something the list does not cover, open My event needs something that isn't listed and answer three questions: what is it called, what happens at your event that is not covered, and what would the venue need to agree to. Optionally name the closest existing rider.
    Expected result A 201 and a card in the venue's staff channel with your request on it.
    Watch out for This is a message, not a checkbox request that gets granted. Nobody converts your words into contract language — a manager reads them and writes the clause. Requests are rate limited per email per day.
  6. 6
    Submit the application with whatever you could legitimately tick. Your request rides along with it.
    Expected result A 201, and the application flagged as having an open rider request.
    Watch out for Do not wait for the rider before applying. The application is the thing that gets a date held; the clause can arrive afterwards and you will be emailed a link to add it.

Steps 7–17 — Venue Manager

their manual →
  1. 7
    Open the rider-request queue. Each row carries the host, the application it came from, what they say happens, what they think the venue would need to agree to, and how long it has been sitting there.
    Expected result Requests in new, triaged, authoring, approved or declined.
    Watch out for The age column is measured against a service-level setting, defaulting to 72 hours. A request that ages out is a host who has already booked somewhere else.
  2. 8
    Claim a request. Triaging marks it as picked up and optionally assigns it to somebody by name.
    /api/intake/rider-requests/{request_id}/triage POST intake
    Expected result Status triaged, with an event appended to the request's own history.
    Watch out for Triage is also available from the staff channel without opening the app. Either way the transition is recorded with its source, so 'who picked this up' always has an answer.
  3. 9
    Open the composer from the request. The host's words render in a read-only panel beside the editor, together with the application, the event name, the date and the venue.
    Expected result A blank clause editor next to the host's context.
    Watch out for The body starts empty and stays empty until you type. That is the control: the host's text is reference material, never a draft. Copying it in by hand defeats the entire design.
  4. 10
    Write the clause for a host's free-text note on the application — the 'anything else we should know' answer. You supply three things: the host label for the checkbox, the help text a host reads before ticking it, and the legal body.
    /api/intake/applications/{app_id}/author-rider POST intake
    Expected result A one-off rider scoped to that application and its venue, plus a note on the application recording that you authored it.
    Watch out for All three fields are required and the refusals say why — help text in particular, because a checkbox with no explanation is how a host agrees to something they did not read. The call is idempotent per application and label, so a double click does not mint two clauses.
  5. 11
    From the queue instead, author against the request itself. This creates the rider (or links an existing one) and moves the request to authoring, joining the two objects — the request page then shows live rider state and the rider page shows who asked for it and why.
    /api/intake/rider-requests/{request_id}/author POST intake
    Expected result Status authoring, with the rider key attached to the request.
    Watch out for If the request came from an application, the rider is created as a one-off scoped to that application and venue. Choose deliberately: a library rider written in a hurry is a clause every future host can tick.
  6. 12
    Open the rider. You get its versions, a word-level diff between them, its append-only trail, and where it is currently in use.
    /admin/contract-riders/{rider_key} contracts
    Expected result Version 1 in draft, not selectable, not on any contract.
    Watch out for Only a draft is editable. Once a version is in review or approved its text is frozen — corrections are a new version, never a quiet rewrite of the bytes somebody already approved.
  7. 13
    Submit the draft for review when the wording is right.
    /api/contracts/rider-versions/{template_id}/submit POST contracts
    Expected result Status in review, and a trail row recording who submitted it.
    Watch out for Submitting freezes the text. If you spot a typo now, reject it and fork a fresh draft rather than looking for an edit button that is deliberately not there.
  8. 14
    Approve the version. This retires whatever version was previously live, makes this the active text, and closes every request linked to the rider — emailing each requester a link back into their application.
    /api/contracts/rider-versions/{template_id}/approve POST contracts
    Expected result Status approved, is-active set, linked requests flipped to approved.
    Watch out for Approving your own draft succeeds on this install and is permanently marked as self-approved: a chip in every listing, a reason on the trail row, and a notice in the staff channel. If your install turns the two-person rule on instead, the same call is refused and says so.
  9. 15
    Reject a version whose wording is wrong, with a reason. The reason is required.
    /api/contracts/rider-versions/{template_id}/reject POST contracts
    Expected result Status rejected, the reason on the trail.
    Watch out for Rejecting is not deleting. The rejected text stays readable in the version history, which is what makes 'why did we not go with that wording' answerable a year later.
  10. 16
    Make the rider selectable, and set how it presents: group, label, help text, sort order.
    /api/contracts/riders/{rider_key} PATCH contracts
    Expected result The rider appears in the form for whoever its scope covers.
    Watch out for Selectable without an approved version is refused. That ordering is the product decision this whole workflow is built on — the checkbox cannot exist before the clause does.
  11. 17
    Decline a request the venue will never say yes to — boxing, pyrotechnics, whatever your licence does not cover — with a reason. The host is emailed.
    /api/intake/rider-requests/{request_id}/decline POST intake
    Expected result Status declined and an email carrying your reason.
    Watch out for Declining is a legitimate answer and it is deliberately as easy as approving. A queue where 'no' is harder than 'yes' becomes a queue where requests rot instead.

Steps 18 — Host / Promoter

their manual →

Ticks what the library already covers, and writes the free text that starts a new rider.

  1. 18
    Open the link from the 'your request is now available' email. Your application reopens with the new checkbox present.
    /apply/edit/{token} intake
    Expected result The clause you asked for, now tickable, with the venue's help text.
    Watch out for Read the help text before you tick it. The venue wrote the clause, not you — what it commits you to may be narrower, or broader, than what you described.

Steps 19–22 — Venue Manager

their manual →
  1. 19
    When the same one-off has been written three times, promote it to the library so every host can tick it.
    /api/contracts/riders/{rider_key}/promote POST contracts
    Expected result The rider leaves the written-for-you group and joins the standing catalogue.
    Watch out for Promotion is the deliberate widening this design keeps separate from answering one host. Do it on purpose, not as a shortcut while you are already in a hurry.
  2. 20
    Read the library periodically: live riders, retired ones, and which are one-offs. Filter by scope.
    Expected result The catalogue with self-approved and one-off badges visible in the list.
    Watch out for Those badges are the control. If a reviewer has to open each rider to find out whether anybody else read it, the control does not exist — which is exactly why they are rendered wherever riders are listed.
  3. 21
    Check recent rider activity — approvals inside the review window, with the self-approved ones surfaced.
    Expected result A short list you can actually read at a Monday meeting.
    Watch out for Watch the self-approved rate rather than individual rows. A single self-approval is a small team working; every rider self-approved means the second pair of eyes has quietly stopped existing.
  4. 22
    Back on the application, read the accepted riders block: what the host actually ticked, with one-off and self-approved flagged inline.
    /admin/intake/{app_id} intake
    Expected result The riders that will be injected into this booking's contract.
    Watch out for This is the last screen before approval where a wrong clause is cheap to remove. After approval the contract is drafted and only an admin can change it.

Apply to host an event

Pitch your night through the public application form — no account, no login.

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

Why this exists

The application form is the front door of the entire platform, and it is deliberately public. You do not register first, you do not get an account first, and no member of staff types your event into the system on your behalf. There is no "new event" form anywhere that a manager can fill in for you.

That is an accountability decision, not a convenience one. Every event on this platform traces back to one application, submitted by one named person, carrying the numbers they committed to: the date, the headcount range, the tier prices, the bar minimum. Staff score it, approve it, price it and publish it — but the numbers on the paperwork are yours, and the system keeps a snapshot of what you said.

Two parts of the form do more than they look like they do. The four flags in section 3 (live music, alcohol, late night, resale royalty) are not survey questions: each one automatically injects a rider clause into the contract you will later be asked to e-sign, so a careless tick becomes a wrong clause in a binding document. And the tier estimates in section 4 are the seed of your real ticket tiers — when the application is converted, those rows become the event's tiers, in order, with the first one opening on sale and later ones cascading behind it.

The viability score you see updating as you type is a scorecard, not a verdict. It weights your headcount against the room and your projected revenue against a floor, and it never blocks anything — a human still decides.

Before you start

  • Nothing. The form needs no account and no login.
  • A real proposed date at least intake.min_lead_days out (seeded: 7 days).
  • Your slot times in UTC: setup start, doors, main start, main end, teardown end.

Practise with

PersonaEmailPasswordNote
host_promoterhost@club.test host123the seeded promoter; already has three demo applications on file
venue_managermanager@club.test manager123reads the queue your submission lands in
adminadmin@club.test admin123owns the intake thresholds and can read the outbound mail log

Steps 1–7 — Host / Promoter

their manual →
  1. 1
    Open the public application form. Log out first if you want to see what a brand-new promoter sees.
    /host/apply intake
    Expected result Five sections: organizer profile, event concept, equipment and production, financial estimates, attachments. No login wall.
    Watch out for /apply redirects here, so both links are legitimate. If you are already logged in and have applied before, the form quietly opens a pre-verified autofill session and paints your old answers red — that is a separate workflow, and the red is not decoration.
  2. 2
    Fill sections 1 and 2: your name, organization, email, phone, socials and track record, then the event name, description, genre, type, proposed date, an optional alternate date, and the five slot times.
    Expected result The form accepts free text as you type; nothing is validated until you submit.
    Watch out for Your email is your identity on this platform, matched case-insensitively. Autofill next time, the approval email, the contract signing link and — if you are approved — your login all key off it. Slot times are UTC strings like 2026-01-01T20:00:00Z, and the proposed date must be at least intake.min_lead_days (seeded 7) days out or you get a per-field validation error.
  3. 3
    Work section 3 carefully: equipment needs, then the four flags — live music, alcohol served, late night, and request resale royalty. If you tick the royalty flag, set the basis points (1 to 3000).
    Expected result The royalty field only appears once the royalty flag is ticked. A hint appears if your main set ends between midnight and 6am local time.
    Watch out for Each flag injects a rider into the contract. The late-night rider has a fallback: even if you leave the flag unticked, the contract engine derives it when your stored main end time is before 06:00. Do not assume an unticked box keeps a clause out.
  4. 4
    Fill section 4: headcount minimum and maximum, the bar minimum in dollars, and between one and six ticket tiers with a name, a price and a quantity each.
    Expected result The summary bar at the bottom updates live: gross door projection, average ticket price, headcount against capacity, and a viability preview out of 100 with a band (viable / marginal / not viable).
    Watch out for Your headcount maximum is scored against intake.venue_capacity (seeded 450). Ask for more than the room holds and that half of the score goes to zero. These tier rows are not a wish list — they become the event's real tiers on conversion, so name and price them as you actually mean to sell them.
  5. 5
    Attach anything that helps: pitch deck, insurance certificate, rider. Pick the kind next to each file.
    Expected result Up to intake.max_files attachments (seeded 10), each up to intake.max_file_mb (seeded 10) MB, in pdf, png, jpg, gif, office or txt format.
    Watch out for The browser warns you about size and type, but the server is the authority: too many files is a 422, a bad extension is a 415, an oversized file is a 413. Nothing is stored until the whole submission validates.
  6. 6
    Press Submit application.
    Expected result 201 with your application id, status new_submission and your viability score. You are redirected to the thank-you page, and a confirmation email plus a Telegram summary card for the venue are recorded.
    Watch out for Three refusals bite here. 422 validation_failed comes back with a per-field message list and the form prints them under the offending fields. 409 duplicate_application means the same email, event name and date were submitted in the last 24 hours. 429 rate_limited means more than 3 submissions from your email or 10 from your IP in 24 hours. An Idempotency-Key header replays the original 201 for 24 hours instead of creating a second application.
  7. 7
    Land on the thank-you page and write down the reference shown there.
    /apply/thanks/{app_id} intake
    Expected result A short reference (the first 8 characters of your application id) and a plain-English note about what happens next.
    Watch out for Built behaviour worth knowing: this page is a receipt, not a portal. It shows no live status and no edit link — the plan imagined a status window here and that is not what was built. Until staff email you, the reference is all you have; quote it if you need to chase.

Steps 8–9 — Venue Manager

their manual →

Yours is the queue they work from — they are the first human to read it.

  1. 8
    As the reviewer, open the intake pipeline board and find the new submission.
    /admin/intake intake
    Expected result A six-column board with viability scores, plus q and band filters.
    Watch out for Gated to venue manager and admin. Door staff get a 403 — the calendar is not a door decision.
  2. 9
    Open the application and read the whole scorecard: the date against the calendar, the headcount against the room, the rider flags, the tier table, the attachments and the timeline.
    /admin/intake/{app_id} intake
    Expected result The full application with its status history, notes, rider summary and an action bar.
    Watch out for The score is a scorecard, not a decision. It knows the numbers; it does not know that this promoter's last night was a disaster.

Steps 10–11 — Admin

their manual →

Owns the capacity and revenue thresholds your form is validated and scored against.

  1. 10
    As an admin, confirm the submission actually generated its notifications: the intake_received email to the promoter and the Telegram summary card with its inline accept and decline buttons.
    Expected result Recorded outbound calls correlated to the application id.
    Watch out for Nothing here is a real email or a real Telegram message — every external service runs behind a mock adapter that records the call. That is why you can rehearse this whole workflow offline.
  2. 11
    Tune the thresholds the form validates and scores against: venue capacity, minimum revenue floor, minimum lead days, file limits.
    Expected result The updated settings; keys are accepted with or without the intake. prefix.
    Watch out for Admin only. A venue manager who runs the queue every day still gets a 403 here — changing the scoring rules is a different authority from working the pipeline.

Fast-track an application from the Telegram alert

Act on the summary card in chat, or open the deep link that claims the review for you.

Owned by Venue Manager · 12 steps · about 14 minutes

Why this exists

When a host application lands, the platform posts a summary card to the venue's Telegram chat with the headline facts and the viability score, plus inline buttons. The idea is that a decision which is obviously yes or obviously no should not require anybody to be at a laptop — a promoter waiting three days for a reply is a promoter taking their night somewhere else.

The card carries three tokenised actions and it is worth understanding what makes them safe. The accept and decline buttons are single-use tokens with a seventy-two hour life, consumed with a rowcount-guarded update so a double tap cannot double-act. The review deep link is multi-use and opens the application in the browser — and, if the application is still a new submission, it auto-claims it into under review, recording the source as telegram. That claim is the point: the queue always knows whether a human has picked something up, without anyone having to remember to press Start Review.

Fast-tracking from chat is exactly as powerful as approving from the dashboard, which means the same consequence follows: approval, in the same transaction, drafts the venue contract with riders derived from the flags the promoter set. Tapping accept in a group chat is signing up to the numbers on that application. Use the card when the answer is genuinely obvious and the deep link when it is not.

One safeguard to know: any transition to approved or rejected invalidates every unused token on that application — the accept and decline buttons, and any host edit link. A card sitting in chat from yesterday cannot re-decide something that has already been decided.

Before you start

  • A venue manager or admin session for the browser half.
  • An application in new_submission. The seed ships Warehouse Frequencies, scored 88.
  • Telegram is a mock here — outbound cards are recorded rather than sent.

Practise with

PersonaEmailPasswordNote
venue_managermanager@club.test manager123shift lead: implies door_staff, plus intake, sales, guest list, marketing
host_promoterhost@club.test host123owns the two seeded applications, Warehouse Frequencies and Analog Sunrise
adminadmin@club.test admin123the only account that can touch money, identity and contracts

Steps 1–8 — Venue Manager

their manual →
  1. 1
    Read the card in the chat before touching a button. It carries the event name, the proposed date, the headcount and the viability score and band — enough to tell an obvious yes from an obvious no.
    Expected result A summary card with inline accept, decline and review buttons.
    Watch out for The score is advice, never a gate. A 40-point application on a dead Tuesday can be a better decision than an 80 on your busiest Saturday — the scorecard does not know your calendar.
  2. 2
    For anything less than obvious, tap the review link. It opens the application detail page and, if the application was still a new submission, silently claims it into under review with the source recorded as telegram.
    /admin/intake/{app_id} intake
    Expected result The full application with the status chip now reading under review.
    Watch out for That auto-claim is real work being recorded against you. Opening the link out of curiosity marks the application as picked up — if you are not going to review it, do not tap it.
  3. 3
    Read the scorecard panel properly: the score out of a hundred, its band, and the split between capacity points and revenue points, each out of fifty. Then read the flags — over capacity, low utilisation, below revenue floor, tier quantity mismatch.
    /admin/intake/{app_id} intake
    Expected result Two point bars, a band of viable, marginal or not viable, and any flags as red chips.
    Watch out for Bands are fixed: seventy and above is viable, forty to sixty-nine marginal, below forty not viable. The inputs line shows the venue capacity and revenue floor used — those are settings, and only an admin can change them.
  4. 4
    If the promoter has updated their numbers since submission, press Recompute so you are scoring the current figures.
    /api/intake/applications/{app_id}/recompute-viability POST intake
    Expected result A fresh score and a new computed-at timestamp.
    Watch out for The score never blocks a transition. Recomputing is for your judgement, not for the machine's permission.
  5. 5
    To decline, press Decline and give a reason. The reason is required and it goes to the promoter in the decline email.
    /api/intake/applications/{app_id}/decline POST intake
    Expected result Status rejected, the promoter emailed, the chat card edited, and every unused token invalidated.
    Watch out for A missing reason is a 422 decline_reason_required. Write something a stranger could act on — 'date already held' or 'headcount exceeds capacity' — because they will read it.
  6. 6
    To approve, press Approve. In the same transaction this drafts the venue contract with riders derived from the promoter's flags.
    /api/intake/applications/{app_id}/approve POST intake
    Expected result Status approved and a draft contract waiting in the contracts list.
    Watch out for You approve; you cannot lock or countersign what approval drafted. That split is deliberate — running the calendar and binding the venue are different jobs.
  7. 7
    Leave a note whenever the decision needed context — a phone call, a past night, a condition you agreed verbally.
    /api/intake/applications/{app_id}/notes POST intake
    Expected result The note in the activity timeline against your name.
    Watch out for Notes are the only place the reasoning survives. The status history records what happened, never why.
  8. 8
    Know the two things you will be refused. You cannot reopen a rejected application, and you cannot reject one that has already been approved or moved to contract deposit. Both are admin-only and both come back as a 403.
    Expected result A clean escalation rather than a confusing error.
    Watch out for These are late-stage reversals with contracts and possibly deposits behind them. The 403 is protecting a decision somebody else has already relied on.

Steps 9 — Host / Promoter

their manual →

Is the applicant on the other end of whichever button gets tapped.

  1. 9
    As the promoter, if you were asked for more information instead, open the edit link you were emailed and answer it.
    /apply/edit/{token} intake
    Expected result Your application, editable, with no login.
    Watch out for That link dies the moment the application is approved or rejected. If it shows a friendly expired page, the decision has already been made.

Steps 10–12 — Admin

their manual →

Owns the debug tools that replay a card button and re-send a card, and the late-stage transitions a manager is refused.

  1. 10
    As an admin, replay a recorded card button when you are testing the fast-track path or a manager reports a button that did nothing.
    Expected result The accept or decline runs through the real webhook path, exactly as the tap would have.
    Watch out for The plaintext tokens exist only inside the recorded outbound payloads — that is what makes replay possible, and why the token table stores hashes only.
  2. 11
    Re-send a summary card when the original was lost or the chat was wrong.
    Expected result A fresh card with fresh tokens.
    Watch out for Re-sending mints new tokens. The old card's buttons are now the stale ones — tell the chat, or somebody taps yesterday's card.
  3. 12
    When somebody swears no card arrived, check the recorded outbound traffic. Telegram and email are both mocks here and every call is recorded.
    Expected result The outbound call rows correlated to the application id.
    Watch out for A recorded call proves the platform tried. Whether a message actually lands depends on the install: the Telegram adapter defaults to a mock that records without sending, but this deployment runs it LIVE against a real staff channel. Check /admin/telegram for the mode before deciding a delivery failure is impossible — see the 'run-the-staff-channel-on-live-telegram' workflow.

Build your run of show backwards from when the night ends

Answer when it ends and how long each part takes; the platform derives setup, doors, showtime and teardown for you — and stores them in UTC.

Owned by Host / Promoter · 17 steps · about 16 minutes

Why this exists

The application form used to ask for five timestamps: setup start, doors, main start, main end, teardown end. Hosts got them wrong constantly, and always in the same way — not because they did not know their own night, but because nobody plans a show forwards. You know the curfew. You know how long your headliner plays. Everything else is arithmetic somebody was being asked to do in their head, at midnight, on a phone.

So the builder asks the questions you can actually answer — when does it end, how long is the headliner, how long are the openers, how long is clean-up, how long is setup, how early are doors — and walks the schedule backwards from the end. Five timestamps still get stored. You just stop being the one computing them.

The midnight rule — read this twice

An event day is not a calendar day. The platform defines the day for a date as local 6am to 6am, so an end time before six in the morning belongs to the next calendar date. Saying "Saturday the 14th, ends at midnight" therefore means Sunday the 15th at 00:00 — which is what you meant — and the backwards walk lands setup on Saturday evening.

Concretely, with a two-hour headliner, 45 minutes of openers, doors 30 minutes early, two hours of setup and an hour of clean-up:

  • Setup starts Sat 14 Mar, 6:45 PM
  • Doors Sat 14 Mar, 8:45 PM
  • First act Sat 14 Mar, 9:15 PM
  • Music ends Sun 15 Mar, 12:00 AM
  • Teardown ends Sun 15 Mar, 1:00 AM

One night, two calendar dates, and that is correct. The preview labels every row with its own day for exactly this reason, and the card carries an ends after midnight badge so nobody has to notice it on their own.

Local in, UTC out

Every time you see is the venue's wall clock — not your phone's. A host applying from New York for a Los Angeles room types Los Angeles times, and the browser is never asked what time it thinks it is. What gets stored is UTC. In the worked example above, all five stored instants fall on 2026-03-15 in UTC, including the setup that happens on Saturday evening locally. That is not a bug and you will see it on staff screens: the stored date and the local date are allowed to disagree.

The derivation runs on the server, once. The page has no timezone logic of its own — it collects your answers, posts them to a preview endpoint, and draws whatever comes back. A second implementation in the browser would drift from the first, and it would drift precisely on daylight-saving weekends and midnight crossings, which is where being wrong actually costs something.

Pinning: the escape hatch, not the default

Every derived row has an edit button. Use it and that row is pinned — it stops being computed, keeps the exact time you typed, and is marked with a pin in the run of show. The other rows keep deriving around it. A pin is reversible from the same button, which then reads reset.

Pins exist because reality has load-ins that start at 2pm for a 10pm show, and no duration answer expresses that. They are not the way to use this feature. A pinned row that puts the schedule out of order is refused with a message on that row, and the offer to undo your pins rides on the error itself.

Before you start

  • No account. The application form is public.
  • A date for your event, and honest answers about how long each part takes.
  • The venue's timezone is shown on the form. Everything you type is in that clock.

Practise with

PersonaEmailPasswordNote
host_promoterhost@club.test host123owns the seeded applications, including one entered through the guided builder
venue_managermanager@club.test manager123reads the stored run of show on the application review page
adminadmin@club.test admin123owns the derivation trace and the timezone health check

Steps 1–12 — Host / Promoter

their manual →
  1. 1
    Open the application form and scroll to the run-of-show section. Read its one-line lede: it names the timezone every answer on this form is in.
    /host/apply intake
    Expected result Six questions and an empty run-of-show card that says it will fill in once you answer.
    Watch out for That timezone is the venue's, not your device's. If you are applying from another city, do not mentally convert — type the times the night actually happens at.
  2. 2
    Set the event date first. The whole derivation hangs off it, and until it is set the preview stays empty however many other answers you give.
    /host/apply intake
    Expected result The date field accepted; the card still empty.
    Watch out for This is the date the night starts in the ordinary sense — the Saturday, not the Sunday you go home on.
  3. 3
    Answer Event ends at. There are three presets — Midnight, 10pm, 8pm — plus Other… for an exact time. This is the anchor: every other row is computed backwards or forwards from it.
    /host/apply intake
    Expected result The question collapses to a summary chip showing your answer.
    Watch out for Choosing Midnight on a Saturday means Sunday 00:00. You are not saying the night ends 24 hours ago — the day-rollover rule handles it, and the preview will show you the Sunday label on that row.
  4. 4
    Answer the four duration questions: Headliner set length and Clean-up time are required; Opener time (all openers together) and Doors before first act are optional. Setup time is required too.
    /host/apply intake
    Expected result Each answered question collapses; the next unanswered one is highlighted.
    Watch out for Openers is the total for all of them, not each. And setup is measured back from doors, not from showtime — two hours of setup with doors 30 minutes early puts your load-in two and a half hours before the first act.
  5. 5
    Watch the run-of-show card fill in as you answer. It posts your answers to the public preview endpoint and renders the five rows it gets back, each with its local time, its day label and its UTC instant.
    Expected result Five rows, the gaps between them, a total duration, and any badges.
    Watch out for The preview writes nothing and needs no application — it is a pure calculator, and it is rate limited per IP. If it is unavailable the form still submits: the server re-derives from the same answers, so a missing preview costs you the picture, never the schedule.
  6. 6
    Read the badges under the card. Ends after midnight tells you the night crosses a date. Setup before the event date tells you your load-in starts on the previous day. A clocks-change weekend gets its own badge, and each row starts showing its timezone abbreviation.
    Expected result Badges that describe your schedule in words, not just colours.
    Watch out for A midnight-or-later finish also raises a late-night suggestion. That is advice about a contract clause, not a validation error — nothing here blocks you.
  7. 7
    If the clocks change during your night, the form asks one extra question: that wall-clock time happens twice, which one did you mean? Pick the earlier or the later.
    Expected result The fold question appears only when it is genuinely ambiguous.
    Watch out for A time that does not exist at all — the hour that is skipped when clocks go forward — is refused with a suggestion of the next valid time. There is no silently-rounded answer here, because 2:30am on that Sunday is not a moment.
  8. 8
    Pin a row you cannot express as a duration. Press edit on it, type the exact local time, and save. The row gains a pin marker and its button becomes reset.
    /host/apply intake
    Expected result That row keeps your time; the others keep deriving around it.
    Watch out for A pin that puts the schedule out of order — teardown before the music ends — is refused on that row, and the last good run of show stays on screen dimmed rather than vanishing. The message carries an undo for all your pins, which is the fastest way out.
  9. 9
    Finish the rest of the form and submit. The server re-derives your schedule from the same answers and stores five UTC instants.
    Expected result A 201 and the thank-you page.
    Watch out for Two submit modes exist and they are chosen by what you send. Answering the guided questions uses the derivation; posting five explicit timestamps uses the old direct path. Sending both is a 422 telling you they conflict — it will not guess which one you meant.
  10. 10
    Read the run of show back on the thank-you page. This is the stored schedule, projected into the venue's clock — the same rows staff will see.
    /apply/thanks/{app_id} intake
    Expected result Your five moments, with their day labels.
    Watch out for Check the day labels one last time here. A schedule that reads 'ends Sunday' when you meant Saturday afternoon is trivial to fix now and awkward to fix after a manager has already read it.
  11. 11
    If staff ask for more information, the emailed edit link reopens the form with your schedule loaded back into the guided questions.
    /apply/edit/{token} intake
    Expected result The builder, pre-answered, with your existing times reproduced exactly.
    Watch out for One thing does not survive the round trip: the split between headliner and openers. The reverse mapping resolves it as the whole programme being the headliner with no openers, because that reproduces your five stored instants byte for byte. Re-split it if you care about the distinction.
  12. 12
    Save the edit. The schedule is re-derived and re-stored from whatever the questions now say.
    /api/intake/public/applications/{app_id} PUT intake
    Expected result The application returns to under review with an updated card in the staff channel.
    Watch out for Timeline answers never prefill from a previous application, and they never count toward the red confirm gate on returning-host autofill. Every night gets its own schedule, deliberately.

Steps 13–14 — Venue Manager

their manual →

Reviews the stored schedule on the application, in the venue's clock, before approving it.

  1. 13
    Open the application and read the run of show in the timeline panel. It is rendered in the venue's timezone, with the same day labels the host saw.
    /admin/intake/{app_id} intake
    Expected result Five rows plus the badges, matching the host's screen.
    Watch out for The date on the application and the date on the stored setup time are allowed to differ by a day, in both directions. Before you query it with a host, check whether you are reading a UTC instant next to a local clock — that difference is the feature working, not a typo.
  2. 14
    Scan the board for midnight-crossing nights. The seed deliberately ships one — Midnight Cassette — entered through the guided builder with a midnight end, so the board always has a crossing row to look at.
    /admin/intake intake
    Expected result An application whose schedule spans two dates.
    Watch out for Late-night nights carry contract consequences. Noticing the crossing at review time is what lets you check the right rider is on the paperwork.

Steps 15–17 — Admin

their manual →

Owns the derivation trace and the timezone health check when a schedule looks wrong.

  1. 15
    When a schedule looks impossible, replay the derivation with the same inputs. The trace prints which rollover branch was taken, how the local time was resolved, and every step in both projections.
    Expected result A full derivation trace, answering 'why is setup on the 13th'.
    Watch out for There is also a built-in fixture table of known cases. If it reports any failures, the model itself is broken, not the application in front of you — that is a much bigger problem and a much shorter conversation.
  2. 16
    Render exactly what the UI would show for a stored application, and check the stored instants round-trip through the reverse mapping.
    /debug/intake/timeline-render/{app_id} intake
    Expected result The projected rows plus a round-trip verdict.
    Watch out for If the round trip fails, do not edit the application to 'fix' it — the stored instants are the truth and the inference is the derived view. Editing would overwrite good data with a bad guess.
  3. 17
    If every schedule in the platform is suddenly in UTC, check whether a timezone database is present in this container.
    Expected result A yes/no answer and which resolver answered it.
    Watch out for This is a real deployment failure mode on slim base images: with no timezone data, local time silently becomes UTC and nothing raises. This endpoint exists so the silence is diagnosable in one click.

Re-apply with autofill (and the red confirm gate)

Load last time's answers, then confirm or change every red field before the form will submit.

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

Why this exists

Returning promoters re-apply constantly, and the failure mode is always the same: they copy last year's application, change the date, and leave a stale price or a stale headcount in place. The venue then approves numbers nobody meant, and those numbers get snapshotted into a contract and signed.

So autofill on this platform is deliberately uncomfortable. It is opt-in (you press "I've applied before"), it is verified by a six-digit code emailed to the address you claim, and every value it loads arrives marked, with a From last time chip beside it. The mark does not mean wrong. It means nobody has looked at this yet. You clear it one of two ways: change the value, or switch its Confirmed toggle on. Until every marked field is cleared the submit button will not submit — it reads "Review N highlighted fields".

The part that matters most is invisible in the browser: the gate is enforced on the server. When the autofill session is verified, the exact set of prefilled values is frozen onto the session row, and that frozen snapshot is the source of truth. On submit the server re-canonicalises what you actually sent and compares it, field by field, with the frozen value. Skipping the UI and posting the form directly earns a 422 listing the fields you did not review.

Be precise about which half the server decides, because a reviewer reads the scorecard on the strength of it. Edited is derived: the value you sent differs from the frozen one, and no claim from the browser can create or erase that. Confirmed is a claim — you sent the same value and said you had looked at it — and the server takes it at face value, because there is no other evidence that a person read a field. A client that auto-confirms everything therefore passes the gate having changed nothing. What stops that being invisible is that the two are recorded differently: the audit table says confirmed, not edited, and the reviewer sees which answers were re-affirmed rather than re-typed. So read a screen of confirmations as what it is — an applicant saying "still right" — and not as the platform having checked anything.

Every one of those decisions is written to an audit table, so the reviewer can see on the scorecard which answers you actively re-affirmed and which you changed. That audit trail is the point of the whole feature.

Before you start

  • An earlier application under the same email address (the seed ships three for host@club.test).
  • Access to that mailbox — or, in a demo environment, the recorded outbound mail log.

Practise with

PersonaEmailPasswordNote
host_promoterhost@club.test host123has 'Warehouse Frequencies', 'Analog Sunrise' and 'Neon Circuit Gala' on file to autofill from
venue_managermanager@club.test manager123sees the prefill audit panel on the scorecard
adminadmin@club.test admin123can read the emailed code out of the outbound call log and clear rate limits

Steps 1–6 — Host / Promoter

their manual →
  1. 1
    Open the form, type the email address you applied with last time, and press the button beside it: I've applied before.
    /host/apply intake
    Expected result A code box appears under the email field.
    Watch out for If you were already logged in when you opened the page, autofill has already happened without any code — a logged-in visit creates a pre-verified session automatically. Skip to the red fields.
  2. 2
    Request the code.
    Expected result prefill_available true plus a session id, and a six-digit code emailed to that address.
    Watch out for prefill_available comes back false when no earlier application exists for that email — the platform will not tell a stranger whose address is on file. Limits are 5 per email per hour and 20 per IP per hour, then 429.
  3. 3
    Enter the six digits and press Unlock autofill.
    Expected result Your previous answers drop into the form, a warning banner appears, and every prefilled field is marked, with a 'From last time' chip and a Confirmed toggle beside it.
    Watch out for The code lives 15 minutes and you get 5 attempts. A wrong code is 401 bad_code; an expired or exhausted session is 410 prefill_expired and you start over. Only meaningful values prefill — blanks and unticked flags are dropped rather than carried forward.
  4. 4
    Work every marked field. Change the ones that changed — the date, almost certainly the prices. Switch Confirmed on for the ones that are genuinely still true.
    Expected result The submit button counts down: 'Review 12 highlighted fields', then 11, then 10, and finally 'Submit application' when the count reaches zero.
    Watch out for Three traps. First, editing a field back to exactly the suggested value marks it again — the gate compares values, not keystrokes. Second, the whole tier table is a single field: confirm it with the one 'Tier table confirmed' toggle, not row by row. Third, the bar minimum is confirmed as cents even though you type dollars.
  5. 5
    Submit. The form sends the prefill session id and your confirmations along with the answers.
    Expected result 201 as usual, plus an audit row per prefilled field recording whether it was confirmed or edited, with the old and the new value.
    Watch out for 422 unconfirmed_prefill_fields comes back with error.fields listing the field names you skipped — that is the server gate, and no browser trick avoids it. 410 prefill_session_invalid means the session was already used, was never verified, or is more than 24 hours old: sessions are single-use on purpose.
  6. 6
    Confirm you landed on the thank-you page with a new reference.
    /apply/thanks/{app_id} intake
    Expected result A new application, entirely separate from the one you autofilled from.
    Watch out for Autofill copies values; it does not copy attachments. Re-attach your insurance certificate.

Steps 7 — Venue Manager

their manual →

Reads the prefill audit to see which answers you re-affirmed and which you changed.

  1. 7
    Open the new application and scroll to the prefill audit panel.
    /admin/intake/{app_id} intake
    Expected result A table headed 'Prefill audit (RED-field gate)' with one row per prefilled field: the field, the action (confirmed or edited), the prefilled value and the final value.
    Watch out for A wall of 'confirmed' with an unchanged ticket price on a date twelve months later is exactly the signal this panel exists to give you. Read it before you approve.

Steps 8 — Admin

their manual →

Can recover the emailed code and clear the prefill rate limits when a demo hits them.

  1. 8
    As an admin running a training session, inspect the prefill and submission counters when a room full of trainees starts hitting 429.
    Expected result The fixed-window counters keyed by email and IP.
    Watch out for Clear them with the sibling endpoint /debug/intake/clear-rate-limits. Debug routes are admin-only and the whole tree 404s when debug endpoints are disabled, so this is a training and demo tool, not a production escape hatch.

Respond to a request for more information

Answer the venue's question through your tokenised edit link and put the ball back in their court.

Owned by Host / Promoter · 6 steps · about 10 minutes

Why this exists

When the venue needs something from you, they do not send a personal email and hope. Asking for more information is a status: the application moves to info_requested, the question is stored as a note against the application, and you are emailed a tokenised link that lets you edit your own submission with no account and no password.

Two design ideas sit behind that. The first is that the queue must never lie — as long as your application is in info_requested, the board says out loud that the venue is waiting on you, and nothing is quietly stuck in somebody's inbox. The second is that the token is the credential. It is a 14-day, multi-use link scoped to exactly one application, stored only as a hash. Treat it like a password: whoever holds it can edit your pitch.

When you resubmit, the application returns to under_review with your change recorded in its history, and the venue's Telegram channel gets a fresh "UPDATED APPLICATION" card with new action buttons. The moment a decision is made — approved or rejected — every unused token on the application is invalidated, so bookmarking the edit link buys you nothing afterwards.

Before you start

  • An application of yours sitting in info_requested.
  • The edit link the venue emailed you (or, in a demo, the recorded outbound email).

Practise with

PersonaEmailPasswordNote
host_promoterhost@club.test host123the applicant on all three seeded demo applications
venue_managermanager@club.test manager123asks the question and re-reviews the answer

Steps 1 — Venue Manager

their manual →

Asks the question — and requesting info is a first-class status, not an email thread.

  1. 1
    From the application's action bar, request more information and say exactly what is missing, in 1 to 2000 characters.
    /api/intake/applications/{app_id}/request-info POST intake
    Expected result Status moves to info_requested, the question is stored as a note flagged as an info request, a 14-day host_edit token is minted, and the promoter is emailed a link to it.
    Watch out for Only legal from under_review — anything else is 422 invalid_transition. Claim the application first (opening it from the Telegram review deep link does that for you).

Steps 2–4 — Host / Promoter

their manual →
  1. 2
    Open the link from your email. There is nothing to log into — the token in the URL is your credential.
    /apply/edit/{token} intake
    Expected result Your application, editable, with the venue's question printed at the top.
    Watch out for Four ways this page turns you away, and it tells you which: an invalid link, an application that no longer exists, one that is no longer editable because a decision was made, and an expired link. All of them render a friendly page with a 410 status rather than a stack trace. If it is dead, ask the venue to send a fresh one — do not go hunting for another way in.
  2. 3
    Understand what the page loaded: the same editable payload the API returns for your application, resolved from your token.
    /api/intake/public/applications/{app_id}/edit intake
    Expected result Your current answers, tiers and attachment list.
    Watch out for The guard order is deliberate and worth recognising in an error: a bad token is 401, a wrong status is 409 not_editable, and an expired token is 410. A 409 means your token is fine but a decision already happened.
  3. 4
    Answer the question, fix whatever they asked about, add or remove attachments, and resubmit.
    /api/intake/public/applications/{app_id} PUT intake
    Expected result The application returns to under_review, your change is recorded in its status history, and the venue's channel gets an UPDATED APPLICATION card with fresh action tokens.
    Watch out for Removing an attachment is explicit — you send the ids to remove, you do not just leave the file out. The same validation rules apply as on first submission, so a date that has drifted inside the minimum lead time will now fail.

Steps 5–6 — Venue Manager

their manual →

Asks the question — and requesting info is a first-class status, not an email thread.

  1. 5
    Re-open the application and read the update against the timeline.
    /admin/intake/{app_id} intake
    Expected result The new values, the recomputed viability score, and the whole exchange in the status history.
    Watch out for You can loop: request info again. Each round trip is another status event, which is exactly the record you want three months later.
  2. 6
    Approve once you are satisfied.
    /api/intake/applications/{app_id}/approve POST intake
    Expected result Status approved, every unused token on the application invalidated, an acceptance email to the promoter, and — in the same transaction — a draft contract created with the riders your flags implied.
    Watch out for Approving is the moment the paperwork exists. You can approve, but you cannot lock or counter-sign what you just drafted: that split between running the calendar and binding the venue is intentional.

Review the riders your approval created

Read the drafted contract and its auto-injected riders — and learn why you cannot edit them.

Owned by Venue Manager · 11 steps · about 12 minutes

Why this exists

Approving an application drafts a contract in the same transaction, and the riders in that draft are not chosen by a human — they are derived from the flags the promoter ticked on the application. Live music, alcohol, late night and ticket royalty each pull their clause from the rider library, and the headcount, average ticket price and event date are snapshotted into the contract's variables from what was submitted. The design goal is that the venue's standard terms cannot be forgotten by whoever happened to be reviewing that night.

Which is exactly why a venue manager can read this and not write it. The built permissions are clear and worth stating plainly, because the training plan assumed otherwise: every contract edit is admin-only. Editing a section, adding or removing one, changing the variables, re-syncing the riders, locking, unlocking, voiding, resending the signature link and countersigning are all refused for a manager. What you get is the contracts list, the contract page, the rendered preview, and the redlines view that shows how the draft differs from the template.

That is not a gap to work around. Your approval is the operational decision — this night is worth having — and the contract is the legal one. Reviewing the draft carefully and telling an admin exactly what is wrong is a faster path than an edit button would be, because the person who binds the venue is then the person who read the change.

One built behaviour to teach, because the requirements document says otherwise: sealing a contract flips the event from draft to announced. It does not move an application to some approved state — the application's own status stays approved until a deposit is recorded.

Before you start

  • A venue manager or admin session.
  • An approved application with a drafted contract. Approve the seeded Warehouse Frequencies to make one.

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
host_promoterhost@club.test host123owns the two seeded applications, Warehouse Frequencies and Analog Sunrise

Steps 1–6 — Venue Manager

their manual →
  1. 1
    Start from the application you approved and re-read the Production flags block — live music, alcohol, late night, ticket royalty — plus the royalty percentage if it is set.
    /admin/intake/{app_id} intake
    Expected result The flags as the promoter submitted them.
    Watch out for These flags are the input to the riders. A promoter who ticked alcohol by accident has just acquired a clause, and this is the last screen where that is easy to see.
  2. 2
    Open the contracts list and find the draft your approval created.
    /admin/contracts contracts
    Expected result The contract against that application, in draft.
    Watch out for This list is open to managers and admins. The individual contract pages are broader still — a host can see their own — so do not assume a page being visible means it is yours to change.
  3. 3
    Open the draft and read it in the order that matters: the variables first — headcount, ticket price, event date — then the riders that the flags injected.
    /admin/contracts/{contract_id} contracts
    Expected result The draft with its sections, its auto-injected riders and its variable values.
    Watch out for The variables are a snapshot taken at approval. If the promoter has since changed their numbers, the contract does not follow them — that divergence is the single most common thing to catch here.
  4. 4
    Open the redlines view to see where the draft departs from the standard template.
    /contracts/{contract_id}/redlines contracts
    Expected result The differences between this contract and the template it was built from.
    Watch out for This view is manager-and-admin only. It is the fastest way to spot a clause somebody edited by hand, which is exactly what you want to know before it is signed.
  5. 5
    Render the preview to read it as the promoter will see it, with the variables substituted.
    /contracts/{contract_id}/preview contracts
    Expected result The contract as it will appear on the signing page.
    Watch out for Unfilled variables are glaringly obvious in the preview and easy to miss in the editor. Read the preview, not the source, when you are checking for blanks.
  6. 6
    Write up anything wrong and hand it to an admin — the clause, the variable, and what it should say. You cannot change it yourself: section edits, variable edits, rider re-sync, lock, unlock, void, resend and countersign are all admin-only.
    Expected result A specific request rather than 'the contract looks wrong'.
    Watch out for Do not ask for the permission instead. The split exists so that the person who binds the venue is the person who read the change.

Steps 7–10 — Admin

their manual →

Is the only role that can change anything in the contract you are reviewing.

  1. 7
    As an admin, correct the variables the manager flagged.
    /contracts/{contract_id}/variables PATCH contracts
    Expected result Updated values throughout the rendered contract.
    Watch out for Variables are substituted at render time, so fixing one repairs every place it appears — including clauses the manager did not think to check.
  2. 8
    Edit a clause where the standard wording genuinely does not fit this night.
    /contracts/{contract_id}/sections/{section_id} PATCH contracts
    Expected result The section updates and shows up in the redlines view.
    Watch out for Every hand edit is a redline somebody has to justify later. Prefer fixing the rider library over hand-editing the same clause for the fifth time.
  3. 9
    If the promoter's flags changed after approval, re-sync the riders rather than adding clauses by hand.
    /contracts/{contract_id}/resync-riders POST contracts
    Expected result The rider set matches the current flags.
    Watch out for Re-syncing touches the auto-injected riders. Check the redlines afterwards to make sure a deliberate hand edit did not get reverted along with it.
  4. 10
    Lock the contract when it is right. From here it goes out for signature and, once sealed, flips the event from draft to announced.
    /contracts/{contract_id}/lock POST contracts
    Expected result A locked contract ready for signing.
    Watch out for The seal moves the EVENT status, not the application's. The application stays approved until a deposit is recorded against it — the requirements document describes this differently from what was built.

Steps 11 — Host / Promoter

their manual →

Set the flags that produced these riders, and signs whatever they end up saying.

  1. 11
    As the promoter, read your own contract before signing. Full signing and countersigning is covered in the Host / Promoter manual.
    /admin/contracts/{contract_id} contracts
    Expected result Your contract, readable.
    Watch out for The riders came from the boxes you ticked on your application. If one surprises you, say so before signing, not after.

Convert an application into an event

One button that creates the draft event, creates or reuses the host account, and grants the scoped role.

Owned by Venue Manager · 10 steps · about 12 minutes

Why this exists

Conversion is the seam between the application pipeline and the events engine, and it is deliberately a single button doing three things atomically: it creates a draft event seeded from the application, it creates or reuses the host's user account, and it grants that user the host role scoped to this one event. If any part fails, none of it happened.

The scoped grant is the piece worth understanding. Being a host is not a standing privilege on this platform — it is a grant attached to a specific event id, so a promoter with three nights across the year has three grants and can see precisely three events. There is no such thing as a promoter who can browse the venue's calendar.

The event lands as a draft on purpose. A draft is invisible to the public and never appears in the door's event picker, so conversion cannot accidentally put something on sale. Announcing it is a separate, admin-only transition — and in practice it is the contract seal that flips draft to announced, which is the platform's way of saying no night goes public before the paperwork is done.

Conversion is once and only once. A second attempt returns a conflict carrying the existing event id, and a unique index on the source application backstops the race. If the converted event is wrong, the answer is to fix the event, not to convert again.

Before you start

  • A venue manager or admin session.
  • An application in approved status that has not been converted. The seed ships Analog Sunrise, approved and unconverted, scored 77.

Practise with

PersonaEmailPasswordNote
venue_managermanager@club.test manager123shift lead: implies door_staff, plus intake, sales, guest list, marketing
host_promoterhost@club.test host123owns the two seeded applications, Warehouse Frequencies and Analog Sunrise
adminadmin@club.test admin123the only account that can touch money, identity and contracts

Steps 1–6 — Venue Manager

their manual →
  1. 1
    Filter the queue to approved applications and pick one that has no event against it yet.
    /admin/intake intake
    Expected result Approved applications, some already showing a converted event link.
    Watch out for An application already showing View event has been converted. Look for that link before you go looking for the convert button.
  2. 2
    Open it and check the financials block one last time — the tier estimates are what the draft event's tiers will be seeded from.
    /admin/intake/{app_id} intake
    Expected result The tier estimate rows with names, prices and quantities.
    Watch out for A tier quantity mismatch flag on the scorecard means the tier quantities do not add up against the headcount. Converting anyway is legal, but you have just seeded an event with tiers somebody will have to fix.
  3. 3
    Press Convert to Draft Event and confirm the prompt, which asks in plain words whether to create the draft event and grant the host role.
    /api/intake/applications/{app_id}/convert POST intake
    Expected result A new draft event id; the page now offers a View event link.
    Watch out for 409 application_not_approved means you are trying to convert something still under review. 409 already_converted carries the existing event id in the message — read it rather than pressing again.
  4. 4
    Follow the link into the new draft event and check what was copied: name, description, the slot times, the doors-open time, and the tiers.
    /admin/events/{event_id} events
    Expected result A draft event with tiers seeded from the application's estimates.
    Watch out for Draft events are invisible to the public and never appear in the guest-list door picker. If a promoter says they cannot see their event, this is almost always why.
  5. 5
    Note that the application's status does not move. It stays approved; recording a deposit is what advances it to contract deposit, and that is the contract flow's job.
    /admin/intake/{app_id} intake
    Expected result Status still approved, with a converted event id stamped on it.
    Watch out for Do not force the status to make the board look tidy. The board is telling you the truth — an event exists, the deposit does not.
  6. 6
    Understand what you have and have not just done. You created a draft and a scoped host. You did not set prices, open sales, or announce anything — all admin writes.
    Expected result A clean handover point to an admin.
    Watch out for Do not promise the promoter a public listing off the back of a conversion. The event goes public when the contract is sealed, not when you press this button.

Steps 7–8 — Host / Promoter

their manual →

Receives the account and the scoped grant this button creates.

  1. 7
    As the promoter, sign in — new hosts receive an email with a temporary password — and confirm the host grant on your account.
    /account rbac
    Expected result A host grant scoped to that one event.
    Watch out for Scoped means scoped. You will not see the venue's other events anywhere, and that is not a bug in your account.
  2. 8
    Open your event's dashboard and read the sales figures. What you can and cannot change is covered in the Host / Promoter manual.
    /admin/events/{event_id} events
    Expected result Your event, read-only, with its tiers and sales.
    Watch out for Nothing about the grant lets you edit tiers or prices. It grants visibility of your night, not control of the venue's inventory.

Steps 9–10 — Admin

their manual →

Owns everything that happens to the draft event afterwards — tiers, prices and the transition to announced.

  1. 9
    As an admin, finish the draft: check the tiers the conversion seeded, set real prices, map the zones, and set the release rules.
    /admin/events/{event_id} events
    Expected result An event ready to announce.
    Watch out for The seeded tiers are the promoter's estimates, not your pricing. Treat them as a starting draft, never as agreed numbers.
  2. 10
    Check the host user the conversion created or reused, and their grants.
    /admin/users/{user_id} rbac
    Expected result The user with a host grant scoped to the event.
    Watch out for Conversion reuses an existing user when the email matches, so a returning promoter accumulates grants rather than accounts. Duplicate grants are an audited no-op, not an error.

Review and e-sign your venue agreement

Open the tokenised signing link, read the hashed document, sign it, and wait for the counter-signature.

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

Why this exists

The contract is a gate, not a formality, and the mechanism is worth understanding before you press anything.

When an admin locks the contract, the platform renders it canonically — a deterministic HTML rendering with no timestamps and no randomness — and takes the SHA-256 hash of that rendering. From that moment the sections and the variables are frozen by database triggers. The signing link you are emailed carries a token, and the token is your credential: no account, no password, no login. What you sign is recorded against that hash. If the document were altered underneath you, the recomputed hash would not match and signing would be refused with a hash mismatch. That is the guarantee the hash exists to give you, not the venue.

Signing is not the end. Your signature moves the contract to host_signed and it waits for the venue's counter-signature, which only an admin can give. The counter-signature seals the document into immutable stored HTML with a sealed hash, advances the application to contract_deposit, and attempts to flip the linked event from draft to announced.

Attempts, because the event has its own gate and it is checked separately. The seal always succeeds; the flip does not, and a refused flip does not undo anything. An event still missing a required time, or whose booked act has not yet confirmed, stays a draft — the agreement is final and the night is not yet public. Today the only sign of that is on the staff channel, so if your event has not appeared on the public listing shortly after you get the executed email, ask the venue; the usual answer is a confirmation somebody has not sent yet. (This paragraph describes what happens now. Making the refusal visible to you, rather than to a staff channel, is plan/631.)

Built behaviour, differing from the BRD: the seal moves the event draft to announced. There is no contract_pending or approved event status in this platform. Announced means publicly visible — it does not mean on sale. Going on sale is a separate, deliberate admin action afterwards.

Before you start

  • An approved application of yours with a drafted contract.
  • The signing link from the 'ready to sign' email (or a freshly resent one).
  • An admin to lock the document beforehand and to counter-sign afterwards.

Practise with

PersonaEmailPasswordNote
host_promoterhost@club.test host123owns the seeded 'Neon Circuit Gala' application and its draft contract
adminadmin@club.test admin123the only persona that can lock and counter-sign
venue_managermanager@club.test manager123can read the contract and its redlines, and nothing else

Steps 1–2 — Admin

their manual →

Locks and hashes the document, then counter-signs on behalf of the venue.

  1. 1
    Open the drafted contract, check the auto-injected riders against the promoter's flags, and fill every variable: headcount, ticket price and event date.
    /admin/contracts/{contract_id} contracts
    Expected result A section-by-section editor. Variables render inline in blue, and every edit you make is recorded as an append-only redline.
    Watch out for A blank variable blocks the lock. That is the intended failure — an unpriced agreement must never reach a signature.
  2. 2
    Lock the contract.
    /contracts/{contract_id}/lock POST contracts
    Expected result The document is canonically rendered and SHA-256 hashed, a signing token is minted, and the signing link is emailed to the host.
    Watch out for 422 comes back with a reasons list, for example an empty event_date variable. Locking freezes the wording: if it is wrong, unlock (only possible while unsigned) or void and supersede — do not try to edit around it.

Steps 3–4 — Venue Manager

their manual →

Reads the agreement and its redline history, but may never lock or sign it.

  1. 3
    As the venue manager, open the contracts list and find the locked agreement.
    /admin/contracts contracts
    Expected result The contract with its status, plus any approved applications that still have no paperwork.
    Watch out for You can read this list, but the lock, void, resend and counter-sign controls are not yours — the page renders read-only for you.
  2. 4
    Read the redline history to see exactly what the venue changed from the master template before locking.
    /contracts/{contract_id}/redlines contracts
    Expected result An append-only list of edits with word-level diffs.
    Watch out for Redlines cannot be edited or deleted by anyone — the tables carry both UPDATE and DELETE triggers.

Steps 5–7 — Host / Promoter

their manual →
  1. 5
    Open the signing link from your email and read the whole agreement — your rider clauses and your numbers are in it.
    /sign/{token} contracts
    Expected result The full document exactly as it was locked, with a signature pad below it.
    Watch out for An expired link renders 'Link expired' with a 410; a revised agreement renders 'Agreement revised' with a 410 because the old contract was voided and a fresh draft supersedes it. In both cases ask the venue to resend rather than hunting for another route in. /contracts/sign/{token} redirects here, so both forms of the link work.
  2. 6
    Sign: either draw your signature on the pad or switch to the Type tab and type your full legal name, then tick the consent box and press Sign Agreement.
    /sign/{token} POST contracts
    Expected result Your signature is stored against the locked hash together with your IP address and browser user agent, and the contract moves to host_signed.
    Watch out for The button stays disabled until you have both consented and produced a signature. Refusals you may see: 422 consent_required, 409 already_signed, 409 not_locked, and 409 hash_mismatch — the last one means the document drifted from what was hashed and is a stop-everything signal, not a retry.
  3. 7
    Check where the agreement stands without pestering anyone.
    /sign/{token}/status contracts
    Expected result The contract status plus two booleans: signed, and sealed.
    Watch out for signed true and sealed false is the normal in-between state — you have signed and the venue has not counter-signed yet. Reloading the signing page in that state shows a plain 'awaiting venue counter-signature' message, which is not an error.

Steps 8 — Admin

their manual →

Locks and hashes the document, then counter-signs on behalf of the venue.

  1. 8
    Counter-sign on behalf of the venue.
    /contracts/{contract_id}/countersign POST contracts
    Expected result Status executed with a sealed hash, and the response reports event_flipped and application_advanced. In the same transaction the event goes draft to announced and the application moves to contract_deposit.
    Watch out for 409 host_must_sign_first if you jump the queue, 409 already_executed on a replay. Executed rows are immutable at the database level — a trigger refuses any UPDATE, even from the admin data suite.

Steps 9–11 — Host / Promoter

their manual →
  1. 9
    Reopen your original signing link.
    /sign/{token} contracts
    Expected result The same URL now serves the sealed, executed document — your permanent copy.
    Watch out for The stored sealed HTML never changes again. A preview of an unexecuted contract is watermarked; the sealed document is not, because there is nothing provisional left about it.
  2. 10
    Once you have your host login, fetch the sealed copy from inside the app instead of from the email link.
    /contracts/{contract_id}/sealed contracts
    Expected result The same sealed HTML document.
    Watch out for This needs a session and the platform must recognise you: it lets you through if you hold the host grant for the event, or if the application's host or applicant is you, or simply if your account email matches the application's email. It is 404 not_executed until the counter-signature lands.
  3. 11
    Look at your event on the public catalogue.
    /events/{event_id} events
    Expected result It is visible — the seal announced it.
    Watch out for Announced is not on sale. Nobody can buy yet, and no amount of refreshing changes that; putting inventory on sale is a separate admin decision.

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.