About this mockup

A static visual of the Admin Web UI across every phase, starting from features/2026-10-17-counterfoil-admin-web-ui.md. Nothing here talks to a server. Pick a screen on the left. Each screen is shown inside a browser frame at the URL it would plausibly live at (URLs are illustrative; the tech spec decides routing).

Conventions used on every screen

  • Plain pages and forms. Every screen is a full page load. The real ui.js does exactly three things: localize read-only times into the browser's zone, show or hide the drawing form's type-dependent fields, and write the browser's zone into the event-log search form. Nothing polls, and the CSP forbids any request after load. (This mockup's own script, which switches screens, is not part of the product.)
  • Navigation shows only the areas the signed-in user's roles allow. Most screens here are viewed as Pat Admin, who holds both SYSTEM_ADMINISTRATOR and RAFFLE_ADMINISTRATOR, so both areas appear.
  • Every irreversible act and every two-person resolution takes a written note or reason, required by the API itself (since Phase 2).
  • Read-only date-times are in the viewer's browser zone and always carry a zone abbreviation (PDT / PST). The viewer in these mockups is in America/Los_Angeles.
  • Date-time inputs for raffle configuration are in the raffle's zone, and the input is labelled with it (the purple America/Los_Angeles tag). Calendar dates (tax effectiveFrom) have no zone at all.
  • Confirmations are full pages, not dialogs (Phase 1 decision D7): a destructive POST without confirm=yes renders a confirmation page and writes nothing, with scripts on or off. Most are drawn here as a dialog over the page that leads to them, to save space; the Phase 2 void and refund screens show the real page.
  • Server errors render in one envelope: code, message, and for RULE_VIOLATION the rule ID and citation. The form is shown again with the user's values kept, except passwords.
  • Money and rates are text fields submitted exactly as typed. No client-side arithmetic.
  • No screen offers a user delete, a raffle lock/unlock, a draw-number mint, or REFER_A_FRIEND.

What each phase's screens rest on

  • Phases 1 and 2 P2: as built in 0.14.0. These screens were brought in line with the shipped templates on 2026-11-01: navigation order, home tiles, the raffle list's pending counts, the locked raffle's Oversight panel, the presence radio pair, identities as Full Name <email>, and required notes on every resolution. Where the running UI and this page disagree, the running UI wins.
  • Phase 3 · Drawing preparation P3: features/2026-11-01-counterfoil-admin-web-ui-phase-3.md (draft v1). PDF printing only; the Finance/Records Officer gets the printing screens.
  • Phase 4 · Drawing night P4 and Phase 5+ · other roles P5+: no PRD yet. These screens are drawn from the API as built (Appendix B) to support writing those PRDs. Treat their layout and wording as proposals, not decisions.
  • Every Phase 3–5 screen keeps the Phase 1 rules: full page loads, server-enforced confirmations, a written reason on every irreversible act, identities shown as Full Name <email>, and nothing computed in the browser that the API owns.
  • Phase 2–5 screens are mostly viewed as single-role users (Jo Raffles, Lee Finance, Kim Counter, Alex Auditor), so their navigation shows one role's areas.

Sample data

  • Licensee: Evergreen Community Foundation, Seattle, WA. Raffle: 2026 Dream House Raffle, license ER-000123, 40,000 tickets, zone America/Los_Angeles.
  • Sales open 2026-08-01 00:00 and close 2026-11-30 00:00 local, stored as 2026-08-01T07:00:00Z and 2026-11-30T08:00:00Z, which is the PRD's own worked example.

Login & session messages

Email and password only. One generic message for any wrong credential. The session-ended notice is what a user sees when their next action after idle or absolute expiry lands here; nothing was written and unsaved input is gone.

PRD: Authentication & sessions · POST /api/v1/auth/login
🔒https://counterfoil.evergreen.example:9143/ui/login
Other messages the same screen shows
Wrong email or password (either case)
The email or password is incorrect.

Check both and try again.

Account locked (5 failures, UI + API combined)
This account is locked.

Too many failed sign-in attempts. Ask a System Administrator to unlock it.

Per-IP rate limit exceeded
Too many sign-in attempts from this network.

Wait a few minutes before trying again.

Deliberate choices

  • The locked and rate-limited messages name no account detail beyond what the user typed, and the generic message is identical whether the email exists or not.
  • There is no "remember me", no password reset link and no second factor. Reset is a System Administrator action; MFA is Module 13.

Change password (forced on first login)

A user with mustChangePassword = true lands here after login and can reach nothing else until the change succeeds. The navigation bar is absent. The same form, with navigation, is the voluntary change-password screen.

PRD: Authentication & sessions · POST /api/v1/auth/password · error WEAK_PASSWORD
🔒https://counterfoil.evergreen.example:9143/ui/password
You must change your password before continuing.

Your password was set by an administrator. Choose a new one to proceed.

400 WEAK_PASSWORD

Password must be at least 8 characters.

Change password

At least 8 characters.
Sign out

Note

  • After a server rejection every password field comes back empty, by rule. Only non-password fields keep their values.

Home (both administrator roles)

The landing page after login. Only the areas the user's roles allow appear, as tiles and in the navigation bar, in the same order. Pat Admin holds both roles, so all three groups show: System administration, Raffle administration and Oversight. A Raffle Administrator alone sees Raffles, Destruction approvals and Event log.

PRD: Foundation · Authorization & segregation of duties
🔒https://counterfoil.evergreen.example:9143/ui/

Welcome, Pat Admin

System administration

Users

Create accounts, assign roles, unlock, reset passwords, disable.

Open
System administration

Sales units

Register and retire the workstations that may submit orders.

Open
System administration

Tax jurisdictions

Maintain the local gambling-tax reference table.

Open
System administration

Event chain

Verify the significant-event hash chain and view the latest result.

Open
Raffle administration

Raffles

Create and edit draft raffles, set the tax rate, read the compliance checklist.

Open
Oversight

Destruction approvals

Approve or reject pending record-destruction requests.

Open
Oversight

Event log

Search the significant-event log by time, type, actor and entity.

Open

Home (role with no screens yet)

A Sales Operator, Finance/Records Officer or Auditor can sign in, change their password and sign out, and is told plainly that nothing else is available in this release. Superseded for the Finance/Records Officer by Phase 3 (Counterfoil printing), and for all three roles by their Phase 5+ home pages.

PRD: Authorization & segregation of duties, last criterion
🔒https://counterfoil.evergreen.example:9143/ui/

Welcome, Lee

No other functions are available to your role in this release.

This release of the web interface covers System Administrator and Raffle Administrator configuration tasks. Your work continues in the cfoil command-line client.

Your account

Email
lee.finance@evergreen.example
Roles
FINANCE_RECORDS_OFFICER
Last sign-in
2026-10-16 09:41 PDT

Error display conventions

Every server rejection is shown the same way, from the API's shared envelope { error: { code, message, rule?, details? } }. Authorization refusals by URL get a dedicated page rather than a form banner.

PRD: Parity with the API · Appendix B "Error shape"
🔒(component reference)
Plain error
409 EMAIL_TAKEN

A user with this email already exists.

Rule violation (adds rule and citation)
400 RULE_VIOLATIONR19

Local gambling-tax rate 0.0600 exceeds the statutory ceiling of 0.0500.

RCW 9.46.110(3)

Stale form after lock
409 CONFIGURATION_LOCKED

This raffle's configuration locked when its first ticket was issued. Nothing was changed. The attempt was recorded.

Field-level refusal (daylight-saving)
02:30 on 2026-03-08 is not a valid time in America/Los_Angeles: it falls inside the daylight-saving change on that date. Choose a different time.
Authorization refusal by URL (whole page)
⚠

You do not have access to this page

403 FORBIDDEN · Your roles do not include the one this area requires. Nothing was changed.

Back to home

Users list

Filter by status and role. Columns match the user resource. Last login is in the viewer's zone. No delete anywhere.

PRD: System Administrator › Users · GET /api/v1/users?status=&role=
🔒https://counterfoil.evergreen.example:9143/ui/users?status=&role=
8 users
EmailFull nameRolesStatusLast login (your zone)
pat.admin@evergreen.examplePat AdminSYSTEM_ADMINISTRATOR RAFFLE_ADMINISTRATORACTIVE2026-10-17 14:05 PDTView
jo.raffles@evergreen.exampleJo RafflesRAFFLE_ADMINISTRATORACTIVE2026-10-17 08:52 PDTView
sam.operator@evergreen.exampleSam OperatorSALES_OPERATORLOCKED2026-10-15 17:30 PDTView
lee.finance@evergreen.exampleLee FinanceFINANCE_RECORDS_OFFICERACTIVE2026-10-16 09:41 PDTView
kim.counter@evergreen.exampleKim CounterSALES_OPERATORACTIVEneverView
wsgc.auditor@gamb.wa.exampleAlex AuditorAUDITORACTIVE2026-09-30 11:02 PDTView
dana.former@evergreen.exampleDana FormerSALES_OPERATORDISABLED2026-06-12 16:20 PDTView
bootstrap.admin@evergreen.exampleBootstrap AdministratorSYSTEM_ADMINISTRATORDISABLED2026-07-13 10:00 PDTView
Showing 1–8 of 8

