Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Birthday Add-On

🧾 History

DateChangesLinkBy
July 16, 2026-ClickUp PRD 8ca1fpw-66296 (v7)Umam (PM via Claude)
July 16, 2026KB doc created from v7, decisions resolved, glossary + ADRs writtenThis docEngineering
July 16, 2026v1 scope revised (defer all diner-facing flows pending hh-pegasus access). See 📦 v1 Scope (Revised).This docEngineering

📕 About

ContextAdd-On — birthday-specific extension
Work TypeFeature (two genuinely new things: a diner-facing “Birthday Celebration” section + discovery badge, and a restaurant-facing partner-portal creation screen)
PurposeGrowing attach rate / order value on eligible bookings; also helps restaurant partnerships + diner engagement
ProblemDiners planning a birthday have nothing built for them — only generic add-ons or guesswork from reviews. Restaurants have no self-service way (until now) to offer anything special for birthdays.
SolutionRestaurants get a new partner-portal screen to create their own birthday add-on(s) — their own name, price, cover image, what’s included. These show up in a fixed “Birthday Celebration” section on the restaurant page. Restaurants can price at ฿0 for a free treat (hidden from store page / cart; only selectable after diner picks “Birthday” occasion at checkout). Restaurant cards get a small discovery icon pulled dynamically from whichever birthday add-on(s) the restaurant actually has.
Judge v1 byOne metric: does it lift attach rate / order value on eligible bookings? (≥1% target within 60 days of launch.) Everything else is a supporting benefit.
SprintStarting July 20, 2026 — flagged as tight given newly confirmed partner-portal build
PlatformWeb + mobile app (diner side), partner portal (restaurant side). v1 ships hh-server + book-bite only — hh-pegasus flows are deferred, see 📦 v1 Scope (Revised).
Design LinkBirthday Add-On Figma (partner-portal creation screen not yet mocked, only labeled placeholder)
Related Docs2002_AddOn20250910164246.md (general add-on system), 2002_Limit20250910164300.md (limits), 2002_Price_Type20250910164316.md (per-person vs per-item)

🧭 How This Fits With Other Birthday-Related Work

We have three different things touching the word “birthday” right now. Each is scoped separately so work doesn’t overlap or get confused in standup.

InitiativeWhat it really isRelation to this PRD
This PRD, Birthday Add-OnRestaurants selling their own birthday package on their restaurant page at the moment someone is already booking there.—
Occasion Vault (“The Diary”)Reminder system in diner’s profile. ~2 weeks before birthday it nudges them to book. Uses same “occasion” concept as checkout.Sits upstream of us. The free-add-on mechanism here reuses the same occasion plumbing Occasion Vault already stores.
Celebration Landing PagesSEO/discovery page listing restaurants with tags like “Birthday,” “Party Pack,” “Private Room.”Runs in parallel. Restaurant can show up here just by having tags, with or without an actual birthday add-on. The badge and store-page section here only care about real add-ons, never tags.

One thing regardless: all three must use consistent naming in analytics (Birthday Add-On, Birthday Landing Page, Birthday Diary Card) so dashboards don’t end up three ways labeled “birthday.”

📦 v1 Scope (Revised)

Decision (July 2026): hh-pegasus (diner-facing frontend) is currently out of reach. v1 ships only the data-layer and restaurant-facing surface. Diner-facing flows are deferred and will follow a separate workstream once hh-pegasus is back in scope.

In scope (this v1)

