Platform concept · v1.1 · reviewed three times

The restaurant already knows.
Nobody has asked it yet.

Connect your data. Know the score. Find the opportunity.

Ask My Restaurant is an AI operating system for owner-led ANZ restaurants. It connects the software the restaurant already runs, turns the numbers into a scoreboard the whole team can read, and ends every answer with a recommendation — never just a report.

The Scoreboard Published Week ending 18 January
Revenue ex GST
$14,635
▲ +6% vs 4-wk
Covers
99
▲ +4% vs last wk
Spend / head
$147.83
▲ +2% vs yr avg
Beverage SPH
$44.20
▼ −3% vs 4-wk
Wage %
26%
▲ on target
Bev match
35%
▼ −5% vs yr avg
First-time
23%
▲ 23 bookings
Returning
20%
▲ 20 guests
VIPs (6+ visits)
9
▲ +2 this wk
9-course menu
43%
▲ 44 covers
Food cost %
31%
▲ −1pt vs 4-wk
Dot notes
39
▲ new this wk

Every number carries a trend against last week, the year average, and the rolling four-week average — and is stored once, versioned, and published only after its data-quality assertions pass. A tile that cannot pass shows the last published week, labelled stale. Never a wrong number.

The problem

Every restaurant already has the data it needs. It's trapped in six systems that don't talk.

Bookings in Now Book It. Sales in Bepoz. Rosters in Loaded. Accounts in Xero. Guests in inboxes and review sites. The owner reconciles it in a spreadsheet on Mondays — when there's time. AMR brings it together and turns it into actions.

Not another dashboard

Reporting tools stop at what happened. Every AMR answer ends with a recommendation — or an explicit "no action needed," never filler.

Not autopilot

Incumbents pitch "growth on autopilot." AMR's stance is the opposite: computers do the counting so the team does more noticing.

A workflow, not a feature

The incumbents now ship AI analytics too. What they don't ship is a weekly accountability loop that runs from the number to the huddle to next Monday's result.

The claim, stated narrowly enough to defend. v1.0 said no incumbent combines guest intelligence, coaching and huddle execution. That is an absolute category claim, it is contestable on published product material, and it is easy to rebut in the room. Withdrawn. What replaces it carries an as-of date and gets re-checked every quarter:

AMR is built for owner-led ANZ restaurants running fragmented local systems. It turns governed cross-system data into a weekly accountability loop and a pre-service guest briefing.

Competitor positions verified July 2026 from official product material — not from earlier vision-doc notes.
PlayerWhat it does nowWhat that means for AMR
TenzoData aggregation, automated reports, AI questions, recommendations, forecasting, scorecards, 90+ integrations, MCP beta. Explicitly serves single sites.The closest direct threat. "Shows what's happening" is no longer true and must not be said.
SevenRoomsGuest CRM, 100+ guest data points, AI Notes, segmentation, POS-linked spendGuest data alone is not a moat — win on cross-stack neutrality and workflow
NoryAI-led forecasting, labour, inventory, profitability; aimed at 2+ locationsOwns the profitability narrative; AMR stays sharper on guest and service
AveroAnalytics for independents and groups — revenue, labour, menu engineeringNot enterprise-only; "we serve independents" isn't a difference
MarginEdgeInvoice and food-cost automation; public US$350/mo tierThe price ceiling is higher than v1.0 assumed — but buyers expect financial value
AMRGoverned cross-system metrics → a statused weekly accountability loop → a pre-service guest briefing, on the ANZ indie stackThe workflow none of them run end to end

And the moat is now one thing, not two. v1.0 said the moat was the guest graph "not any single connector" in one section and "these small integrations are AMR's actual moat" in another — which sends scarce engineering to opposite places. Resolved: the moat is the governed restaurant ontology plus the outcome-labelled operating dataset. Connectors are a wedge with a 12–24 month life. Ask AMR is table stakes.

The product

Five layers, in order

Each layer earns the next. Data that flows becomes a score; a score becomes understanding; understanding becomes a plan; the plan walks onto the floor at 5:55pm.

1

Connect