User detail & disable confirmation

Actions are offered according to state: Unlock only when LOCKED. Disable asks for explicit confirmation and says the record is retained. Reset password sets a new password and forces a change at next login.

PRD: System Administrator › Users · POST /users/{id}/unlock, …/reset-password, …/disable · errors NOT_LOCKED, NOT_FOUND
🔒https://counterfoil.evergreen.example:9143/ui/users/57
Users › sam.operator@evergreen.example

Sam Operator LOCKED

Edit
Locked at 2026-10-15 17:31 PDT after 5 consecutive failed sign-ins. Unlock clears the lock and resets the failure count.

Account

ID
57
Email
sam.operator@evergreen.example
Full name
Sam Operator
Roles
SALES_OPERATOR
Status
LOCKED
Must change password
No
Last login
2026-10-15 17:30 PDT

Attribution

Created
2026-07-13 11:22 PDT by pat.admin@evergreen.example (42)
Updated
2026-10-15 17:31 PDT by system

Users are never deleted. Disabling revokes every session and keeps the account for attribution across the records' retention life (R12).

Reset password dialog
🔒https://counterfoil.evergreen.example:9143/ui/users/57/reset-password
Users › sam.operator@evergreen.example › Reset password

Reset password for Sam Operator

At least 8 characters. The user must change it at their next sign-in. Tell them the password out of band; it is never shown again.
Cancel

Create user, shown after a server rejection

The same form serves create and edit (edit has no initial-password field). Here the server answered EMAIL_TAKEN: the form re-renders with every entered value kept except the password.

PRD: System Administrator › Users · POST /api/v1/users · errors EMAIL_TAKEN, NO_ROLES, UNKNOWN_ROLE, WEAK_PASSWORD
🔒https://counterfoil.evergreen.example:9143/ui/users/new
Users › Create user
409 EMAIL_TAKEN

A user with this email already exists.

Create user

At least one role. Auditor and System Administrator hold no gaming-record write authority whatever else is ticked.
Optional. If set, the user must change it at first sign-in. Cleared after any server rejection.
Cancel

Sales units

List filtered by ACTIVE/RETIRED, with the register form alongside. Only WORKSTATION is offered. Retire asks for confirmation.

PRD: System Administrator › Sales units · POST /api/v1/sales-units, POST …/{id}/retire · error VALIDATION_ERROR (duplicate code)
🔒https://counterfoil.evergreen.example:9143/ui/sales-units?status=ACTIVE

Sales units

Deployment check: at least one active workstation must be registered before the first sale, or every order is refused with SALES_UNIT_UNKNOWN.
3 active units
CodeNameTypeStatus
WS-FRONT-1Front desk 1WORKSTATIONACTIVE
WS-FRONT-2Front desk 2WORKSTATIONACTIVE
WS-MAILROOMMail-order entryWORKSTATIONACTIVE

Register a workstation

400 VALIDATION_ERROR

A sales unit with code WS-FRONT-2 already exists.

Unique, stable identifier printed on the station.
RSU and vendor-feed units are reserved for a later release and are not offered.
Retire confirmation

Tax jurisdictions

Reference data. A rate change is an append, never an edit: Edit is offered only on rows whose effective date is still in the future. No delete. effectiveFrom is a calendar date and gets no zone conversion.

PRD: System Administrator › Tax jurisdictions · POST /api/v1/tax-jurisdictions, PATCH …/{id} · error JURISDICTION_IN_EFFECT
🔒https://counterfoil.evergreen.example:9143/ui/tax-jurisdictions

Tax jurisdictions

A row that has taken effect is history: a raffle may have been configured from it. To change a rate, add a new row with a later effective date. A jurisdiction that stops taxing raffles gets a new row at 0.0000.
CityCountyStateRateEffective fromCitationStatus
——WA0.05002026-01-01RCW 9.46.110(3) 5% ceilingIN EFFECTno actions
—KingWA0.05002026-01-01King County Code ch. 4A.100IN EFFECTno actions
SeattleKingWA0.05002026-01-01SMC 5.52.030IN EFFECTno actions
BellevueKingWA0.04502026-01-01BCC 4.10.020IN EFFECTno actions
BellevueKingWA0.05002027-01-01BCC 4.10.020 (Ord. 6789)FUTURE
—SpokaneWA0.00002026-01-01No county raffle taxIN EFFECTno actions
Showing 1–6 of 6

Event chain verification

One button runs a verification. Small chains answer inline; long ones answer RUNNING and the user reloads later. The page never polls. The latest result is the same screen.

PRD: System Administrator › Event chain verification · POST /api/v1/events/verification, GET …/latest
🔒https://counterfoil.evergreen.example:9143/ui/event-chain

Event chain

Latest verification run 7

PASSThe chain is intact through sequence 128.
Status
PASS
Verified through seq
128
Events checked
128
Started
2026-10-17 14:12:00 PDT
Finished
2026-10-17 14:12:00 PDT (412 ms)
Trigger
ON_DEMAND

To search the events themselves, use the Event log.

The other three answers, in the same card position
RUNNING (202 from the server)
Verification is running on the server.

Run 8 started 2026-10-17 14:15:02 PDT. Reload this page later to see the result. This page does not refresh itself.

BROKEN
Chain BROKEN at sequence 97.

Verified through seq 96; 97 checked. An EVENT_CHAIN_BROKEN event was recorded. Preserve the database and notify the compliance lead.

ERROR
Verification did not complete (ERROR).

The run ended without a verdict. Run it again; if it repeats, check the server log.

Destruction approvals

Destruction requests are per raffle, so the screen starts by choosing one. Approve and Reject each lead to a confirmation page that requires a note; the API also requires an explicit decision, so nothing is approved by omission. Since Phase 2 both the System Administrator and the Raffle Administrator reach this screen, and the requester is shown as Full Name <email> from the API, not looked up from the user list.

PRD: System Administrator › Destruction approvals · GET /raffles/{id}/destructions, POST …/{requestId}/approval · errors DESTRUCTION_APPROVAL_SELF, DESTRUCTION_NOT_PENDING, RETENTION_NOT_EXPIRED, ARCHIVE_REQUIRED
🔒https://counterfoil.evergreen.example:9143/ui/destructions?raffle=1

Destruction approvals

Destruction is redaction under a permanent certificate, never a delete. Counts, figures, draw numbers and hashes survive; only personal detail is overwritten. Receipts, drawing reports, counterfoil manifests, tax worksheets and the event log are never destructible.
#Record classReasonRequested byRequestedStatePreview
12TICKET_STUB_DETAILRetention expired 2024-12-31; archive 2023-DH-A1 completed.Lee Finance <lee.finance@evergreen.example>2026-10-16 09:50 PDTPENDING1,980 purchaser names, 1,980 addresses, 1,602 phone numbers across 18,440 stubs
11RAFFLE_RECORDSRetention expired 2027-06-30.Lee Finance <lee.finance@evergreen.example>2026-10-02 15:11 PDTREJECTED—rejected
9COUNTERFOIL_PHYSICALAdvisory: paper stubs shredded 2024-02-01.Jo Raffles <jo.raffles@evergreen.example>2026-09-01 10:00 PDTCOMPLETEDcertificate C-0009completed
Refusals the dialog can come back with
409 DESTRUCTION_APPROVAL_SELF

You requested this destruction and cannot also approve it.

409 ARCHIVE_REQUIRED

No COMPLETED archive exists for this raffle. Records may not be destroyed until an archive is on file.

409 RETENTION_NOT_EXPIRED

TICKET_STUB_DETAIL for this raffle must be retained until 2027-06-30. The refusal was recorded as a significant event.

409 DESTRUCTION_NOT_PENDING

Request #12 is no longer PENDING (it was approved by jo.raffles@evergreen.example at 2026-10-17 14:20 PDT).

400 VALIDATION_ERROR

note: must not be blank. Nothing was redacted; the request is still PENDING.

Raffles list

Filter by configuration status. Times are the viewer's zone, so the two sales instants on the 2026 raffle show as PDT and PST respectively. The last three columns (Phase 2) count pending void, refund and destruction requests when the page loads; a non-zero count links to its queue. A draft cannot have sold a ticket, so it shows a dash for voids and refunds. A count the server would not give is also a dash, never an error.