ComponentWhere
Data model — add_on_type enum column on add_ons table (per 🧠 Design Decisions)hh-server
Admin filter — global WHERE add_on_type = 'standard' across all admin catalog list viewshh-server
Commission — reuse existing package-creation inheritance (restaurant override → latest non-partner-created package → 12.50% fallback), no new rulehh-server
Backend API contracts — has_birthday_add_on exposed on restaurant API; occasion-plumbing backend ready (no consumer yet)hh-server
Flow 8 — Partner-portal Create/Edit Add-On screen with full validation: image specs (cover 420×240/10MB, menu 2048×2048/10MB), at least one “Cake” item required, price zero-or-positive, time-in-advance default to restaurant-level, items-included repeatable list with minimum 1 row (removable when > 1), delete-with-confirmbook-bite
Flow 9 — Staff-side create/edit booking in partner portal: mutual exclusivity for birthday add-ons (one active selectable per booking)book-bite
Group-login restriction — select blocked for group login accounts; create/edit allowed per restaurant (conservative for selection only)hh-server + book-bite
Zero-price bug — real test covering ฿0 add-on specifically (defensive; pays back when pegasus ships)hh-server
Feature flag — ff_birthday_add_on_v1 wired in Flipper (default off)hh-server

Deferred (gated on hh-pegasus access)

Flow / EventWhat
Flow 1 — Discovery IconIcon on 4 restaurant-card components (homepage, search, group-landing-page, bite-list), deep-link scroll to section
Flow 2 — Birthday Celebration SectionFixed section + cards on restaurant page, “swap on tap” UX
Flow 4 — Cart SwapExactly-one birthday add-on in cart, client-side swap UX
Flow 5 — Checkout AuthorityBackend authoritative gate for one-per-booking + ฿0 payment quirk
Flow 6 — Free Add-On UnlockFree add-on only selectable after diner picks Birthday occasion at checkout
Flow 7 — Confirmation Line ItemsConfirmation screen + email line items
birthday_icon_impression, birthday_icon_click, birthday_section_view, birthday_add_to_cart, birthday_occasion_selected, birthday_free_addon_added, birthday_checkout_successAll seven frontend-fired analytics events
has_birthday_add_on API field consumerWhoever displays the discovery icon / section (i.e., pegasus)

birthday_addon_created event is measurable in v1 — fires from book-bite when a restaurant creates a birthday add-on, no pegasus dependency.

Implications for success metrics

MetricStatus in v1Why
Birthday attach rate (≥1% of eligible bookings)❌ BlockedRequires diner-side add-to-cart to measure
Restaurant self-service adoption (150+ restaurants create ≥1)✅ Measurablebirthday_addon_created event + count of restaurant-created birthday add-ons
Icon effectiveness (≥3% click-through)❌ BlockedIcon lives on pegasus
Is this actually new revenue?❌ BlockedNeeds full diner funnel
Free item usage❌ BlockedFree item only selectable at checkout (Flow 6)
Are we cannibalizing other add-ons?❌ BlockedNeeds diner-side measurements
Checkout reliability (<0.5%)❌ BlockedNo checkout path yet
Refunds❌ BlockedNo diner-side purchases yet

Bottom line: in v1, the only top-line metric that moves is “restaurant self-service adoption”. The attach-rate, cannibalization, and checkout-reliability metrics are unblocked the moment hh-pegasus ships the diner-facing flows. Tracking for these deferred metrics should start fresh at pegasus release, not retroactively from v1 launch.

📕 Glossary

Vocabulary for terms unique to this PRD. Same terms must be used across hh-server, hh-pegasus, partner portal, and analytics — distinct, consistent naming is the whole point.

Add-On:

A supplementary item a restaurant sells alongside a booking, priced per-person or per-item, with its own date range, image, and limits. Stored in the add_ons table.

Avoid: “package” (means Party Pack), “promotion”, “extra” (overloaded with delivery upsells).

Birthday Add-On:

An add-on (in add_ons table) the restaurant has marked as occasion-gated for birthdays, via add_on_type = 'birthday'. Surfaced in the fixed “Birthday Celebration” section on the restaurant page, drives the discovery icon on restaurant cards, and is excluded from the admin catalog list.

Avoid: “birthday package” (already means Party Pack), “birthday perk” (overlaps with the free variant below).

Premium Birthday Add-On:

A birthday add-on priced above zero. Appears in the Birthday Celebration section and is treated like a standard add-on at checkout.

