Boom Media
Boom Labs · Product Builds

Rewardee — one loyalty engine, two very different clients

How points, rewards and staff recognition actually work for a BOO restaurant versus a service business, what is already built, and the three things standing between here and live.

29 July 2026 · read from the Rewardee source and the BOO bridge migration, not from planning notes

⚖️ The ruling this all rests on

Boundary R10

Rewardee is the loyalty engine. BOO consumes it.

BOO does not own loyalty logic and never should — it owns ordering. Rewardee owns points, tiers, rewards, stamp cards, gift cards and staff recognition, and every other app calls it. This is why 004_boo_bridge.sql exists: it makes each BOO restaurant a Rewardee business rather than duplicating a points engine inside the ordering app.

🍽️ Path one — a BOO restaurant

Diner places an order │ ▼ BOO checkout │ order completed ▼ Rewardee ── earn points at the configured rate │ ├── balance shown back in BOO at next checkout ├── reward redeemed against the order total └── /rate — diner rates their server staff recognition, not reviews
Already handled by the bridge

004_boo_bridge.sql does the whole restaurant onboarding automatically: it creates a rewardee_businesses row for every BOO restaurant keyed on the BOO client_id, migrates legacy staff reviews out of restaurant_settings.value_json into proper tables, and seeds loyalty_program_config with the rates BOO already runs — 1 point per dollar, 100 points = $5, minimum redemption 100, capped at 50% of the order.

Two details in that migration worth knowing. Lucky-order bonuses seed inactive, so switching this on changes nothing a diner sees until a restaurant opts in. And the legacy JSON review history is left in place as an archive rather than deleted — the import only runs for a business with no reviews yet, so re-running is safe.

🔧 Path two — a service business

The real difference

No checkout means no automatic earn event

A restaurant earns points because BOO knows when an order completes. A law firm, a bail bondsman or a salon has no equivalent moment the software sees. That is the whole difference — everything downstream is identical.

So the earn has to come from somewhere else:

Staff awards it

Someone at the front desk records a visit or a job completed. Rewardee already has the staff-facing routes. Lowest friction, most realistic for a small team.

Their system calls the API

An invoice paid in Dashee, a job marked complete, a form submitted — any of those can post an earn. This is the same integration BOO uses, pointed at a different trigger.

Stamp cards

rewardee_stamp_cards and rewardee_member_stamps already exist. Ten visits earns one free — no order total required, which suits service work far better than points-per-dollar.

Staff recognition on its own

/rate works without any loyalty programme at all. For a service business this is often the only part they want, and it is sellable by itself.

Do not sell points-per-dollar to a service business by default. A client whose customers visit four times a year will never accumulate a meaningful balance, and a loyalty programme nobody can reach the reward in is worse than none — it advertises that you did not think about their business. Stamp cards or recognition usually fit better.

🧩 What exists today

PieceStateNotes
Schema — 15 tablesBuiltMembers, tiers, rewards, stamp cards, gift cards, campaigns, staff, reviews
BOO bridge migrationBuilt004 — auto-onboards every BOO restaurant, migrates legacy reviews
Earn / balance / redeem APIsBuilt/api/award, /balance, /redeem-preview, /history, /program
Staff recognitionBuilt/rate plus /api/staff/* — leaderboard, manage, review
Dashboard & member portalBuilt/dashboard, /portal, /docs
BillingBuiltCheckout, portal and webhook routes exist
Migrations run on the databaseVerifyThe loyalty_* tables exist and hold data; the rewardee_* tables need confirming
Stripe products & env varsPending$29 / $59 / $99 tiers not created; billing cannot charge
External auth for the earn APIPendingSee below — this is the one real gap

🔑 The one real gap

Blocks the integration

There is no machine-to-machine key for the earn endpoint

Every loyalty route uses createServiceClient — correct, and it means the RLS lockdown of 29 July did not break anything. But those routes authenticate a signed-in user. For BOO or a client system to post an earn event server-to-server, there needs to be a bearer-key route, the pattern Replyee and Rankee already use for their v1 APIs.

Roughly: a rewardee_api_keys table with hashed keys, an auth helper, and POST /api/v1/earn that resolves the business from the key rather than a session. Half a day, and the pattern is already written twice in the estate to copy from.

POST https://rewardee.online/api/v1/earn
Authorization: Bearer rwd_live_…

{
  "external_id": "boo_order_88213",   // idempotency — a retry must not double-award
  "customer": { "phone": "+15615550142" },
  "amount_cents": 4250,
  "source": "boo"
}
Idempotency is not optional here. Webhooks and retries are normal, and a loyalty system that double-awards on a retry is one a client stops trusting the first time it happens. Key the earn on the caller's own order id and make a repeat call a no-op.

Order of work

  1. Confirm which migrations have actually run. The loyalty_* tables exist and hold rows, so 002 is live. Whether 001, 003 and 004 ran is unverified — and a deployed feature writing to a table nobody created has been the single most common failure across these apps.
  2. Run 001 → 004 in order if they have not. 004 must come last; it depends on the other three.
  3. Build the bearer-key earn API. Copy the Replyee/Rankee pattern. Nothing integrates until this exists.
  4. Wire BOO's order-completed hook to call it, with the order id as the idempotency key.
  5. Pilot on one restaurant — Guarapos. Place a real order, confirm points land, redeem one.
  6. Create the Stripe products only once a client is ready to pay. Billing routes exist; the products do not.
Sequencing note. Steps 1–5 cost nothing and can all be done before anyone is charged. Stripe last means you are never maintaining a billing configuration for a product that has not proven itself on a single real order.