PRD: Raffle Administrator › Raffles · GET /api/v1/raffles?configStatus=
🔒https://counterfoil.evergreen.example:9143/ui/raffles?configStatus=
3 raffles
NameLicenseSales open (your zone)Sales close (your zone)TicketsConfigVoidsRefundsDestructions
2027 Dream House Rafflenot set2027-08-01 00:00 PDT2027-11-30 00:00 PST40,000DRAFT——0
2026 Dream House RaffleER-0001232026-08-01 00:00 PDT2026-11-30 00:00 PST40,000LOCKED320
2023 Dream House RaffleER-0000982023-08-01 00:00 PDT2023-11-30 00:00 PST30,000LOCKED001
Showing 1–3 of 3

Create raffle

Every field of POST /api/v1/raffles including the tax block. The date-time inputs use the zone selected on this same form. The suggested rate comes from the server's jurisdiction lookup and the form says whether the entered rate overrides it.

PRD: Raffle Administrator › Raffles, Date-times (entry) · GET /tax-jurisdictions/suggestion
🔒https://counterfoil.evergreen.example:9143/ui/raffles/new
Raffles › Create raffle

Create raffle saved as DRAFT; nothing locks until the first ticket is sold

Optional in DRAFT. Required before the first sale.
All date-times on this form are read in this zone. Default America/Los_Angeles.
Stored as 2027-11-30T08:00:00Z.
Local gambling tax (R19)
Decimal fraction, submitted as typed. Ceiling 0.0500.
Suggested rate 0.0500 (CITY match: Seattle, King, WA · SMC 5.52.030). The entered rate matches the suggestion and does not override it.
Cancel

Suggestion states the callout can show

  • Overrides: "Suggested rate 0.0500 (COUNTY match). The entered rate 0.0450 overrides the suggestion; the raffle's tax block will record overridden: true."
  • No match: "No jurisdiction entry matched Walla Walla / Walla Walla / WA. Enter the rate directly." This is a normal answer.

Raffle detail (DRAFT)

The hub page: core fields, tax block, organization-profile status, compliance checklist, pricing plans with the recorded per-ticket rate, drawings with their prizes. Soft-removed children are hidden until Show removed is pressed (in the real UI it reloads the page with ?includeRemoved=true; no script). Reloading re-evaluates the checklist.

PRD: Raffle detail, Compliance checklist, Organization status · GET /raffles/{id}?includeRemoved=, GET …/compliance-checklist, GET /organization
🔒https://counterfoil.evergreen.example:9143/ui/raffles/3
Raffles › 2027 Dream House Raffle

2027 Dream House Raffle DRAFT

Organization profile is not configured.

Reason from the server: COUNTERFOIL_ORGANIZATION_FILE is not set. Consequence: no raffle can start selling. A receipt without a licensee name is not a lawful receipt (R9/R10). Configuring the profile is an operator action on the server; there is no screen for it.

Configuration

License number
not set
Time zone
America/Los_Angeles
Sales open
2027-08-01 00:00 PDT (2027-08-01 00:00 in raffle zone)
Sales close
2027-11-30 00:00 PST (2027-11-30 00:00 in raffle zone)
Total tickets
40,000
Created
2026-10-17 15:02 PDT by pat.admin@evergreen.example
Updated
2026-10-17 15:40 PDT by jo.raffles@evergreen.example

Local gambling tax

Jurisdiction
Seattle, King County, WA
Suggested rate
0.0500 CITY match · SMC 5.52.030
Effective rate
0.0500
Overridden
No
Worksheet
Not finalized (rate remains editable)

Compliance checklist evaluated 2026-10-17 16:04:11 PDT on page load

NOT READY · 3 FAIL
RuleCitationRequirementStatusDetail
R3WAC 230-11-014(2)≥1 pricing plan; every plan ≤ $250/ticketPASS
R4RCW 9.46.0323(3)(a)Grand prize total ≤ $10MPASS
R5RCW 9.46.0323Grand drawing defined; every drawing's eligibility standards completeFAILNo GRAND drawing configured.
R10WAC 230-11-010/-360Disclosures complete: date/time/location, prize descriptions, presence flagsPASS
R18WAC 230-11-085/-086Every bundle's discounted per-ticket rate computed and recordedPASS
LICENSERCW 9.46.0323WSGC license number recorded (required before first sale)FAILlicense_number not set.
ORGANIZATIONWAC 230-11-010Licensee profile configured on the serverFAILCOUNTERFOIL_ORGANIZATION_FILE is not set.

The lock trigger is the first sale, not checklist success. Reload the page to re-evaluate.

Pricing plans

Add pricing plan
LabelTicketsPricePer-ticket rate (recorded, R18)Status
Single ticket1150.00150.0000ACTIVEEdit · Remove
3-pack3400.00133.3333ACTIVEEdit · Remove
5-pack5600.00120.0000ACTIVEEdit · Remove

Drawings and prizes

Add drawing

Early Bird Drawing EARLY_BIRD

Scheduled 2027-09-18 19:00 PDT · Evergreen Hall, 1200 Pine St, Seattle · Winner need not be present · Entry cutoff 2027-09-15 23:59 PDT

Prizes active total $25,000.00Add prize

DescriptionValue typeFixed valueStatus
$25,000 cashFIXED25000.00ACTIVEEdit · Remove

Multiple Ticket Bonus MULTIPLE_TICKET

Scheduled 2027-11-06 19:00 PDT · Evergreen Hall · Winner need not be present · Entry cutoff 2027-10-31 23:59 PDT · Minimum 3 tickets

Prizes active total $10,000.00Add prize

DescriptionValue typeFixed valueStatus
$10,000 cashFIXED10000.00ACTIVEEdit · Remove
No GRAND drawing yet. Exactly one active GRAND drawing is required (R5). It has no entry cutoff: its pool is the sales close.

Raffle detail (LOCKED)

Same page, read-only. No add, edit or remove control anywhere except Change tax rate. The banner says the configuration is locked and why. Phase 2 adds the Oversight panel: pending counts linking to each queue, the ticket inventory and the raffle's destruction requests.

PRD: Lock status, Locked raffle · 409 CONFIGURATION_LOCKED on a stale form
🔒https://counterfoil.evergreen.example:9143/ui/raffles/1
Raffles › 2026 Dream House Raffle

2026 Dream House Raffle LOCKED

Configuration is locked.

The first ticket was issued at 2026-08-01 09:12 PDT, which locked this raffle's configuration permanently (R5). The raffle, its pricing plans, drawings and prizes can no longer be changed. Only the tax rate remains editable, until the worksheet is finalized.

Configuration

License number
ER-000123
Time zone
America/Los_Angeles
Sales open
2026-08-01 00:00 PDT
Sales close
2026-11-30 00:00 PST
Total tickets
40,000
Locked
2026-08-01 09:12 PDT

Local gambling tax

Jurisdiction
Seattle, King County, WA
Suggested rate
0.0500
Effective rate
0.0500
Overridden
No
Worksheet
Not finalized (rate remains editable)

Compliance checklist evaluated 2026-10-17 16:05:40 PDT

ALL PASS
RuleCitationRequirementStatus
R3WAC 230-11-014(2)≥1 pricing plan; every plan ≤ $250/ticketPASS
R4RCW 9.46.0323(3)(a)Grand prize total ≤ $10MPASS
R5RCW 9.46.0323Grand drawing defined; eligibility standards completePASS
R10WAC 230-11-010/-360Disclosures completePASS
R18WAC 230-11-085/-086Per-ticket rates recordedPASS
LICENSERCW 9.46.0323License number recordedPASS
ORGANIZATIONWAC 230-11-010Licensee profile configuredPASS

Pricing plans

LabelTicketsPricePer-ticket rateStatus
Single ticket1150.00150.0000ACTIVE
3-pack3400.00133.3333ACTIVE
5-pack5600.00120.0000ACTIVE

Drawings and prizes

Grand Prize Drawing GRAND

Scheduled 2026-12-05 19:00 PST · Evergreen Hall, 1200 Pine St, Seattle · Winner need not be present · No entry cutoff (pool is the sales close)

Prizes active total $2,565,000.00 of $10,000,000.00 (R4)

DescriptionValue typeFixed valueStatus
Dream House: 4 bed / 3 bath, Kirkland WA, fully furnishedFIXED2500000.00ACTIVE
2026 electric SUVFIXED65000.00ACTIVE

Early Bird Drawing EARLY_BIRD

Scheduled 2026-09-18 19:00 PDT · Evergreen Hall · Entry cutoff 2026-09-15 23:59 PDT

Prizes active total $25,000.00