Now Book It, Bepoz, Loaded, Xero, Me&U, Google reviews, supplier invoices — plus a high-entropy per-tenant ingest address for feedback email. CSV upload is the universal on-ramp; live connectors replace it source-by-source with zero downstream change. Every connector needs written access terms before it is built — access is a commercial risk, not an engineering ticket.

2

Scoreboard

The digital evolution of the Monday spreadsheet. Revenue net and gross of tax, covers, spend per head, beverage match, wage %, food cost %, guest segments — every number with its trend, every week with its wins celebrated. A Win is chosen by rule, not by the model.

3

Understand

Ask AMR anything in plain English — "Did Michelin increase bookings?" "Who buys the beer?" — and get answers grounded in governed metrics, with the working shown on request. Analyst, accountant, interpreter. It refuses rather than guesses.

4

Coach

The biggest opportunity, the critical number, three owner focuses ranked by estimated impact on a basis the owner can see — revenue, assumed margin, or modelled net profit — questions for the team, a weekly game plan. Opportunities persist with status, a captured baseline and a measurement window, so next Monday can ask whether the number actually moved.

5

Huddle

Tonight's service, table by table: who's returning, who's celebrating, what to ask, what to notice. Phone-first and cached offline — but caching the minimum, encrypted, expiring at close, revocable remotely. Guest notes must not outlive the shift on a personal phone.

The noticing engine

Computers do the counting. Humans do the noticing.

Deterministic rules watch the guest graph and the week's bookings. The model writes the story and the questions. Rules find; the model narrates — and every answer a waiter records becomes a dot note that sharpens the next story.

People story · Welcome back

Kim Wilson

Kim's back after her longest absence since 2024. Eleven visits puts her one short of a loyalty milestone. Tonight is the visit that decides whether the gap was a blip or a drift.

  • What has she been up to? Has she travelled?
  • What would make this her best visit yet?
  • Anything coming up worth celebrating here?

The rules behind the stories

  • WELCOME BACKbooked, and away longer than 1.5× their own median gap
  • LOYALTYnext visit is their 5th, 10th, 25th, or 50th
  • AT RISKfirst-timer with no second booking in 60 days
  • LAPSED REGULARquiet for 3× their personal median, no future booking
  • FUTURE REGULARsecond and third visits booked inside 0.75× their gap
  • CELEBRATIONa stored date or booking note within the next 7 days
  • MISSED DEMANDwaitlist entries we couldn't seat

Thresholds are personal — multiples of each guest's own rhythm, never a fixed number of weeks. A weekly regular quiet for a month means something; a quarterly visitor doesn't.

But a personal threshold needs a person with a rhythm. The gap is the median between visits and is undefined below three visits — where most guests sit. Those two rules simply don't fire for them, rather than firing on a sample of one. Story volume is capped at six per service, and every rule reports precision and recall against a labelled fixture. A rule below its floor is suppressed, not shipped noisy.

"Did Michelin increase bookings?"

July 2026 bookings were up 18% on July 2025. First-time guests rose 32%; repeat guests rose 4%.

RecommendationCreate a "Michelin Selected" email sequence aimed at converting first-time visitors into repeat guests — the 32% is only worth what it returns.

↑ Every underlined figure is a slot. The model wrote {fact:bookings_yoy} and the server rendered the label, the value and the unit from one fact object. The model cannot type a digit.

The system

Built like a platform, sized like a pilot

A multi-tenant SaaS from day one — shared-schema Postgres with forced row-level security and four separated database roles, one ingestion contract for CSV and API alike, and an AI that is structurally incapable of arithmetic.