Avoid: “paid birthday package”, “birthday upsell”.

Free Birthday Add-On:

A birthday add-on priced at zero. Hidden from the store page and cart; only selectable after the diner picks “Birthday” as the booking occasion at checkout. The restaurant sets their own daily limit on how many they’ll give away.

Avoid: “comp”, “freebie”, “gift” (gift has a separate voucher meaning).

Occasion:

A diner-declared context for a booking — birthday, anniversary, business dinner, etc. — picked from a fixed selector at checkout. Used to unlock occasion-gated features (free add-ons) and to drive CRM reminders via the separate Occasion Vault initiative.

Avoid: “event” (overloaded), “celebration” (overlaps with Celebration Landing Pages).

Occasion Vault:

A separate HH initiative (codename “The Diary”) that stores each user’s occasions in their profile and sends CRM reminders roughly two weeks before the date. Upstream of any booking. Shares the “occasion” vocabulary with checkout but solves a different problem (pre-booking nudge vs. in-checkpoint unlock).

Celebration Landing Page:

A separate SEO/discovery surface listing restaurants whose tags fit a celebration theme (Birthday, Party Pack, Private Room). Built from the same restaurant-tagging system that powers other themed landing pages (e.g. Romantic Dinner). Parallel to, not downstream of, birthday add-ons.

Discovery Icon:

The small icon shown on a restaurant card when the restaurant has at least one active birthday add-on. Pulled dynamically from that add-on’s own cover image, not a fixed cake graphic. Multiple icons can show if the restaurant has multiple birthday add-ons.

Avoid: “birthday badge” (overloaded — the partner-portal creation screen also calls itself a “badge”), “cake icon” (no longer accurate since the icon is dynamic).

Add-On Type (add_on_type):

The classification column on add_ons that distinguishes occasion-gated add-ons from standard ones. Currently only standard and birthday. Reserved so other occasion types can be added later without schema changes.

Commission Inheritance:

The rule for setting commission on a new add-on (or package) when the restaurant has no explicit override: take the rate from the restaurant’s most recent active, non-partner-created package of any type; if none exists, fall back to a flat 12.50%. Already implemented for package creation; birthday add-ons plug into the same inheritance.

Avoid: “default commission” (the 12.50% is a fallback, not a default override).

🧠 Design Decisions

TopicDecisionWhy
Data modelReuse existing add_ons table; add enum column add_on_type (default 'standard', value 'birthday'); filter admin list with WHERE add_on_type = 'standard'.Every behavior in the PRD (pricing, image, limits, cart, checkout, reporting) already has a working slot in the existing table — a parallel table would duplicate those slots and force every read path to UNION two shapes. The enum also leaves room for future occasion types (anniversary, business dinner) without another schema change.
Admin visibilityBirthday add-ons invisible to admin by default, no toggle, no separate tab.PRD: “they should not appear in the regular add-on list in the admin dashboard.” Strict global filter matches the spec; a toggle adds accidental-edit risk without operational benefit.
Group login restriction scopeSelection blocked for group login accounts; creation and editing are allowed for the authenticated active restaurant context (default_restaurant), rather than trusting a request-body restaurant_id.Selection is blocked because a multi-outlet staff member should not attach a birthday add-on while creating a booking on behalf of a customer. Creation and editing are per-restaurant actions, so group-login staff can manage each outlet separately.
CommissionReuse existing package-creation inheritance: restaurant-level override → most recent active non-partner-created package (any type) → 12.50% fallback. No new “birthday default” or “free treat” carve-out.Already implemented for packages. Reusing keeps commission predictable across surfaces and avoids drift that would force every commission report to reason about two inheritance paths. Edge case worth flagging: a brand-new restaurant with no pinned rate and no legacy staff-created package lands on the 12.50% fallback when creating their first self-service birthday add-on — needs explicit confirmation before launch.

🎯 User Stories & Functional Requirements