DescriptionValue typeFixed valueStatus
$25,000 cashFIXED25000.00ACTIVE

Multiple Ticket Bonus MULTIPLE_TICKET

Scheduled 2026-11-06 19:00 PST · Evergreen Hall · Entry cutoff 2026-10-31 23:59 PDT · Minimum 3 tickets

Prizes active total $10,000.00

DescriptionValue typeFixed valueStatus
$10,000 cashFIXED10000.00ACTIVE

Stale form after the lock

  • If an administrator had a DRAFT edit form open when the first ticket sold, submitting it returns the form with 409 CONFIGURATION_LOCKED (see Error display conventions), changes nothing, and the server records CONFIGURATION_LOCKED_ATTEMPT.
  • Nothing on this page, or anywhere, offers lock, unlock or draw-number minting.

Pricing plan form, after an R3 violation

Label, ticket count and price. Price is a text field submitted as typed. The server derives and records the per-ticket rate, which the form shows read-only when editing.

PRD: Pricing plans, Money and rates · POST /raffles/{id}/pricing-plans · RULE_VIOLATION R3, VALIDATION_ERROR (duplicate active ticketCount)
🔒https://counterfoil.evergreen.example:9143/ui/raffles/3/pricing-plans/new
Raffles › 2027 Dream House Raffle › Add pricing plan
400 RULE_VIOLATIONR3

Price 1300.00 for 5 tickets exceeds the maximum of 250.00 per ticket (1250.00 for this plan).

WAC 230-11-014(2)

Add pricing plan

Only one active plan per ticket count.
Dollars and cents, as typed. No rounding is done in the browser.
Cancel

Drawing form with type-dependent fields

Only GRAND, EARLY_BIRD and MULTIPLE_TICKET are offered. Change the type in the mockup to see the fields appear and disappear: entry cutoff for the two non-grand types, minimum ticket count only for multiple-ticket. The form's date-times are in the raffle's zone.

PRD: Drawings · POST /raffles/{id}/drawings · VALIDATION_ERROR (second GRAND, params vs type, cutoff outside sales window)
🔒https://counterfoil.evergreen.example:9143/ui/raffles/3/drawings/new
Raffles › 2027 Dream House Raffle › Add drawing

Add drawing

Entrants holding at least the minimum ticket count by the entry cutoff.
Presence (R10) *
Required, with no default: the API will not assume either answer.
Must fall within the sales window.
At least 2.
Cancel

Prize form (not shown separately)

  • Description, value type (a single fixed FIXED option), fixed value as typed. The server answers RULE_VIOLATION R4 when the GRAND drawing's active total would exceed $10,000,000, shown in the standard envelope above the form.

Daylight-saving refusal

A local time that does not exist, or occurs twice, in the raffle's zone is refused on the field, nothing is saved and every other value is kept. The system never silently picks an occurrence.

PRD: Date-times › Daylight-saving changes · decision 10
🔒https://counterfoil.evergreen.example:9143/ui/raffles/3/drawings/14/edit
Raffles › 2027 Dream House Raffle › Edit drawing
400 VALIDATION_ERROR

Scheduled at: 02:30 on 2027-03-14 is not a valid time in America/Los_Angeles because it falls inside the daylight-saving change on that date. Choose a different time.

Edit drawing Spring Preview · EARLY_BIRD

This time does not exist on 2027-03-14 in America/Los_Angeles: clocks spring forward from 02:00 to 03:00. 01:59 and 03:00 are both valid.
Presence (R10) *
Cancel
The fall-back case uses the same shape
400 VALIDATION_ERROR

Entry cutoff: 01:30 on 2027-11-07 is ambiguous in America/Los_Angeles because it occurs twice during the daylight-saving change. Choose a different time.

Accepted just outside the change: 01:59 and 03:00 on the spring-forward date; 00:59 and 02:00 on the fall-back date.

Time zone change confirmation

Submitting a new timeZone on a draft raffle that already has date-times saves nothing until the user confirms here. Every affected field is listed with its old and new local reading. The instants do not move. Only the zone is saved on confirm.

PRD: Changing a draft raffle's time zone · decision 12
🔒https://counterfoil.evergreen.example:9143/ui/raffles/3/edit/confirm-time-zone
Raffles › 2027 Dream House Raffle › Edit raffle › Confirm time zone

Change time zone from America/Los_Angeles to America/Denver?

The moments themselves are unchanged. Only their local reading moves.

Each stored instant stays exactly as it is; it will now be shown and edited as the Denver local time below. To keep a field's original wall-clock time, edit that field afterward. Nothing is re-saved automatically.

FieldNow reads (America/Los_Angeles)Will read (America/Denver)Stored instant (unchanged)
Sales open2027-08-01 00:00→2027-08-01 01:002027-08-01T07:00:00Z
Sales close2027-11-30 00:00→2027-11-30 01:002027-11-30T08:00:00Z
Early Bird Drawing · scheduled2027-09-18 19:00→2027-09-18 20:002027-09-19T02:00:00Z
Early Bird Drawing · entry cutoff2027-09-15 23:59→2027-09-16 00:592027-09-16T06:59:00Z
Multiple Ticket Bonus · scheduled2027-11-06 19:00→2027-11-06 20:002027-11-07T02:00:00Z
Multiple Ticket Bonus · entry cutoff2027-10-31 23:59→2027-11-01 00:592027-11-01T06:59:00Z
Cancel, save nothing

When this step is skipped

  • A raffle with none of these date-times saves the new zone immediately with no confirmation.

Tax rate (editable in DRAFT and after lock)

The one post-lock parameter. Jurisdiction plus effective rate, until the worksheet is finalized. Shown after an R19 rejection.

PRD: Tax rate · PUT /raffles/{id}/tax-rate · RULE_VIOLATION R19, TAX_RATE_FINALIZED
🔒https://counterfoil.evergreen.example:9143/ui/raffles/1/tax-rate
Raffles › 2026 Dream House Raffle › Change tax rate
This raffle is LOCKED. The tax rate is the only configuration still editable, until the local gambling-tax worksheet is finalized.
400 RULE_VIOLATIONR19

Local gambling-tax rate 0.0600 exceeds the statutory ceiling of 0.0500.

RCW 9.46.110(3)

Local gambling tax

Decimal fraction as typed. Ceiling 0.0500.
Suggested rate 0.0500 (CITY match · SMC 5.52.030). An entered rate that differs is recorded as an override.
Cancel
After the worksheet is finalized
409 TAX_RATE_FINALIZED

The local gambling-tax worksheet for this raffle was finalized on 2027-01-15. The rate can no longer be changed.

Remove confirmations

Every remove is a soft removal and asks for explicit confirmation. Removing a drawing says that its prizes go with it. The removed row stays visible under "show removed".

PRD: Remove (pricing plan, drawing, prize) · DELETE endpoints are soft (status = REMOVED)
Phase 2

Void requests & approval

A locked raffle's void queue, pending by default, reached from the raffle's Oversight panel or its pending count. Approve is never offered on your own request; Withdraw is, and closes it with the requester as the resolver. Each button leads to a confirmation page with a required note.

Phase 2 PRD: Void approvals · GET /raffles/{id}/void-requests, POST …/{requestId}/approve, …/reject (both require note) · VOID_APPROVAL_SELF, VOID_REQUEST_NOT_PENDING
🔒https://counterfoil.evergreen.example:9143/ui/raffles/1/void-requests?status=PENDING
Raffles › 2026 Dream House Raffle › Void requests

Void requests

3 requests
A void is a two-person control. One person requests it; a different person approves it. An approved void keeps its draw number, visible as void, forever. A pending request changes nothing.
#Draw numberReasonRequested byRequestedStateResolution
212026-A-004417DATA_ENTRY_ERROR
Keyed twice for the same mail order; ORD-8790 is the original.
Sam Operator <sam.operator@evergreen.example>2026-09-17 10:12 PDTPENDING
222026-A-009120PURCHASER_REQUEST
Purchaser asked to cancel one ticket of a 3-pack.
Kim Counter <kim.counter@evergreen.example>2026-09-17 11:40 PDTPENDING
232026-A-012002WRONG_PRICING_PLAN
Sold as single; buyer paid for the 5-pack.
Jo Raffles <jo.raffles@evergreen.example>2026-09-17 12:05 PDTPENDING You opened this request; a different person must approve.
Showing 1–3 of 3
The confirmation page Approve leads to
🔒https://counterfoil.evergreen.example:9143/ui/raffles/1/void-requests/21/approve
Raffles › 2026 Dream House Raffle › Void requests › Approve request 21

Approve the void of draw number 2026-A-004417?