Sourcesthe software they already run
Now Book ItBepoz POSLoaded XeroMe&UGoogle reviews supplier invoicesfeedback ingest addressCSV / Excel
Ingestionone contract, any transport
per-source adaptersidempotent, natural-keyed upserts 180-day raw retentionPII vault, per-tenant envelope keys backfill on its own queue lanemapping versions + dead letter drift alerts
Data platformthe canonical restaurant
Postgres + forced RLS4 separated DB roles guest graph + merge / split / unmerge staging → canonical → martsmetric registry, one definition per KPI metric store: draft → publishedmatch review queue
Intelligencedeterministic core, generative shell
noticing rulesopportunity scoring Ask AMR — tools over governed metrics, no free SQL voice-of-guest: closed schema, no tools bound eval harness: the 100 questionsrecommendation contract
Guardnew in v1.1 — where the numbers get made
slot renderer — server writes label + value no digits outside a slot → 422 zero permitted derivations metric sensitivity + role filter
Applicationwhat the team touches
ScoreboardToday's Service (privacy-scoped offline cache) Owner CoachOpportunities Ask AMRGuest Matches to Review Monday 6am reportowner / staff roles via role-scoped views
The canonical data model
org · restaurant(tenant, timezone, country_code, currency,
                 business_day_cutoff, week_start_dow, region_key)  — a 00:30 seating belongs to which day?
app_user · membership(user_id, scope_type, scope_id, role)     — an accountant can serve five venues

guest(display_name, visit_count, avg_gap_days, segment, computed_at)
guest_identifier(kind, normalised_value, verified_at, status)  — unique per tenant, not an array
guest_consent(channel, state, evidence, effective_at)          — exports are consent-filtered
guest_link(match_method, match_score, status)                  — a number, not high|low
guest_identity_event(op: merge|split|unmerge, inputs[], outputs[])
guest_alias(alias → canonical, scope_kind: all|record)        — per-record scope makes a split possible
guest_segment_history(segment, valid_from, valid_to)          — as-of segments, for backfill

booking(business_date, status, party_size, source_record_id)
visit(guest_id NULL,                                          — anonymous walk-ins are first-class
      covers, covers_source,                                 — THE SPH DENOMINATOR
      net_total_cents, tax_total_cents, food_net, bev_alc_net,
      discount_cents, comp_cents, tips_cents, is_voided,
      menu_type, bev_match_covers, server_id,
      source_system, source_record_id, ingest_run_id)         — unique natural key ⇒ replay is a no-op
dot_note · team_member · shift(kind: rostered|actual) · huddle_entry

supplier · purchase_invoice · purchase_line · gl_account_map       — Phase 3; declared now so the
                                                               model is designed for it

metric_value(metric_key, definition_version, period_type, period_start,
             dimension_key, dimensions, numerator, denominator, value,
             unit, status: draft|published|superseded)              — nothing reads a draft
goal(metric_key, period, target_value, set_by)                 — joins to the metric store
opportunity(template_key, slot, evidence, est_impact_cents,
            impact_basis, ease_score, status, outcome_metric_key,
            baseline_value, measurement_window_days)              — "did it move?" is now a query
story(rule_key, booking_id, suggested_questions, status)      — unique ⇒ idempotent nightly re-run

feedback_item(text, trust: untrusted, sender_verified, source_record_id)
theme(key, label, status: approved|proposed)                  — closed taxonomy
feedback_theme(feedback_item, theme, sentiment, span)        — sentiment belongs here, per mention
feedback_mention(feedback_item, team_member_id FK, sentiment)  — roster-validated, or discarded

ai_output(model_id, prompt_version, facts, metric_value_ids)   — full provenance
source_connection · mapping_version · dataset_version · ingest_run
ingest_dead_letter · resolution_run · erasure_request · source_incident
context_event(region_key, date, kind, label, payload)          — joinable, and numeric
audit_log(actor_kind, before, after, request_id)             — INSERT-only grant

Green marks what v1.1 added or changed. Every tenant-scoped table carries tenant_id and an RLS policy; the three exempt tables are listed and their exemption is recorded in an ADR. Adding a table without a policy fails CI.

The API contract
AUTH & TENANT CONTEXT (normative — this is what v1.0 omitted entirely)
  Authorization: Bearer <JWT>         — carries `sub` only, NO tenant claim
  X-AMR-Tenant: <restaurant_id>       — resolved against `membership`, 403 if absent
  tenant_id is NEVER read from a path, query string, or request body.
  Every handler runs in one transaction:
      BEGIN; SET LOCAL app.current_tenant_id = $tenant; SET LOCAL app.current_role = $role;
  Plain SET of an app.* variable fails a CI lint rule.