Flow 1: Spotting a Birthday-Friendly Restaurant (Discovery Icon)

FieldDetail
TriggerA restaurant card appears anywhere diners browse (homepage, search, category pages, bite-lists).
Pre-conditionsRestaurant currently has ≥1 live, visible birthday add-on (paid or free).
What happensBackend exposes has_birthday_add_on (and qualifying add-on(s)) through restaurant API response. Frontend shows a small icon on the restaurant card. Dynamic per add-on (no longer a fixed cake graphic) — uses the add-on’s own icon/cover image. Multiple icons can show if multiple birthday add-ons exist. Icon goes into all four restaurant-card components: homepage sliders, search results, group landing pages, bite-list. Tap icon or card → restaurant page scrolled to Birthday Celebration section.
What happens afterDiner lands on restaurant page, already scrolled to birthday section.

When exactly should the icon show?

SituationIcon shown?Why
Has ≥1 active paid birthday add-on✅ Yes, using that add-on’s iconNormal case
Has only free (฿0) birthday add-on active✅ YesRestaurant still gets discovery credit
Has multiple birthday add-ons active✅ Yes, multiple iconsLayout still needs design pass
Had one, just turned it off, listing stale⚠️ Might briefly showRestaurant page checks live, no broken purchase
Add-on is in preview/staff-only mode❌ NoNot meant to be public yet
Add-on is inside a blackout date❌ NoIcon must reflect “can book this today”
Restaurant tagged “Birthday” for landing page, but no actual add-on❌ NoTwo systems intentionally separate

Our app is a wrapper around web pages (universal links), so one web approach covers web, mobile web, and app.

Link shape: web.hungryhub.com/{lang}/restaurants/{restaurant_slug}?section=birthday-celebration

Restaurant page already scrolls to specific sections (used for on-page tabs). What’s new: read section value from URL on page load, trigger scroll once birthday section has actually rendered (it may load slightly later than rest of page). If section not found: wait a few seconds before giving up; if genuinely gone, land normally at top; if URL value broken, ignore silently.

Flow 2: Browsing and Picking a Paid Birthday Add-On on the Restaurant Page

FieldDetail
TriggerDiner opens/scrolls restaurant page; restaurant has ≥1 active paid birthday add-on.
What happensFixed section “Birthday Celebration” with hardcoded title + description (same for every restaurant, not editable by anyone). Paid birthday add-on(s) show as cards reusing existing add-on card style. Free (฿0) add-ons never appear here (see Flow 6). Diner can pick exactly one birthday add-on, quantity one; picking a different one swaps the selection instantly. Checkout is the real enforcement (Flow 5).
What happens afterSelected add-on shows in cart.
Watch forIf restaurant only has free add-ons (no paid), this section doesn’t render — but the icon (Flow 1) can still show. If every paid birthday add-on is sold out / hidden, section doesn’t render (consistent with other add-ons). Swapping add-on when one already in cart: quick confirmation message.

Flow 3: Viewing an Add-On’s Details

Reuses existing add-on detail page (cover image, description, what’s included, refund policy). Nothing birthday-specific here.

Flow 4: Adding to Cart

Cart itself needs nothing new. What’s new: exactly one birthday add-on, quantity one, can sit in cart at a time. Client-side convenience logic with checkout as the real gate (Flow 5). This rule applies only to birthday add-ons; other regular add-ons unaffected.

Watch for: existing rule that blocks mixing add-ons with certain ticketed packages still works correctly once birthday add-ons are in the mix.

Flow 5: Checkout & Confirming the Booking

FieldDetail
What happensCheckout itself needs little new — payment system handles ฿0 total fine. Checkout (not the app UI) is the authoritative gate for “one birthday add-on per booking” — app’s instant swap is convenience, not the gate. Vouchers apply to birthday add-ons same as any other add-on (no special exclusion). They sit outside the separate demand-based pricing system (real edge case). Free add-on shows as a literal “฿0” line in order summary (numeric breakdown, not “Free” word).
Watch forZero-price bug in existing pricing code — ฿0 currently treated as “not calculated yet”, fallback substituted in. Lands on zero today by coincidence; needs a real test before launch. Also: ฿0 alongside a real charge (deposit) shouldn’t confuse payment flow. If free-item daily limit already hit at checkout: clearly block + say so.