Requested by Sam Operator <sam.operator@evergreen.example>. Reason: DATA_ENTRY_ERROR. Keyed twice for the same mail order; ORD-8790 is the original.

The ticket is currently ISSUED.

Approving voids the ticket. Its draw number is kept and stays visible as void; it is never reissued. This cannot be undone.

Why — recorded with the decision.
Cancel
Other outcomes
Withdraw (own request)
"You opened this request. Withdrawing closes it; the ticket is unchanged." A note is still required.
409 VOID_REQUEST_NOT_PENDING

Void request 21 is not PENDING; another user resolved it first. Nothing changed.

400 VALIDATION_ERROR

note: must not be blank. The request is still PENDING.

Phase 2

Refund requests, approval & result

The confirmation reads the consequence fresh from the order: total, paid now, the requested amount and every ticket that will be voided. If the paid amount moved since the request was opened, a warning replaces the consequence (the server will refuse it). The result page is rebuilt from stored state, so a reload cannot resubmit. Purchaser details are not shown.

Phase 2 PRD: Refund approvals · GET /raffles/{id}/refund-requests, GET …/orders/{orderId}, POST …/{reqId}/approval { note, paymentDate? }, …/rejection · REFUND_NOT_ELIGIBLE, VOID_APPROVAL_SELF
🔒https://counterfoil.evergreen.example:9143/ui/raffles/1/refund-requests?status=PENDING
Raffles › 2026 Dream House Raffle › Refund requests

Refund requests

2 requests
A refund is a two-person control. One person requests it; a different person approves it. Approving returns the whole amount the order has paid and voids every live ticket on it, in one step that cannot be undone.
#OrderReasonAmountRequested byRequestedStateResolution
7ORD-8790PURCHASER_REQUEST
Purchaser moved out of state before the drawing.
$400.00Lee Finance <lee.finance@evergreen.example>2026-09-16 15:20 PDTPENDING
8ORD-8802DUPLICATE_ORDER
Faxed and mailed the same form.
$150.00Lee Finance <lee.finance@evergreen.example>2026-09-17 09:02 PDTPENDING
The approval confirmation
🔒https://counterfoil.evergreen.example:9143/ui/raffles/1/refund-requests/7/approval
Raffles › 2026 Dream House Raffle › Refund requests › Approve request 7

Approve the refund of order ORD-8790?

Order ORD-8790: total $400.00, paid now $400.00, requested amount $400.00.

Requested by Lee Finance <lee.finance@evergreen.example>. Reason: PURCHASER_REQUEST. Purchaser moved out of state before the drawing.

Tickets on this order: 2026-A-008230, 2026-A-008231, 2026-A-008232. Every ticket on this order that is not already void is voided, in the same transaction.

This returns the whole amount paid, as re-read when you approve. It cannot be undone.

The business date the money went back. Blank uses the raffle's local today.
Why — recorded with the decision.
Cancel
Variants
Paid amount moved since the request
The order's paid amount changed since this request was opened, from $400.00 to $250.00. The server will refuse this approval. Reject this request and open a new one.
A ticket has a pending void
409 REFUND_NOT_ELIGIBLE

A ticket on this order has a pending void request. Nothing moved.

Pending void on draw number 2026-A-008231: resolve it first

Result page after approval

Approved. $400.00 is recorded as returned, and the order is closed.

Tickets voided
2026-A-008230, 2026-A-008231, 2026-A-008232
Decided by
Jo Raffles <jo.raffles@evergreen.example>
Phase 2

Ticket inventory & lookup

Read-only. The reconciling report per series and in total, shown as an alarm if it ever does not reconcile, then a lookup by draw number and the filtered ticket list. Nothing here opens a void or changes a ticket: voids are requested through cfoil and decided in the void queue.

Phase 2 PRD: Ticket inventory · GET /raffles/{id}/ticket-inventory, GET …/tickets?status=&seriesCode=&voidPending=, GET …/tickets/{drawNumber} · NOT_FOUND
🔒https://counterfoil.evergreen.example:9143/ui/raffles/1/tickets
Raffles › 2026 Dream House Raffle › Ticket inventory

Ticket inventory

Reconciles: unissued + issued + voided = configured total.

By series

SeriesPrefixStartTotalAllocatedUnissuedIssuedVoidedVoid pending
A2026-A-140,00018,41221,58818,375373
Total40,00018,41221,58818,375373
3 tickets
Draw numberSeriesStatusIssuedVoided
2026-A-004417AISSUED2026-08-19 14:02 PDTView
2026-A-009120AISSUED2026-08-30 10:47 PDTView
2026-A-012002AISSUED2026-09-05 16:31 PDTView
Showing 1–3 of 3
Alarm, lookup miss, and one ticket
Does not reconcile. This inventory does not reconcile: unissued + issued + voided does not equal the configured total. A draw number is unaccounted for. Investigate before the next sale or drawing.
404 NOT_FOUND

No ticket with draw number 2026-A-090000 in this raffle.

Draw number
2026-A-004417
Series
A
Order
ORD-8791
Voided
not void

A void keeps its draw number forever; it is never reissued.

Phase 2

Event log search

For the Raffle Administrator and the System Administrator. Bounds are entered in the browser's zone (written into a hidden field by ui.js), half-open, and refused on a daylight-saving gap or overlap. With scripts off they are read as UTC and the page says so. Each result's actor and entity link to a narrowed search. Sign-in events withhold the actor's identity.

Phase 2 PRD: Event-log search · GET /api/v1/events?from=&to=&eventType=&actor=&entityType=&entityId=&sort=&size=
🔒https://counterfoil.evergreen.example:9143/ui/events?zone=America/Los_Angeles&entityType=ticket_void_request&entityId=21

Event log

From is included; To is not. Times are entered in the zone shown, the same zone the results are displayed in.

Any of the chosen types matches. Choose none for every type.
A user id, or system.
Clear2 events
#TypeOccurredActorEntitySummaryHashes
48213RECORD_VOID2026-09-17 14:31 PDTJo Raffles <jo.raffles@evergreen.example>ticket_void_request 21Void approved: draw number 2026-A-0044179c41…02ad
prev 77e0…b913
48107RECORD_CREATE2026-09-17 10:12 PDTSam Operator <sam.operator@evergreen.example>ticket_void_request 21Void requested: DATA_ENTRY_ERROR3be8…41c0
prev 0f19…d6e2
Scripts off, and a sign-in event
Times entered as UTC: this browser did not report its time zone (scripts may be off).
LOGIN_FAILURE · actor user 57
Identity withheld on sign-in events

Note

  • Event summaries above are illustrative; the real summaries are written by each module and keep their existing PII masking. A summary is always shown as text, never as markup.
Phase 3

Drawing preparation overview

Reached from a LOCKED raffle's page. One row per active drawing: its pool, its batches and the barrel check, all computed when the page loads. Viewed as Jo Raffles, who holds only RAFFLE_ADMINISTRATOR, on the morning after the Early Bird cutoff.

Phase 3 PRD: Drawing preparation overview · GET /raffles/{id}/pools, GET …/counterfoil-batches, GET …/drawings/{drawingId}/counterfoil-verification
🔒https://counterfoil.evergreen.example:9143/ui/raffles/1/drawing-prep
Raffles › 2026 Dream House Raffle › Drawing preparation

Drawing preparation LOCKED

Computed 2026-09-16 08:30 PDT when this page loaded. Reload to refresh.
1 · Cutoff passes2 · Close the pool3 · Cut the batch4 · Print and confirm5 · Barrel matches pool
DrawingEntry cutoffPoolPending paymentsMembersActive batchesBarrel check
Early Bird Drawing
EARLY_BIRD scheduled 2026-09-18 19:00 PDT
2026-09-15 23:59 PDT
REACHED
OPEN
preview as of load
3 8,912 preview 1 · PRELIMINARY
B-0001 · 8,904 stubs · not printed
NOT READY
pool not closed · batch not printed · 8 in pool, not printed
Pool & close
Barrel check
Multiple Ticket Bonus
MULTIPLE_TICKET scheduled 2026-11-06 19:00 PST
2026-10-31 23:59 PDT
NOT YET
OPEN 11 3,147 preview none NOT READY
no batch · pool not closed
Pool
Grand Prize Drawing
GRAND scheduled 2026-12-05 19:00 PST
2026-11-30 00:00 PST (sales close)
NOT YET
OPEN 14 21,406 preview none NOT READY
no batch · pool not closed
Pool

No column here is computed in the browser. Pool counts, coverage and balance are the API's answers, shown as returned.

Deliberate choices

  • A DRAFT raffle has no link to this page: nothing can be prepared before a sale.
  • The step strip is only a guide. The page computes it from the same three answers as the table and enforces nothing; the server does.
  • A REFER_A_FRIEND drawing would show the server's FEATURE_NOT_AVAILABLE reason in place of its pool and offer no action.