CONVENTIONS
  Errors       RFC 9457 problem+json, stable `type` URI
  Periods      tenant-local; weeks start on restaurant.week_start_dow; `ending` inclusive
  Idempotency  Idempotency-Key on /imports and /cashup · If-Match on opportunity status

GET  /v1/scoreboard?period=week&ending=2026-01-18
GET  /v1/huddle/{date}                      POST /v1/huddle/{date}/cashup
GET  /v1/opportunities?status=suggested     POST /v1/opportunities/{id}/status
POST /v1/ask                                GET/POST /v1/goals
GET  /v1/segments/{at_risk_first_timer | lapsed_regular | upcoming_celebration}/export
GET  /v1/guests/{id}                        POST /v1/guests/{id}/erasure
GET  /v1/dot-notes                          POST /v1/dot-notes
GET  /v1/review-queue                       POST /v1/review-queue/{id}/decision
GET  /v1/sources/health                     POST /v1/imports
GET  /v1/me

POST /v1/ask — the response the whole guardrail stack depends on
  200  { answer, facts[{metric_value_id, definition_version, period, value, unit}],
         recommendation | no_action_needed(rationale), tool_calls[], cost_cents }
  200  { kind: cannot_answer, missing_data[] }        — refusal is a success, not an error
  422  { type: .../unslotted_numerals, unsupported_tokens[] }

Load-bearing decisions

Three bets the whole design rests on

All three survived review. Two had their justifications rewritten — because the numbers v1.0 leaned on came from vendors of the options being compared, and a design about numeric rigour cannot rest on a statistic it hasn't checked.

0 digits

The AI never writes a number

Every KPI is defined once; the dashboard, the Monday report and Ask AMR read the same stored value or no value. The model emits {fact:id} slots and the server renders the label, the value and the unit from one fact object — so a real number cannot appear under the wrong label, and there is no arithmetic left to get wrong.

Deferred deliberately: a typed metric registry in code now; a semantic-layer product when metric and consumer count — not tenant count — justifies it.

4 roles

Shared-schema tenancy, forced RLS

Single Postgres, tenant_id everywhere, FORCE ROW LEVEL SECURITY, and four separated database roles — migrate, app, worker, support — where the app role owns nothing and cannot hold BYPASSRLS. Batch jobs run per tenant with context set, not as cross-tenant sweeps.

Corrected: RLS is row-level and cannot mask a column. "Staff can't see wage %" is enforced by metric sensitivity classes and role-scoped views — v1.0 called it a database guarantee, which it wasn't.

NZ$399–499

Priced for what it actually costs

Commodity connectors are bought; ANZ adapters are built. Narratives are batch-generated and cached; chat is the only interactive LLM surface, capped per tenant. The real early cost is onboarding, mapping, connector support and reconciliation — not tokens — so the price carries a one-off onboarding fee and the envelope is stated as scale-dependent with a break-even tenant count.

Deferred deliberately: Postgres job queue now, backfill on its own lane; a durable workflow engine when connector concurrency actually hurts.

New in v1.1

The two things a guest-data product has to get right

v1.0 had no attacker model for the text it ingests, and a deletion mechanism that covered one of the five places personal data actually lives. Both reviews landed on these independently. They are now sections, not assumptions.

The attack nobody had modelled

AMR ingests reviews, forwarded feedback, booking notes and dot notes — then shows them to a model that extracts named staff. v1.0's ingest address was feedback@<tenant>: guessable by construction, accepting mail from anyone.

Review-bombing is routine in hospitality. The harm isn't data theft — the tools are read-only and the output reaches only the owner. The harm is a line in the Monday report reading "Sarah was named negatively four times this week," and a real employee disciplined on fabricated evidence.

THE ONLY FAILURE MODE HERE WHOSE VICTIM IS OUTSIDE THE TENANT.

What closes it

  • CLOSED SCHEMAextraction returns theme ID, sentiment enum, roster ID — no model-authored free text ever reaches a report
  • ROSTER FKa staff mention is a foreign key or it is discarded; an invented name cannot be surfaced
  • CORROBORATIONa named-staff claim needs ≥3 items from ≥2 verified sources, quotes linked
  • HIGH ENTROPYthe ingest address is random, never the venue's name
  • ARC, NOT SPFforwarding breaks SPF and usually DKIM — the obvious defence is the wrong one
  • EMPTY TOOLSETextraction passes run with no tools bound, which closes exfiltration as a side effect
  • 30 CASESan adversarial corpus in CI: zero cases may alter a theme, a name, a metric or a recommendation