Flow 6: Selecting Birthday as the Occasion Unlocks the Free Add-On

FieldDetail
TriggerDiner picks “Birthday” from the existing occasion selector at checkout.
What happensFree add-on never appears on store page or cart; only becomes selectable here, at checkout, after the occasion is picked. No extra validation, no minimum spend, no separate confirmation step. If restaurant has one, it’s offered right there; diner adds or skips.
What happens afterIf added, shows in order summary as ฿0 line, on confirmation as a normal add-on.
Watch forOnly one free add-on per booking (same rule as paid). If restaurant’s free daily limit already hit, it shouldn’t be offered at all (rather than offered then failing).

Flow 7: Booking Confirmation & Reporting

A birthday add-on is treated exactly like any other add-on everywhere in reporting, booking list, booking detail, exports, and analytics — both admin dashboard and partner portal. No new design, no special highlighting, no separate section. Deliberate call, not oversight.

Free item reads as “Free,” not “฿0.00” or blank, in confirmation screen and email.

Flow 8: Creating & Editing a Birthday Add-On (Partner Portal)

This is genuinely new: the partner portal’s current add-on screen is read-only (list only, no create/edit form). This is a real screen to design and build from scratch.

FieldDetail
What happensBirthday add-ons created only through this partner-portal screen — no path through internal admin dashboard, and they don’t appear in the standard admin add-on catalog list. Likely approach: keep using the same underlying add_ons table (see 🧠 Design Decisions above), mark them with add_on_type = 'birthday', filtered out of admin’s list view. Restaurant sets own price; ฿0 = free (Flow 6), anything above = normal in Birthday Celebration section (Flow 2).
UX detailSee the navigation hub for the BE integration doc that defines the form fields, validation rules, image upload specs, and API contracts.
BE detailSee the hh-server implementation plan for data model, admin catalog filter, and commission inheritance.

Flow 9: Picking a Birthday Add-On When Staff Manually Create / Edit a Booking (Partner Portal)

Different screen from the diner-facing store page and cart. Partner portal’s own booking create/edit tool has its own add-on selection component. Same core rule applies here: once staff pick one birthday add-on, the other birthday add-on(s) in the list become inactive / unselectable, so staff can’t attach two to the same booking. Non-birthday add-ons in the list are unaffected.

Watch for: needs its own implementation, separate from the diner-facing cart logic in Flow 4 — different screen with its own add-on selection component.

UX detail: see the navigation hub for the BE integration doc that defines the selection-gate behavior.

🧠 Rules

  • Exactly one birthday add-on per booking, quantity one. Applies regardless of how many birthday add-ons a restaurant has. Other add-ons unaffected. App swaps instantly for feel; checkout is the authoritative gate (matters for stale sessions, multiple tabs, direct API hits).
  • A restaurant can have at most one free (฿0) birthday add-on configured at any time. The partner-portal list page shows “Free Birthday Add-On (Max 1)” and the server enforces the cap at creation time.
  • A ฿0 add-on behaves differently, on purpose. Hidden from store page and cart. Selectable only after diner picks Birthday occasion at checkout (Flow 6). No extra validation beyond that.
  • Free item capped at one per booking. Restaurant sets own daily limit on how many they’ll give away.
  • No verification of actual birthday. Self-declared, same as any other add-on or occasion.
  • Vouchers work the same as any other add-on — no special exclusion. Treated as a standard add-on everywhere (Flow 7). Still sits outside the separate demand-based pricing system (real edge case).
  • Created only through partner portal, not admin. Restaurants set these up themselves. Not in the regular admin add-on list.
  • Commission follows package-creation inheritance — see 🧠 Design Decisions above.
  • Section title and description are fixed — no setting anywhere, hardcoded in app.
  • Discovery icon is dynamic — pulled from whichever birthday add-on(s) restaurant has, not one universal graphic. Multiple icons if multiple add-ons.
  • Available all day, every day it’s active. No opening-hours / time-of-day schedule. On or off for whatever active date range restaurant sets, no intraday scheduling.
  • Group-login accounts may create and edit birthday add-ons per restaurant, but cannot select them while creating or editing a booking on a customer’s behalf.