Phase 3

Pool detail & irreversible closure

The open Early Bird pool, with the close confirmation over it. Three mail-in payments are still pending, so the acknowledgement is required and starts unticked. A note is required. Below: what the same page shows once the pool is closed.

Phase 3 PRD: Pools, Closing a pool · GET …/drawings/{drawingId}/pool, POST …/pool/closure · POOL_PENDING_PAYMENTS, POOL_CUTOFF_NOT_REACHED, POOL_ALREADY_CLOSED, CARD_DATA_REJECTED
🔒https://counterfoil.evergreen.example:9143/ui/raffles/1/drawings/2/pool/close
Raffles › 2026 Dream House Raffle › Drawing preparation › Early Bird Drawing pool

Early Bird Drawing pool OPEN

Rule

Rule
EARLY_BIRD_V1
Members
Every paid, non-void ticket ordered on or before the entry cutoff. Winning removes nothing (R6).
Entry cutoff
2026-09-15 23:59 PDT · reached

Preview as of 2026-09-16 08:31 PDT, this page load

Members
8,912 tickets · 2,104 purchasers
Pending payments
3 orders placed before the cutoff are not fully paid. They are out of the pool if you close now.

Members paged · draw number and standing only; the API's member list carries no purchaser name

Download CSV
#Draw numberStanding
12026-A-000001DRAWABLE
22026-A-000002DRAWABLE
32026-A-000004DRAWABLE
Showing 1–3 of 8,912
Other answers in the same places
Before the cutoff (no Close control)
Closing becomes possible after the entry cutoff, 2026-10-31 23:59 PDT. A forged close is refused with POOL_CUTOFF_NOT_REACHED.
Count rose since the page loaded
409 POOL_PENDING_PAYMENTS

4 orders placed before the cutoff are not fully paid. Acknowledge them to close. Nothing was written.

Someone else closed it first
409 POOL_ALREADY_CLOSED

This drawing's pool was closed at 2026-09-16 08:33 PDT. Nothing was written.

The same page once closed

Early Bird Drawing pool CLOSED

Closed
2026-09-16 08:36 PDT by Jo Raffles <jo.raffles@evergreen.example>
Note
Mail for 15 Sept processed; remaining 3 cheques not received by 16 Sept 08:30.
Pending acknowledged
Yes, 3 orders
Members
8,912 · 2,104 purchasers · 2026-A-000001 – 2026-A-018377
Drawable now
8,910
Member hash
b41e09…7c2d
Withdrawn since closure: 2. Each stub is in the barrel and must be pulled before the drawing.
Draw numberReason
2026-A-004417Ticket voided 2026-09-17
2026-A-011209Payment reversed (NSF) 2026-09-17
Determinism: reproduced. Recomputed at the closing instant; the member hash matches.
Determinism: MISMATCH. Recomputing at the closing instant gives a different set. The recorded pool stands; this is an alarm, not a repair.
Not checkable under this build. The pool was closed under a superseded rule (deterministic: null). This is not a failure.
Phase 3

Generate a counterfoil batch

The mode list is narrowed by state: INITIAL only when the drawing has no active batch, SUPPLEMENT and REPLACEMENT only when it has one. Every mode needs a reason, and the confirmation says what the mode will do.

Phase 3 PRD: Counterfoil batches · POST …/drawings/{drawingId}/counterfoil-batches · COUNTERFOIL_POOL_EMPTY, RULE_VIOLATION, VALIDATION_ERROR
🔒https://counterfoil.evergreen.example:9143/ui/raffles/1/drawings/2/counterfoil-batches/new
Raffles › 2026 Dream House Raffle › Drawing preparation › Early Bird Drawing › Generate batch

Generate a batch for the Early Bird Drawing

INITIAL is not offered: B-0001 is active.
1–500 characters. Card numbers are refused.
A batch is frozen when it is cut. Its stubs, and the purchaser name, city, state and phone printed on them, can never be edited. The pool is closed, so this batch's basis is CLOSED.
Cancel
What the confirmation adds, per situation
Pool still open
This batch will be PRELIMINARY. The pool can still change. After closure you may need a supplement (new members) or a replacement.
REPLACEMENT
Supersedes B-0001 and B-0002. Both are retained forever, but every one of their stubs must be pulled from the barrel and the new batch printed in full.
Nothing new to supplement
409 COUNTERFOIL_POOL_EMPTY

Every eligible ticket for this drawing is already in an active batch. Nothing was written.

Phase 3

Barrel check (counterfoil verification)

Compares the union of active manifests with the pool now. A stub that is printed but no longer in the pool is an alarm: it is physically in the barrel and must be pulled. Tickets in the pool but not printed are outstanding work and point to a supplement.

Phase 3 PRD: Barrel check · GET …/drawings/{drawingId}/counterfoil-verification
🔒https://counterfoil.evergreen.example:9143/ui/raffles/1/drawings/2/barrel-check
Raffles › 2026 Dream House Raffle › Drawing preparation › Early Bird Drawing › Barrel check

Barrel check · Early Bird Drawing

Checked 2026-09-17 16:02 PDT on page load
NOT READY FOR DRAWINGBalanced: no

Blockers

  • Not every active batch has been fully printed and confirmed.

Printed, not in pool ALARM · 2

Each stub below is in the barrel but has lost its place in the pool. Pull it before the drawing. On drawing night you will attest each one by draw number.

Draw numberBatch
2026-A-004417B-0001
2026-A-011209B-0001

In pool, not printed 8

Generate supplement…

Normal while a batch is outstanding. A supplement batch covers exactly these.

Draw number
2026-A-017990
2026-A-018004
… 6 more

No control on this page pulls, attests or removes a stub. Attestation happens when the drawing session opens (Phase 4).

Truncation

  • On a grand-prize barrel either list can run to thousands. The API truncates at its configured cap; the page then says "showing the first 500 of a truncated list" and never implies the list is complete.
Phase 3

Batch detail & PDF print runs

Seen by the Finance/Records Officer, the second role that prints. Coverage comes from the API. Open a run on a PDF profile, download its PDF, print it at actual size, then confirm or abandon. A run opened in cfoil on a thermal profile is listed, and the UI offers only Abandon on it.

Phase 3 PRD: Printing, Downloads · …/counterfoil-batches/{batchId}, …/print-runs, …/stream, …/confirmation, …/abandonment, …/preview, …/manifest
🔒https://counterfoil.evergreen.example:9143/ui/printing/raffles/1/batches/7
Counterfoil printing › 2026 Dream House Raffle › Batch B-0001

Batch B-0001 · Early Bird Drawing ACTIVE PRELIMINARY

Batch

Stubs
8,904 · sequence 1–8,904 · 2026-A-000001 – 2026-A-018355
Pool as of
2026-09-15 09:00 PDT (pool was still open)
Generated
2026-09-15 09:00 PDT by Jo Raffles <jo.raffles@evergreen.example>
Content hash
7f20aa…91c3

Print coverage from confirmed runs

1–4,100 confirmed4,101–8,904 not printed

Not fully printed. An abandoned run leaves its range uncovered so the gap stays visible.

Print runs

RunRangeProfileDeviceStatusDownloadedResolved
11–4,100PDF_LETTEROffice laser 2FCONFIRMED09:14 PDT2026-09-15 09:58 PDT by Lee Finance <lee.finance@evergreen.example> · INTERACTIVE
24,101–8,904PDF_LETTEROffice laser 2FABANDONED10:02 PDT10:31 PDT by Lee Finance · "Toner streak from sheet 610; reprinting from 4,101"
34,101–8,904PDF_LETTEROffice laser 1FOPENnot yetexpires 2026-09-15 22:35 PDTDownload PDF
41–40ESCPOS_80Front-desk thermalOPEN11:05 PDT (cfoil)expires 2026-09-15 23:05 PDTthermal: confirm in cfoil

Open a run

Prefilled with the first uncovered gap.
Thermal profiles stay in cfoil.
Opening a run records your intent. Downloading marks nothing printed.
Confirm and abandon
Printing from the browser
Print at actual size. In the print dialog choose 100% / "Actual size", no "Fit to page", no headers and footers. Print the alignment preview first and check the cut guides and barcode against the sheet.
409 PRINT_RUN_NOT_OPEN

Run 3 is no longer open (expired 22:35 PDT). Nothing was written.

Open question in the Phase 3 PRD

  • Row 4 shows the proposed default for a thermal run opened in cfoil: Abandon is offered (it records nothing as printed), Confirm is not (only the cfoil operator watched that paper).
Phase 4

Readiness & open the drawing session