Retention replaces "forever"

v1.0 described the raw zone as replayable forever while tokenising PII, and proposed per-guest cryptographic keys so a deletion could shred one guest out of an immutable store. But crypto-shredding only earns its keep against a store you cannot delete from — so the cheaper move is to not have one. Bounding retention removes the un-deletable store, and with it the need for per-guest key lifecycle, rotation and restore behaviour. Less crypto in Phase 1, not more.

StoreRetentionWhy
raw landing zone180 daysLong enough for reconciliation and mapping-bug replay; short enough that nothing un-deletable accumulates
canonical historystated purposeThe operating record the product exists to provide; free text separately erasable
PII vaultlife of the guestPer-tenant envelope encryption under managed KMS
backups / PITR35 daysBounded, and any restore re-applies the erasure log
AI outputs, caches, exportsregenerated or redactedWhere v1.0's mechanism silently failed — stories quote guest names verbatim

Erasing the person is separated from retaining the money. Australian tax retention runs seven years and collides head-on with erasure rights; v1.0 had no policy for the collision. An erasure nulls visit.guest_id and keeps every financial column — which works precisely because anonymous visits already count in revenue, covers and SPH. Re-running the affected periods after an erasure must produce identical published values. Deletion must not restate history.

Every table declares an erasure policy — retain, null_fk, redact_column, delete_row — with no default. A table without one fails CI. That is what makes the cascade machine-checkable rather than tribal knowledge.

Acceptance criteria

Eleven claims, eleven CI gates

v1.0 stated these as guarantees and verified exactly one of them. A guarantee with no test is a preference. These block the build.

Roadmap

Thin vertical slices, with gates you can fail

v1.0 had one falsifiable gate across five phases — and used none of the three measurable targets it had already defined. Every phase now exits on evidence, not on a deliverable list.

Phase 0 · 4 weeks Validation & access

No platform code. Write the 100 questions, interview 10 owners, run the manual export-and-upload loop, agree the first 8–12 metric definitions, build the privacy data map — and get connector terms in writing. ≥3 owners credibly willing to pay at or above NZ$399/mo, usable historical exports in hand, and written export/API confirmation for the first reservation and POS system.

Phase 1 · 8–12 weeks Trusted scoreboard

Multi-tenant core with four roles, versioned ingestion, backfill with as-of segments, the metric registry and draft→published store, Scoreboard, Monday report, Ask AMR on the top 20 questions with the full guardrail stack, the trust boundary and the erasure workflow. Identity is single-source in Phase 1 — v1.0 shipped segment tiles while deferring identity resolution to Phase 2, so Phase 1 could not build its own scoreboard. Two consecutive weekly reports reconcile to source within tolerance; AC-RLS, AC-ONE-NUMBER and AC-DELETE pass; ≥99% publish before Monday 06:00 over 30 days.

Phase 2 · 10–14 weeks Guest graph + noticing

Live Now Book It and Bepoz adapters, cross-system identity resolution with the review queue, stability contract and shared-identifier guard, stories, Today's Service with its privacy-scoped cache, staff role and role-scoped views, action segments. Second and third pilots. 3–5 pilots use the huddle in ≥60% of services over four weeks; AC-IDENTITY passes including order-independence; auto-link precision ≥0.995.

Phase 3 · 12–20 weeks Coach, cost data, paid

Opportunity templates and the accountability loop, voice-of-guest under the closed schema, Loaded / Xero / supplier-invoice adapters — the cost subject area lands, so profit impact stops being an assumption — goals loop, context events, huddle cash-up. ≥5 paying venues; positive gross margin after onboarding; ≥50% of accepted opportunities reach done; at least one outcome metric demonstrably moved inside its window.

Phase 4 · on evidence Scale decisions

Self-serve onboarding, connector catalogue, columnar marts, group views, opt-in benchmarking behind its privacy gate — minimum cohort size, k-anonymity floor, documented de-identification — and write-back actions. Triggered by customer demand and operating metrics, not by the calendar.