Why the free item works the way it does

The restaurant’s gesture, not HH subsidizing. Restaurant sets up ฿0 add-on = their own choice, their own cost to bear (like comping something today without our involvement). Because it’s the restaurant’s cost, they’re the natural check on how far it goes — they set own daily limit, can turn off any time. Gating behind “diner already told us it’s a birthday booking” naturally limits to people in a birthday-booking moment, not opportunistic grabs. If abused post-launch: tighten the daily limit, don’t rethink the approach.

📊 How We’ll Know If This Worked

~10M users/year, ~20k daily app users — goals sized for v1.

MetricGoalWhat it tells usHow we’d checkStatus
Birthday attach rate (main one)≥1% of eligible bookings include a birthday add-on within 60 days of launchWhether this actually converts, not just getting looked atBookings with birthday add-on ÷ total bookings at restaurants that have ≥1 activeNot started
Restaurant self-service adoption (new)150+ restaurants create ≥1 birthday add-on themselves by launch, with low support-ticket rate for doing soWhether the new partner-portal screen is actually usable without hand-holdingCount of restaurant-created birthday add-ons; support tickets tagged to this flowNot started
Icon effectiveness≥3% of people who see the icon go on to view the sectionWhether discovery icon actually drives people somewhereSee event names belowNot started
Is this actually new revenue?Birthday-add-on revenue at participating restaurants should be additional, not just diners swapping one add-on for anotherAttach rate alone can’t tell us if someone bought a birthday cake instead of a mocktail they’d have bought anywayCompare each restaurant’s total add-on revenue before and afterNot started
Free item usageNo target yet, just watchingUnderstand actual usage before setting limits/goalsCount of free redemptions via checkout-occasion pathNot started
Are we cannibalizing other add-ons?Existing add-on attach rate shouldn’t drop >10% at restaurants that add a birthday add-onMaking sure this grows the pie instead of just moving revenue aroundCompare non-birthday add-on attach rate before/afterNot started
Checkout reliability<0.5% of birthday checkouts failDirectly watching for the zero-price bugPayment error logs, filtered to birthday bookingsNot started
RefundsNo worse than normal add-on refund rateConfirms pricing / expectations are clearRefunded birthday bookings ÷ total soldNot started

Events we’ll track

EventFires when
birthday_icon_impressionDiscovery icon is actually visible on a restaurant card
birthday_icon_clickSomeone taps the icon specifically
birthday_section_viewBirthday Celebration section becomes visible on restaurant page
birthday_add_to_cartSomeone adds a paid birthday add-on to cart
birthday_occasion_selectedDiner picks Birthday as occasion at checkout
birthday_free_addon_addedFree add-on is added after Birthday occasion selection
birthday_checkout_successBooking with birthday add-on successfully confirmed
birthday_addon_createdRestaurant successfully creates birthday add-on in partner portal

These names must be distinct from anything Occasion Vault or Celebration Landing Pages use.

⚠️ Risks & What We’re Depending On