The gate is previewed and enforced by the same server code, so this page and the act cannot disagree. Every barrel blocker is a hard stop. Only the R11 reserve check can be overridden, with a reason. Withdrawn stubs are attested one draw number at a time; there is no "select all".

API: GET …/drawings/{drawingId}/readiness, POST …/drawings/{drawingId}/sessions · WITHDRAWN_STUB_UNACKNOWLEDGED, RESERVE_OVERRIDE_REQUIRED, PRIZE_RECORD_REQUIRED, SESSION_ALREADY_OPEN
🔒https://counterfoil.evergreen.example:9143/ui/raffles/1/drawings/2/session/new
Raffles › 2026 Dream House Raffle › Early Bird Drawing › Open session

Open the Early Bird Drawing

Readiness read 2026-09-18 18:42 PDT

The barrel not overridable

Pool closed and not emptyPASS8,912 members · closed 2026-09-16 08:36 PDT
Every eligible member has a printed stubPASSB-0001 + B-0002, both fully confirmed
Prize records on file (R11)PASS1 of 1 prize documented
Withdrawn stubs pulledATTEST BELOW2 printed stubs have lost standing

Attest the pulled stubs

Find each stub in the barrel, remove it, and type its draw number. The list you submit must equal the server's list exactly; it is printed on the drawing report with your name.

Prize reserve (R11) overridable with a reason

Reserve check: NOT_AVAILABLE. It could not be performed: the latest reserve balance (2026-09-01) is older than the 7-day freshness window. This is not a finding that funds are short.
Frozen onto the session with the figures and printed on the report.
Cancel
Refusals
409 WITHDRAWN_STUB_UNACKNOWLEDGED

2026-A-011209 has lost standing and is printed, but was not attested as pulled. No session was opened.

400 VALIDATION_ERROR

2026-A-004471 is not a withdrawn printed stub for this drawing. Check the number you typed.

409 PRIZE_RECORD_REQUIRED

"$25,000 cash" has no prize record, so it cannot be classified as cash or noncash. A Finance/Records Officer must file one. Not overridable.

Phase 4

Drawing console

The page the caller's assistant keeps open on drawing night. The scanner types into one field and presses Enter: a full page POST carrying exactly what the scanner produced (R2). Every presentation, accepted or rejected, becomes a numbered row. The accepted stub is shown with the purchaser's details, the only place the product shows them at speed.

API: POST …/sessions/{sessionId}/draw-events (201 for accepted and rejected) · GET …/draw-events · rejection reasons MALFORMED, WRONG_RAFFLE, WRONG_DRAWING, NOT_IN_POOL, WITHDRAWN, ALREADY_DRAWN
🔒https://counterfoil.evergreen.example:9143/ui/raffles/1/sessions/5

Early Bird Drawing SESSION OPEN

Present a stub

Sent verbatim. The browser does not trim, upper-case or reformat what was scanned.

Prizes

$25,000 cashUNAWARDED

#3 · Accepted 2026-A-007731

SCAN · 19:14:06 PDT
Purchaser
Maria Delgado
City, state
Tacoma, WA
Phone
+1 253 555 0187
Stubs in this barrel
4

Shown on screen only. Never written to an event summary.

Sequence every presentation, in order; this is what the report prints

#TimeMethodRaw inputDraw numberOutcome
119:12:40 PDTSCANCF1:1:3:2026-A-0022102026-A-002210REJECTED · WRONG_DRAWING
219:13:22 PDTKEYED2026-A-0773102026-A-077310REJECTED · NOT_IN_POOL
319:14:06 PDTSCANCF1:1:2:2026-A-0077312026-A-007731PENDING DECISION