Stop condition Do not expand beyond the trusted-scoreboard phase if the first three pilots cannot reconcile core metrics, do not use the report repeatedly, or will not pay a price that covers onboarding and support. Change the product or the ICP before adding guest intelligence, huddles or more connectors.

And the real ceiling isn't Postgres. A thousand venues over ten years is well inside one managed instance. The binding constraint is concierge onboarding, which is human-time-linear — roughly fifty hand-run onboardings before self-serve arrives, each with thousands of match decisions to triage. That, not infrastructure, is what caps growth through Phase 3.

v1.0 → v1.1

The design was attacked twice more before it was built

A three-judge specification critique read the schema field by field. An independent investment and technical due diligence review read the market and the money. Thirty changes were folded in. These are the eight that mattered most.

Critical · fixed

The headline metric had no denominator

v1.0: visit carried check totals and no cover count.

Spend per head — the number on the front of the product — was not computable from the canonical model. Nor was revenue inc/ex GST, in a cohort spanning NZ at 15% and AU at 10%. Fix: covers with a provenance enum, a net/tax split, and void, comp and discount handling. The three contested formulas are now pinned in the document itself.

Critical · fixed

Deletion covered one of five places PII lives

v1.0: tokenise at ingest, crypto-shred to delete.

The mechanism was scoped correctly to raw records — then generalised into a compliance guarantee that cleartext in guest, dot_note, feedback_item and ai_output quietly defeated. Generated stories quote guest names verbatim. Fix: bounded retention, a declared per-table erasure policy, and a tested cascade.

Critical · fixed

No trust boundary, and an open front door

v1.0: "injection", "untrusted" and "sanitise" appear nowhere.

The decision to avoid Gmail OAuth was right, and it introduced an unauthenticated public write path that neither the original design nor its first review noticed. Fix: a whole section — closed-schema extraction, roster foreign keys, corroboration thresholds, high-entropy addressing, ARC verification and a CI-blocking adversarial corpus.

High · rebuilt

The numeric guardrail was a membership test

v1.0: check that every figure appears in the tool results.

That cannot catch a correct number under the wrong label, the pool of permitted numbers grows with every tool call, and the mandated ▲/▼ comparisons are computed rather than quoted — so they fail the check legitimately, and the check gets loosened until it's decorative. Fix: slot rendering. The model cannot type a digit.

High · corrected

RLS was asked to do something it can't

v1.0: "staff can't see wage % is a database guarantee."

PostgreSQL row-level security is row-level. Masking a column needs column grants, role-scoped views, or an extension — and with one app role plus a session variable you cannot vary column privileges per request. It was a UI convention wearing a database label. Fix: sensitivity classes on every metric, plus role-scoped views for every staff-reachable surface.

High · fixed

Ranked by a number it could not compute

v1.0: three focuses "ranked by Net Profit impact."

The model held no supplier, invoice or expense entity — so the ranking criterion had no input, and est_profit_impact was a column with nothing to fill it. A margin proxy carries two of the four templates; menu-mix fails directionally without plate cost, and a wrong sign is the one error a ranking can't absorb. Fix: declare the cost subject area, surface impact_basis in the UI, and hold menu-mix out of ranked focuses until plate cost lands.

Withdrawn

The statistic that proved the architecture

v1.0: "raw text-to-SQL solves ~21%; grounded reaches 98–100%."

Every source traced to a vendor of one of the compared options, and the two figures measure different query populations — the semantic layer's misses are removed from the count rather than failed. In a design whose entire thesis is numeric rigour, an unchecked vendor statistic is a self-inflicted wound. The decision is unchanged; it now rests on consistency, auditability and refusal, which survive scrutiny.

From due diligence

Three things the code review couldn't see

Competitor descriptions were a year stale — Tenzo now ships the AI layer v1.0 said it lacked. Connector access was treated as engineering when it is a commercial risk requiring written terms before a line is written. And the offline huddle cache, which both the original design and the specification critique waved through as a nice touch, puts guest names and notes on personal phones that outlive the shift, the employment and the access revocation.