RiskTypeOwnerMitigation
Partner-portal creation screen is genuinely new, not a small tweakDelivery risk, launch-blockingFrontend + backend engThis is the foundation. Without it, no restaurant has anything to show. Top delivery priority.
Keeping birthday add-ons out of admin catalog listTechnical, needs early decisionBackend engReuse add_ons table with add_on_type enum + global WHERE add_on_type = 'standard' filter — see 🧠 Design Decisions above. Needs to be settled early since it shapes data model.
Zero-price bug in existing pricing codeTechnical, must fix before launchBackend engWrite a real test covering the free item specifically.
Existing bug: “Add” button on add-ons doesn’t update after adding to cartPre-existing, not yet startedFrontend engBirthday add-ons run through this exact flow. Review before launch.
Existing bug: “Add” button redirects to add-on detail page unexpectedlyPre-existing, not yet startedFrontend engSame reasoning — review before relying on detail-page flow.
Everything at once (icon + paid + free + partner-portal creation + 3 markets)Delivery riskPM + eng leadsKnown, accepted risk.
Commission edge case for restaurant’s first-ever add-onMinor, worth confirmingBackend eng + PMExisting commission logic skips other partner-created packages, so a restaurant with no legacy staff-created package lands on 12.50% fallback by default. Worth confirming intended outcome.

What has to be done before launch (v1 scope — see 📦 v1 Scope (Revised))

ItemOwnerNeeded byStatus
Confirm 12.50%-fallback edge case is intended for restaurant’s first add-onPM + Backend engStart of Sprint 1Not started
hh-server — add_ons.add_on_type enum migration + global admin catalog filterBackend engEnd of Sprint 1Not started
Decision made on how birthday add-ons stay out of admin catalog listBackend eng + PMStart of Sprint 1Resolved via see 🧠 Design Decisions above
hh-server — Commission inheritance: confirm it reuses package-creation logic, write spec test for birthday add-onBackend engEnd of Sprint 1Not started
hh-server — Zero-price bug has a real passing test covering ฿0 birthday add-onBackend engEnd of Sprint 1Not started
hh-server — Birthday add-ons confirmed excluded from any restaurant’s demand-based / dynamic pricing rulesOps + Backend engEnd of Sprint 1Not started
hh-server — Feature flag ff_birthday_add_on_v1 wired (default off)Backend engEnd of Sprint 1Not started
book-bite — Partner-portal Create/Edit Add-On screen built with Flow 8 validation rules (cover 420×240/10MB, menu 2048×2048/10MB, at least one “Cake” item required, price zero-or-positive, time-in-advance default to restaurant-level, repeatable items list with minimum 1 row and removable when > 1)Frontend + backend engEnd of Sprint 1Not started
book-bite — Delete-with-confirmation built for add-on listFrontend engEnd of Sprint 1Not started
book-bite — Mutual-exclusivity built in staff-side create/edit booking screen (Flow 9)Frontend engEnd of Sprint 1Not started
hh-server + book-bite — Group-login restriction enforced (selection blocked; create/edit allowed per restaurant)Backend + frontend engEnd of Sprint 1Not started
150+ restaurants have created ≥1 birthday add-on themselvesOps / Account ManagementAug 17Not started
All copy translated for TH/SG/MY (only copy used in Flow 8 + Flow 9 + confirmation-style placeholder text needs translation now; defer diner-side copy until hh-pegasus in scope)Content / LocalizationAug 7Not started

Implementation-status note (August 6, 2026): the table above is the original launch-planning snapshot and its historical Not started values are not a current code-status board. The current backend/API verification is recorded in the HHServer KB contract section (§29). Backend staging verification is green for the focused birthday, booking add-on, existing add-on, pricing, and reservation specs. Native birthday error translations and explicit zero-price/dynamic-pricing regression coverage remain launch-preparation items; diner-facing Pegasus flows remain intentionally deferred under the v1 scope decision.

Items moved to v2 (gated on hh-pegasus access)