For the Phase 4 spec

  • Rows 1 and 2 (a stub from the wrong drawing's barrel, then a mistyped number) are 201 rows with their reasons, never error banners, because the row is the evidence. A stub that should have been pulled would read WITHDRAWN the same way.
  • How the UI tells a scan from a keyed entry is open: the radio above is one option; a separate keyed field is another. The scanner must not be able to submit as KEYED silently.
  • Post/redirect/get after every presentation, so a browser reload cannot present the same stub twice.
Phase 4

Record a winner (two people at one keyboard)

The witness types their own email and password into this form, in the same request. No session is created for them, and a wrong password counts toward their lockout like any sign-in. Every witness failure has one message, so nobody at the table learns which part was wrong.

API: POST …/draw-events/{eventId}/award { drawingPrizeId, notes, witness } · WITNESS_INVALID, PRIZE_ALREADY_AWARDED, ACCOUNT_LOCKED, RATE_LIMITED
🔒https://counterfoil.evergreen.example:9143/ui/raffles/1/sessions/5/draw-events/3/award
Early Bird Drawing session › #5 2026-A-015002 › Record winner
403 WITNESS_INVALID

The witness could not be verified. Nothing was recorded. The witness's password field has been cleared.

Award 2026-A-015002 to R. Nguyen

Witness · a different Raffle Administrator or Finance/Records Officer
The witness types this themselves. It is checked once and never stored, logged or shown again.
A recorded winner is final. If the prize goes unclaimed, a Finance/Records Officer forfeits it before a redraw.
Back to console
Phase 4

Disqualify a drawn stub

Single-actor by design: the remedy is the next draw, which is the next line of the report. The stub is set aside, not returned to the barrel; presenting it again is rejected ALREADY_DRAWN.

API: POST …/draw-events/{eventId}/disqualification { reason, reasonText } · reasons NOT_PRESENT, INELIGIBLE_PURCHASER, DECLINED, OTHER
Phase 4

Close the session & the drawing report

Closing generates the immutable Raffle Drawing Report in the same transaction (R17). There is no regenerate. The closing note is required and printed on the report. Abandoning instead is offered only while no winner exists.

API: POST …/sessions/{sessionId}/closure { closingNote }, …/abandonment, GET …/report?format=pdf|csv|json · SESSION_HAS_WINNERS
🔒https://counterfoil.evergreen.example:9143/ui/raffles/1/sessions/5/close

Close the Early Bird Drawing session?

Presentations
5 (#1, #2, #4 rejected · #3 disqualified · #5 winner)
Winners
$25,000 cash → 2026-A-015002, witnessed by Lee Finance <lee.finance@evergreen.example>
Unawarded prizes
none
The report is generated now and can never be regenerated or edited.
Back

Raffle Drawing Report FROZEN

After closing, the session page offers the report: PDF · CSV · JSON. Each download is the same bytes every time.

Abandon instead
409 SESSION_HAS_WINNERS

A winner has been recorded in this session, so it cannot be abandoned. Close it instead.

Phase 5+

Home (Sales Operator)

Replaces the "no other functions" message for this role. A Sales Operator keys orders taken in person, by mail, fax or phone, records cash taken at the table, issues receipts and opens void requests.

API roles: orders, payments (record), receipts, receipt print batches, void requests
🔒https://counterfoil.evergreen.example:9143/ui/

Welcome, Kim

Workstation WS-FRONT-1 · 2026 Dream House Raffle is selling until 2026-11-30 00:00 PST
Sales

New order

Key an in-person, mail, fax or telephone order. Draw numbers are issued when you submit.

Start
Sales

Orders

Find an order, record a payment, issue or resend a receipt, request a ticket void.

Open
Sales

Mail receipts

Print receipts for mail-in orders and confirm they printed.

Open

There is no online sale anywhere in Counterfoil (R1). This screen records orders taken through the four legal channels.

Phase 5+

Order entry

No price field anywhere: the operator picks plans and quantities and the server prices them. The quote on the right is the server's preview, refreshed by the Update quote button, never by script. A hidden clientRequestId minted with the form makes a double submit return the original order instead of a second set of numbers.

API: POST …/orders/preview, POST …/orders · SALES_WINDOW_CLOSED, ATTESTATION_REQUIRED, RULE_VIOLATION P1, PURCHASER_MATCH_FOUND, INVENTORY_EXHAUSTED
🔒https://counterfoil.evergreen.example:9143/ui/raffles/1/orders/new

New order · 2026 Dream House Raffle

Workstation WS-FRONT-1
Required for mail and fax: the document you are keying from.
Purchaser
Receipts are emailed here; otherwise mailed.
Tickets
PlanTicketsPriceQuantity
Single ticket1150.00
3-pack3400.00
5-pack5600.00

Quote from the server; writes nothing

Single ticket × 1150.00
3-pack × 1 133.3333 / ticket (R18)400.00
4 tickets550.00
Sales window open. Closes 2026-11-30 00:00 PST.
Possible existing purchaser. 1 record matches this name and ZIP. You will be asked to choose when you place the order.
Phase 5+

Duplicate-purchaser prompt

A prompt, not a block. Reusing the existing record is the usual answer and keeps the buyer's ticket count whole for the multiple-ticket drawing. Creating a new one is recorded as an acknowledged duplicate. Either choice resubmits the same order with the same request id.

API: 409 PURCHASER_MATCH_FOUND with details.candidates · resubmit with purchaser.purchaserId or acknowledgeDuplicate: true
🔒https://counterfoil.evergreen.example:9143/ui/raffles/1/orders/new
An existing purchaser matches this name and ZIP code. No order was placed and no draw number was issued yet.

Who is this order for?

NameAddressPhoneOrders
Maria Delgado418 N Yakima Ave, Tacoma WA 98403+1 253 555 01872
A different person with the same name. Create a new purchaser record.
Back to the form
Phase 5+

Order placed: draw numbers, payment, receipt

The order is complete the moment it exists; there is no draft. From here the operator records the money that came with it, issues the receipt, and can request a void of any ticket with a reason (approved by someone else). No card number is ever accepted: only the last four digits, in their own field.

API: GET …/orders/{orderId}, POST …/payments, POST …/receipt?deliver=, POST …/tickets/{ticketId}/void-requests · PAYMENT_EXCEEDS_ORDER_TOTAL, CARD_DATA_REJECTED
🔒https://counterfoil.evergreen.example:9143/ui/raffles/1/orders/8841

ORD-8841 COMPLETED UNPAID

MAIL · MAIL-2026-09-14-0118 · placed 2026-09-14 10:22 PDT by Kim Counter

Draw numbers 4 tickets · 550.00

Draw numberPlanStatus
2026-A-018350Single ticketISSUEDRequest void…
2026-A-0183513-packISSUEDRequest void…
2026-A-0183523-packISSUEDRequest void…
2026-A-0183533-packISSUEDRequest void…

Unpaid tickets are in no drawing pool until the order is fully paid.

Record a payment

400 CARD_DATA_REJECTED

The note looks like it contains a card number. Nothing was saved, and the value was not kept.

The business day the money moved. A calendar date.
Re-enter the note without card digits.

Receipt

Not yet issued. A receipt is an immutable snapshot; a correction is a reissue, never an edit.

Phase 5+

Home (Finance/Records Officer)

Lee's areas after Phase 5. Counterfoil printing arrives first, in Phase 3; the rest follow. Approvals Lee can give (voids, refunds) sit beside the requests Lee opens.

API roles: payments and reversals, refunds, receipts, prize records, reserve balances, winners, archives, destruction requests, print runs
🔒https://counterfoil.evergreen.example:9143/ui/

Welcome, Lee

Money

Payments & reconciliation

Ledger search, reversals, deposit matching, the held bucket.

Open
Money

Refunds & voids

Open refund requests; approve others' void and refund requests. 2 pending

Open
Prizes

Prize records & reserve

File prize evidence and bank balances before a drawing (R11). 1 prize missing a record

Open
Prizes

Winners & fulfilment

Record delivery, or forfeit an unclaimed prize before a redraw.

Open
Drawing Phase 3

Counterfoil printing

Print batches on sheet stock; confirm or abandon runs; manifests.

Open
Records

Archives & retention

Export the encrypted archive, see retention dates, request a destruction.

Open
Phase 5+

Reconciliation & reversal

The report's basis applies to both sides, or its balancing identity means nothing. Gross receipts are recognized when an order becomes fully paid; partial money sits in the held bucket. A reversal is a new negative entry with a coded reason and a required note, never an edit.

API: GET …/reconciliation?from=&to=&basis=&format=, POST …/payments/{paymentId}/reversal · reasons DATA_ENTRY_ERROR, NSF_RETURNED_CHECK, DUPLICATE_RECORDING, PROCESSOR_CHARGEBACK, OTHER
🔒https://counterfoil.evergreen.example:9143/ui/raffles/1/reconciliation?from=2026-09-01&to=2026-10-01&basis=PAYMENT_DATE

Reconciliation · 2026 Dream House Raffle

By method

MethodPaymentsReversalsRefundsNet
CASH48,200.000.00-150.0048,050.00
CHECK212,950.00-550.000.00212,400.00
CREDIT_CARD_EXTERNAL96,400.000.00-400.0096,000.00
Total356,450.00

Balancing

Gross receipts recognized
354,600.00
Held (partially paid orders)
1,850.00
Net ledger movement
356,450.00
Balances. Recognized + held = net movement, on the same basis.
Reversing a payment from the order's ledger
Phase 5+

Prize records & reserve balance

The two things a drawing session needs from Finance. A missing prize record blocks the session outright; a missing or stale balance makes the reserve check NOT_AVAILABLE, which the Raffle Administrator can only override with a reason.

API: GET …/prize-records/missing, POST …/prize-records, POST …/reserve-balances, GET …/drawings/{drawingId}/reserve-check · PRIZE_VALUE_MISMATCH, PRIZE_RECORD_LOCKED
🔒https://counterfoil.evergreen.example:9143/ui/raffles/1/prizes

Prize records & reserve · 2026 Dream House Raffle

Prizes

DrawingPrizeValueTypeEvidenceRecord
GrandDream House, Kirkland WA2500000.00NONCASHPURCHASE_CONTRACT · EH-2026-117ON FILEEdit
Grand2026 electric SUV65000.00NONCASHnone (above threshold)NEEDS DOCUMENTAdd document
Early Bird$25,000 cash25000.00CASHnot required for cashON FILE FROZEN
Multiple Ticket$10,000 cash10000.00—MISSINGFile record

A record documents a prize but never restates it: its value must equal the configured value (R10). It freezes once a session opens on it.

Record a reserve balance

The date the statement speaks to.
A later entry supersedes; none is ever edited.

Reserve check · drawings on 2026-11-06

NOT_AVAILABLE: no balance is on file within 7 days of the drawing date, and the $10,000 cash prize has no record, so it cannot be classified as cash or noncash. This is not a finding that funds are short.
Phase 5+

Winners & fulfilment

Every winner arrives here as PENDING from the drawing session. Two terminal outcomes: delivered (AWARDED) or forfeited, which is the only thing that makes a redraw of that prize possible.

API: GET …/winners, POST …/winners/{id}/fulfilment, POST …/forfeiture, GET …/winner-register?format= · FULFILMENT_NOT_PENDING
🔒https://counterfoil.evergreen.example:9143/ui/raffles/1/winners

Winners · 2026 Dream House Raffle

DrawingPrizeDraw numberWinnerDrawnStatus
Early Bird$25,000 cash2026-A-015002R. Nguyen, Olympia WA2026-09-18 19:26 PDTPENDING
The two transitions
Phase 5+

Archives, retention & destruction requests

An archive passphrase is typed, used and forgotten: never stored, never shown again, unrecoverable. A destruction request needs expired retention and a completed archive, and someone else approves it (the Phase 1–2 approval screen).

API: POST …/archives, GET …/archives/{id}/content, GET …/retention, POST …/destructions · RETENTION_NOT_EXPIRED, ARCHIVE_REQUIRED
🔒https://counterfoil.evergreen.example:9143/ui/raffles/4/records

Records · 2023 Dream House Raffle

Retention

Record classRetain until
TICKET_STUB_DETAIL2024-12-31 EXPIREDRequest destruction…
RAFFLE_RECORDS2027-06-30retained
Drawing reports, receipts, manifests, tax worksheets, event lognever destructible

Export an encrypted archive

Never stored or shown again. Lose it and the archive cannot be opened by anyone, including Counterfoil.

Latest: 2023-DH-A1 COMPLETED 2026-10-01 · Download .cfar · verify offline with cfoil archive verify.

Phase 5+

Home & record set (Auditor)

A WSGC auditor reads everything and changes nothing: no form on any Auditor page has a submit that writes. The record set composes figures from the modules that own them and states whether its ten identities hold.

API: GET …/record-set?format=, GET …/record-set/reconciliation, GET …/reports, GET /events · Auditor-cannot-mutate architecture test
🔒https://counterfoil.evergreen.example:9143/ui/raffles/1/record-set
Read-only access. Nothing in this interface can change a record for your role.

WSGC record set · 2026 Dream House Raffle

Figures

Tickets issued / voided
24,118 / 37
Gross receipts
3,212,450.00
Prizes paid
25,000.00
Net receipts
3,187,450.00
Content hash
e5c1…44a0

Identities 10 of 10 hold

GROSS_RECEIPTS_BREAKOUTHOLDS
NET_RECEIPTSHOLDS
SERIES_INVENTORYHOLDS
AMOUNT_PAID_CACHE_DRIFTHOLDS
… 6 more

Also available

Report catalogue · drawing reports · counterfoil manifests · winner register · event log search · tax worksheet. All downloads, all read-only.