Practical field-by-field intake doc for the first merchants — maps each question to its DB column so the call → admin UI is 1:1. Includes a section on marketing-material reuse aimed at franchisees, with written-authorization clauses for the future marketing module. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
15 KiB
Merchant Intake Checklist — First Store Onboarding
A practical list of every datum to collect from a new merchant before flipping their first store live on the loyalty platform. Each field is mapped to the actual database column behind it, so what you collect in the intake meeting maps 1:1 to the admin UI you'll fill in afterwards.
Use this as a sharable form, or as a script for the first onboarding call.
How to use this doc
- Send the merchant the "What you need to send us" sections (1–7) ahead of the kickoff call, so they have logos / VAT numbers / staff info ready.
- Use the business decisions sections (3, 5) to drive the actual conversation — these are the choices only they can make.
- After the call, populate the admin UI in the order below; the model enforces this dependency chain (merchant → store → loyalty program → staff).
TL;DR — what you absolutely cannot launch without
- Legal business name + VAT number + business address
- Owner email (login + invitation target)
- Store name + store code (URL slug)
- Loyalty program type (stamps / points / hybrid) + reward structure
- Privacy policy URL (GDPR)
- Brand logo (square PNG, transparent bg)
- Brand primary color (hex)
- At least one staff PIN
Everything else can be filled in iteratively post-launch.
1. Business identity (the merchant entity)
One per merchant — feeds the merchants table.
| Field | Required? | DB column | Notes |
|---|---|---|---|
| Legal business name | ✅ | name |
Appears on invoices + email footers |
| Trading name (if different) | optional | (use name) |
The name customers see |
| Business description | recommended | description |
One-line tagline |
| VAT / tax number | ✅ EU | tax_number |
LU format: LU12345678 |
| Business registration number | ✅ | tax_number (or separate field if available) |
LU: RCS Lxxxxxx |
| Business address | ✅ | business_address |
Street + city + ZIP + country |
| Primary contact email | ✅ | contact_email |
Where invoices + platform alerts go |
| Primary contact phone | recommended | contact_phone |
For support callbacks |
| Website URL | optional | website |
Linked from storefront footer |
2. Owner / admin account
The human who logs in as merchant owner.
| Field | Required? | Notes |
|---|---|---|
| Full name | ✅ | Shown on audit logs |
| Email (login) | ✅ | Invitation goes here — must be deliverable |
| Phone | recommended | |
| Preferred UI language (en / fr / de / lb) | ✅ | Drives the admin dashboard's language |
3. Each store / point of sale
Repeat per location. Feeds the stores table. The platform supports
multi-location from day one — even single-store merchants live in this
shape.
| Field | Required? | DB column | Notes |
|---|---|---|---|
| Store name | ✅ | name |
E.g. "Fashion Hub City Concorde" |
| Internal store code | ✅ | store_code |
UPPERCASE slug, no spaces — used in URLs (/store/FASHIONHUB). Pick once, hard to change |
| Subdomain | optional | subdomain |
E.g. fashionhub.rewardflow.lu for the storefront |
| Physical address | ✅ | business_address |
Per-location, overrides merchant address if different |
| Local contact email | optional | contact_email |
Overrides merchant-level if store has its own |
| Local phone | optional | contact_phone |
|
| Default tax rate % | optional | letzshop_default_tax_rate |
Defaults to 17% (LU standard VAT) |
| Default UI language | ✅ | default_language / dashboard_language |
What the staff terminal opens in |
| Customer-facing languages | ✅ | storefront_languages |
Subset of {en, fr, de, lb} for LU |
| Opening hours | optional | not in schema yet | Useful context for support |
4. Staff (per store, for each cashier)
Feeds users + store_users (invitation flow).
| Field | Required? | Notes |
|---|---|---|
| Full name | ✅ | Shown on the tablet PIN screen |
| ✅ if web access needed | Owner-only PIN-on-tablet operators don't need an account | |
| 4-digit PIN | ✅ | They pick it; you set it on /admin/loyalty/pins; staff types it on the tablet for every transaction |
| Employee ID / staff number | optional | Shown alongside name in audit trail |
| Role | ✅ | One of: merchant_owner, store_admin, store_staff |
5. Loyalty program — the business decisions
One program per merchant. Feeds the loyalty_programs table. This is
where most of the kickoff conversation happens — help them choose.
5.1 Program type
| Type | When to recommend |
|---|---|
| Stamps | "Buy 10, get 1 free" model. Cafés, hairdressers, lunch spots, anywhere with a clear unit of sale |
| Points | "Earn X points per €, redeem on rewards menu". Retail with varied basket sizes |
| Hybrid | Both at once. Rare for v1 — defer |
5.2 If stamps
| Field | Typical value | Notes |
|---|---|---|
stamps_target |
10 | Stamps to fill a card |
stamps_reward_description |
"Free coffee" | Customer-facing reward name |
stamps_reward_value_cents |
500 (= €5) | Internal value for analytics |
5.3 If points
| Field | Typical value | Notes |
|---|---|---|
points_per_euro |
1, 5, or 10 | The earn rate |
welcome_bonus_points |
50–100 | Awarded on enrollment, makes new customers feel rewarded |
minimum_purchase_cents |
500 (= €5) | Minimum sale to earn anything — kills chewing-gum farming |
minimum_redemption_points |
100 | Minimum points to redeem anything |
points_expiration_days |
365 | After this, unredeemed points lapse |
points_rewards (JSON list) |
see below | The reward menu |
Each entry in points_rewards needs: name, points_required,
description, value. Example:
[
{"name": "€5 voucher", "points_required": 100, "description": "Apply at checkout", "value": 500},
{"name": "€15 voucher", "points_required": 250, "description": "Apply at checkout", "value": 1500},
{"name": "Free product XYZ", "points_required": 400, "description": "Pickup in store", "value": null}
]
5.4 Anti-fraud knobs
| Field | Default | When to change |
|---|---|---|
cooldown_minutes |
0 | Set 30–60 for high-volume cafés to stop staff abuse |
max_daily_stamps |
10 | Lower if low-volume; this caps a single card's daily stamps |
require_staff_pin |
true | Recommended true for >1 staff member; tablet handles offline |
5.5 Cross-location behaviour
This is on the merchant settings (merchant_loyalty_settings), not the
program:
| Setting | Default | Notes |
|---|---|---|
allow_cross_location_redemption |
✅ on | Earn at Store A, redeem at Store B? Yes for chains, no for indies sharing infrastructure |
allow_void_transactions |
✅ on | Some merchants disable for fraud control |
require_order_reference |
off | Useful if integrating with their POS/till — links each loyalty transaction to a sale receipt |
6. Branding assets
Collect as files, not links — we host them.
| Asset | Format | Size | DB field |
|---|---|---|---|
| Primary logo | PNG, transparent bg | 600×600 min, square | logo_url |
| Hero banner | JPG / PNG | 1200×400 | hero_image_url |
| Brand primary color | Hex | e.g. #7C3AED |
card_color |
| Brand secondary color | Hex | e.g. #10B981 |
card_secondary_color |
| Card display name | Short string | e.g. "Fashion Hub Rewards" | card_name |
The logo doubles as the Google Wallet pass logo, so 600×600 minimum with transparent background is the practical floor — Google enforces clarity.
7. Compliance & GDPR
Required for go-live, especially in Luxembourg.
| Field | Required? | DB field | Notes |
|---|---|---|---|
| Privacy policy URL | ✅ EU | privacy_url |
Linked from enrollment form. Their site or your CMS |
| Loyalty T&C | ✅ | terms_text or terms_cms_page_slug |
Inline text or CMS page slug. Cover: how points work, expiry, when rewards can be revoked, contact for issues |
| DPO contact (if applicable) | depends on size | not in schema | LU rule: >5k customer records → designated DPO |
| Marketing consent | implicit | (enrollment form has the checkbox) | Required if you'll send birthday / re-engagement emails |
8. Tablets (if you provide the POS hardware)
| Question | Why |
|---|---|
| How many tablets per store? | Determines how many device pairings to set up |
| Existing Android tablets? Model + OS? | Need Android 8.0+ (API 26), Play Services. Fire OS won't work |
| Source tablets for them? | Lenovo Tab M11 or Samsung Galaxy Tab A9 (€100–200 each) — known good |
| Mounting? Counter stand or wall? | Affects power/charging plan |
| Wifi at each location | Offline queue handles brief outages, not all-day. Confirm wifi at the counter |
9. Initial data migration (nice-to-have)
| Question | Why |
|---|---|
| Existing loyalty customer list? | If they're switching from another system or paper cards, bulk-import via CSV (email, name, balance, enrollment date) |
| Approximate initial customer count? | Sizes capacity discussion |
| Current monthly transaction volume | Same |
10. Marketing material reuse (for the future marketing module)
Many of our early merchants are franchisees (e.g. fashion, food, beauty chains) and receive a steady stream of campaign material from their franchisor — banners, email templates, social posts, seasonal promotions. The future marketing module aims to republish this material at the local-store level: a franchisee shouldn't have to design their own birthday email when corporate already produces five a year.
At intake, ask permission-style questions so we have a green light before we build the ingestion pipeline. We are not asking them to do anything today — we're inventorying sources and getting written authorization.
10.1 Existing marketing material — what to inventory
| Question | Why we ask |
|---|---|
| Is the merchant a franchisee? Of which brand? | Determines which corporate sources may exist |
| Do they receive a regular email newsletter from the franchisor? Forward us 3–5 recent ones | Lets us assess content type, frequency, language mix, formatting |
| Is there a franchisor extranet / portal where campaigns are published? URL + their login (read-only) | The ideal source: structured, dated, multilingual |
| Social handles the franchisor posts under (Instagram, Facebook, LinkedIn) | Public, scrapeable, but low signal-to-noise |
| WhatsApp / Telegram broadcast groups they're part of | Sometimes the only channel — manual forwarding may be the only path |
| PDF / image packs received by email or download link | Common for seasonal campaigns; ask them to share the latest |
| Frequency: weekly / monthly / per-campaign | Sizes ingestion volume |
| Languages the franchisor publishes in | Influences whether we re-use or re-translate |
10.2 Reuse authorization — get this in writing
Before any scraping or republishing happens, the merchant must authorize us. Capture in the intake (a checkbox on the onboarding form or a clause in the merchant agreement):
- ✅ "I authorize {platform} to ingest marketing material I forward, share, or grant access to, and to republish it on my behalf via the loyalty/marketing channels offered by the platform."
- ✅ "I confirm I have the right (as franchisee) to use this material in local marketing, and that the franchisor's brand guidelines or licensing terms allow republication via third-party platforms."
- ✅ Forward us the franchisor's brand guidelines / co-op marketing policy if one exists. This determines what we can and can't do safely.
10.3 Practical capture paths (least-effort first)
For the future marketing module, in order of how easy they are to implement:
- Forward-to-inbox. Give the merchant a per-store email alias like
marketing+FASHIONHUB@rewardflow.lu. They forward any franchisor email there. We parse + categorize. - Drag-and-drop upload. A page where the merchant pastes a URL or uploads a PDF/image; we OCR/parse + queue for review.
- Authenticated extranet pull. They give us read-only credentials to the franchisor portal; we poll. Highest signal, highest legal bar — needs explicit franchisor clearance.
- Public social scrape. Last resort, only for material the franchisor publishes openly. Respect robots.txt and platform ToS.
10.4 Republish channels we can plug into
What we do with this material once captured:
- Storefront banner rotation (already exists in CMS)
- Loyalty welcome / re-engagement / birthday emails (loyalty module v1 — fixed templates; marketing module v2 — campaign-driven)
- SMS / WhatsApp (marketing module v2, deferred)
- Push notifications via the Wallet pass (Google Wallet supports this — class-level updates push to every customer's phone)
10.5 Red flags to surface back to the merchant
If during intake any of these come up, flag for legal review before we touch the material:
- Material contains the franchisor's trademarks in a way the franchisee doesn't have explicit license to use
- Material references third-party brands (co-promotions with other brands) — those brands didn't consent to our platform
- Material is in a language the merchant's customer base doesn't speak — we don't auto-translate without permission
- Personal data of named individuals (testimonials, photos) — GDPR consent chain may not extend to us
11. Test customer for verification
Before sending the merchant to real customers, run the 8 user-journey tests with two test accounts:
- The merchant owner's personal email
- A team member or your own email
The end-to-end test list lives in
app/modules/loyalty/docs/user-journeys.md.
Suggested fill-in order in the admin UI
The model has hard FK dependencies, so do it in this order:
- Create the merchant (
/admin/merchants/new) — needs § 1. - Invite the owner — needs § 2.
- Create the store(s) (
/admin/stores/new) — needs § 3. - Create the loyalty program (
/admin/loyalty/programs/new) — needs § 5 + § 6 + § 7. - Create staff PINs (
/admin/loyalty/pins/newper store) — needs § 4. - Configure cross-location settings
(
/admin/loyalty/settings/{merchant}) — needs § 5.5. - (Optional) bulk-import customers via CSV — needs § 9.
- Pair tablet(s) at each store (
/admin/loyalty/terminals/new, QR or manual) — needs § 8.
Reference
- Database models:
models/database/{merchants,stores,loyalty}.py - Loyalty production launch plan:
app/modules/loyalty/docs/production-launch-plan.md - Go-live readiness snapshot:
loyalty-go-live-readiness.md - User-journey E2E tests:
app/modules/loyalty/docs/user-journeys.md