ItemWasWhy deferred
Occasion-selection-unlocks-free-add-on flow (Flow 6)Backend + frontendFree item selectable only at checkout; checkout is in pegasus
Both existing cart bugs reviewFrontendCart swap lives in pegasus
Discovery icon on 4 restaurant-card components (Flow 1)Frontend (pegasus)Icon lives on pegasus
Birthday Celebration section + deep link (Flow 2)Frontend (pegasus)Store-page section lives on pegasus
Cart swap one-per-booking (Flow 4)Frontend (pegasus)Cart lives on pegasus
Checkout authority for one-per-booking (Flow 5)Backend + frontend (pegasus)Checkout lives in pegasus
Confirmation line item (Flow 7)Frontend (pegasus)Confirmation screen in pegasus
birthday_addon_created event is measuredBackend + frontendNow in v1 (fires from book-bite)
Frontend birthday_* events (7 events)Frontend (pegasus)Icon-click / cart / checkout events all pegasus-side

🚀 Getting This Live

On the trade-offs we considered

SuggestionOur callWhy
Launch just the store-page section first, add icon + free item laterKeep everything together (icon can slip if genuinely needed)Stronger launch story, accepted coordination risk
Hold off on free item until we have paid-tier dataKeep it in from day oneWant the full pitch ready for restaurant conversations and launch
Launch Thailand first, add SG and MY laterLaunch all three togetherPricing risk is lower now that restaurants set their own price, but translations and content still need to be ready across all three

Rollout plan

  • Feature flag: ff_birthday_add_on_v1, turned on restaurant-by-restaurant so we can ramp gradually.
  • Internal testing (week of July 20): our own team creates test birthday add-ons through the partner portal and runs the full flow end-to-end, including the pricing test.
  • Small rollout (Aug 3–7): partner-portal screen opens to a small group of restaurants across all three markets — catch usability issues in the creation flow early. Matters more than before since restaurants are now doing this themselves.
  • Wider rollout (Aug 10–14): opens up to more restaurants; icon goes live on listing surfaces once there’s enough real inventory to show.
  • Full launch (Aug 17–20): everyone, targeting August 20.

What can / can’t slip

The partner-portal creation screen can’t slip — it’s the foundation everything else sits on. The discovery icon is the one piece that could launch a week or two late if needed, without blocking the core experience.

🔍 Still Open

QuestionWhere we landed
Can restaurants configure this themselves?Resolved: yes, but only through partner portal, not admin. New engineering.
What commission applies to a birthday add-on?Resolved: no new decision — follows package-creation inheritance. See 🧠 Design Decisions above.
What exactly are the image upload rules?Resolved (Flow 8 table).
How should multiple discovery icons lay out when restaurant has >1 birthday add-on?Still open: needs design pass.
What happens when restaurant card would show both the icon and an existing “Ad” label?Still open: needs design call.
Does the free add-on need any diner-facing eligibility check beyond selecting Birthday occasion?Resolved: no.
Should restaurant-facing reporting / exports treat this specially?Resolved: no, regular add-on everywhere in reporting.
Is this truly a 3-market launch?Yes: pricing risk lower now, but translations/content still need to be ready across all three by launch.
Should the group-login restriction also apply to selection?Resolved: selection is blocked for group-login accounts; creation and editing are allowed per restaurant.

📋 What We’re Not Doing (For Now)

  • Letting diners filter / search by “has a birthday add-on” — touches search infrastructure, a bigger separate project.
  • Letting restaurants configure this through the admin dashboard, or letting HH staff create these on a restaurant’s behalf — partner portal is the only path, by design.
  • Giving these add-ons any special, more prominent treatment in restaurant-facing booking lists, reports, or exports — deliberately regular add-on everywhere in reporting.
  • Verifying anyone’s actual birthday, or sending reminders about it — separate Occasion Vault work.
  • A big new homepage entry point beyond the restaurant-card icon — no dedicated birthday landing tab in this phase.
  • Any diner-facing eligibility check or minimum spend to unlock the free add-on beyond picking Birthday as the occasion.
  • A settings screen for editing the Birthday Celebration section’s title / description — fixed copy, not configurable by anyone.

❓ FAQ

(placeholder — populate post-launch from support tickets / partner feedback)