Lalabase
Tickets, Doku, Zeiterfassung und Kundengespräche an einem Ort, durchsuchbar per KI. Für Softwarefirmen mit Kundenprojekten. Open Source: auf deinem eigenen Server oder in unserer Cloud in Deutschland.
Translation pending
QR-code pages for cafés, bars and restaurants: menus, Wi-Fi, loyalty cards and feedback.
Translation pending
BusinessTypeScript · CSSGitHub @nexloop-teamOnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
No updates detected yet. GitHub releases will appear here.
Owners of public repositories under a personal GitHub account can sign in to verify ownership and manage the introduction themselves. No administrator approval is needed.
Sign in, generate a verification file, and commit it to this repository’s default branch within 30 minutes. Files on other branches or forks do not count. Then check the file below to manage the project without administrator approval. Keep the file to retain your access.
If needed, you will go to sign-in. After signing in, return here and press your chosen verification button again.
Run /nomorevibe verify in your project folder to verify ownership and manage the introduction yourself.
Verify in one minuteAsk about other verification options
Loading the full README. The saved excerpt is shown below.
Tapmore — self-serve QR pages for hospitality venues
Venue owners sign up, answer four onboarding questions and get a live guest page with printable QR codes, all without help from us. A guest who scans a table QR code gets the menu, Wi-Fi, a loyalty card that staff stamp at the till, a private feedback box with a Google review invitation for every guest, and a Sudoku to pass the time. Owners manage everything from a dashboard and pay one yearly subscription per venue. Guest-page behaviour is modelled on the analysis in docs/candour-analysis.md, with our own brand, copy and backend.Read the full README ↗Owners of cafés, restaurants, shops, showrooms, pharmacies, salons, clinics, gyms and other local businesses sign up, go through a five-step setup and get a live customer page with printable QR codes, all without help from us. A customer who scans a QR code gets the menu (or the products or services), Wi-Fi, a loyalty card that staff stamp at the till, a private feedback box with a Google review invitation for every customer, and a Sudoku to pass the time.
The business type changes only the wording and defaults (business.ts): a shop gets "Products", "Counter 1" and "customers"; a salon gets "Services" and a booking link; food businesses keep veg marks, allergens and calories, which other types never show. Data, routes and code names stay venue / menu / table throughout. Owners manage everything from a dashboard, can connect their Google Business Profile so every Google review gets a reply (AI-drafted, posted automatically or after approval), and pay one yearly subscription per venue. Guest-page behaviour is modelled on the analysis in docs/candour-analysis.md, with our own brand, copy and backend.
Plans, to-dos, decisions and the change log live in docs/ROADMAP.md.
pnpm install
pnpm dev # http://localhost:3000
pnpm test # unit tests (vitest)
pnpm lint
pnpm build && pnpm start
Requires Node 22+. The database is PostgreSQL:
./data/pg. Uploaded images and feedback photos go to ./data/ too (override with DATA_DIR). Only one process can open that local database at a time.DATABASE_URL set, it connects to that server instead: Supabase, Neon, AWS RDS or your own Postgres. Moving between hosts is a change of that one URL plus a data copy (pg_dump / pg_restore).Migrations run automatically on start; restart the dev server after pulling a schema change.
/signup and create an account. You land in the setup wizard (business, menu or products, look, online, rewards), and the venue starts on a 7-day free trial (no card). Items can be typed in with categories, imported from photos with AI, or linked from another website.outbox table and the server log, so copy the verification or reset link from there./admin, put your email in ADMIN_EMAILS and verify it.Five demo venues are seeded on first start (free access, no owner; hidden from the admin console and its numbers):
| URL | Shows |
|---|---|
/s?i=demo | Stamp card with reward tiers, hosted menu, plain Wi-Fi, Sudoku |
/s?i=demo-crm | Honeycomb Café: Bento tiles on White & Honey, Indian menu (₹, veg / non-veg marks), Wi-Fi email gate, birthday ask |
/s?i=demo-rewards | Rewards-only membership, external ordering link, custom link cards |
/s?i=demo-salon | Glow Salon: services with "From" prices, booking link, a stamp per visit |
/s?i=demo-shop | Sharma General Store: featured products and offers, a stamp per ₹500 spent |
Rewards come in three kinds: a stamp card (optionally with refer-a-friend stamps), a rewards-only members' club, or refer a friend only, with no stamps. With refer a friend only, a member shares their link; the friend gets the owner's offer (e.g. 10% off) on their first visit, and the member gets theirs once the friend has been in. Staff give both from the till ("Give welcome offer", "Give friend reward").
One plan, billed per venue: ₹999 a year, with a 7-day free trial and no card needed. Every feature is included. No GST is charged while the business isn't GST-registered; once it is, set CHARGES_GST = true in src/lib/plans.ts (prices then show ₹999 + 18% GST, ₹1,178.82 in total) and set GSTIN.
When a venue has neither a paid subscription nor trial days left, its guest page (and everything guests reach through it: menu, loyalty card, till stamping) goes offline. The owner can still sign in, edit and subscribe, and the page comes back exactly as it was. A failed renewal keeps the page up while Razorpay retries; it goes offline when Razorpay halts the subscription. Prices and the access rules live in src/lib/plans.ts.
Every email is plain text from MAIL_FROM and is copied to the outbox table (kept 90 days; with Resend on, private links in the copy are blanked out). There are no marketing emails.
| Sent when | |
|---|---|
| Confirm your email | Sign-up, or a new link requested |
| Reset your password | "Forgot password" |
| You're invited to the till | An owner invites a staff member |
| Reconnect Google | A venue's Google access stops working (once) |
| Your {venue} rewards card | A guest joins the card (on the page or at the till), or a returning member asks for their card again |
| Your {reward} is ready | A stamp completes a reward (once per reward per day) |
| Happy birthday from {venue} | On a member's birthday, in the venue's daytime, while its birthday reward is on (with an unsubscribe link) |
Payment receipts and renewal notices come from Razorpay. Resend's free plan sends 100 emails a day, roughly 20 busy venues; upgrade before that.
DATABASE_URL to your Postgres.
postgres://user:pass@host:5432/tapmore, plus DATABASE_SSL=off if it has no TLS.DATABASE_SSL_CA to the provider's CA (PEM text or a file path; Supabase: Project Settings → Database → SSL Configuration), or DATABASE_SSL=verify for a publicly signed certificate. Without either the link is encrypted but unchecked, and startup warns.S3_* variables to a bucket.
tapmore → Storage settings → S3 access keys. Use the endpoint shown there (https://<project>.supabase.co/storage/v1/s3) and the project's region.S3_*, images stay on the server's disk under DATA_DIR. That needs a persistent volume, so it won't work on serverless hosts.node --env-file=.env.local scripts/check-connections.mjs.rate_limits table), so they hold across any number of instances, Vercel included. They read the visitor's address from the proxy's end of X-Forwarded-For: set TRUSTED_PROXY_COUNT=2 with Cloudflare in front of the host, or CLIENT_IP_HEADER to a header your CDN sets.APP_URL to your public origin (e.g. https://www.tapmore.in). It's encoded into every QR code, so it must never change.| Env var | Purpose |
|---|---|
APP_URL | Public origin encoded into QR codes and links (falls back to the request host) |
NEXT_PUBLIC_SUPPORT_EMAIL | Support address on the help page, policies and emails (build time) |
LEGAL_ADDRESS, LEGAL_JURISDICTION_CITY, SUPPORT_PHONE | Business details on the legal and contact pages (required in production) |
LEGAL_ENTITY_NAME, LEGAL_ENTITY_TYPE | The business that runs Tapmore, e.g. Tapmore + a partnership firm. Shown in the legal fine print, the Contact page and on payments (required in production) |
LEGAL_REGISTRATION | Optional: Udyam number, firm registration, or later CIN / LLPIN, printed next to the business name |
SUPPORT_HOURS, GSTIN | Optional: support hours (default Mon–Sat 10–6 IST), GSTIN once registered |
GRIEVANCE_OFFICER_NAME, GRIEVANCE_OFFICER_EMAIL | The Grievance Officer named on the Contact and privacy pages (email defaults to the support address); startup warns until set |
DATABASE_URL | PostgreSQL connection string (Supabase, Neon, RDS, self-hosted). Without it, an embedded Postgres in DATA_DIR/pg is used |
DATABASE_SSL | off for a server without TLS (localhost is detected automatically); verify to check a publicly signed certificate |
DATABASE_SSL_CA | The database's CA certificate (PEM text or file path), so its identity is checked |
DATABASE_POOL_SIZE | Connections per server (default 5) |
S3_BUCKET, S3_ENDPOINT, S3_REGION, S3_ACCESS_KEY_ID, S3_SECRET_ACCESS_KEY | Store images and feedback photos in any S3-compatible bucket (Supabase Storage, MinIO, R2, AWS). Without them they're saved under DATA_DIR |
DATA_DIR | Local folder for the embedded database and, without S3_*, images and feedback photos (default ./data) |
Owners
| Path | What |
|---|---|
/ | Marketing page with pricing and demos |
/signup, /login, /forgot-password, /reset-password | Accounts (email + password) |
/onboarding | Five-step setup wizard that creates a venue (business, menu or products, look, online, rewards) |
/dashboard | Venue list (or straight into your only venue) |
/dashboard/<venue> | Overview: scans, guests, feedback, setup checklist |
/dashboard/<venue>/design, /menu, /loyalty | Editors for the guest page, menus and loyalty/capture |
/dashboard/<venue>/guests, /feedback | Guest list (with CSV export) and feedback inbox |
/dashboard/<venue>/reviews | Google reviews: connect the Business Profile, reply settings, drafts to approve, replies posted |
/dashboard/<venue>/qr, /qr/print | QR downloads (PNG/SVG, per-table sources) and printable table cards |
/dashboard/<venue>/billing, /settings | Plan, venue details, page address, delete venue |
/dashboard/account | Name, password, delete account |
/admin | Operator view: all accounts and venues, suspend, extend trials, give free access, cancel a renewal, delete a venue, revenue, account actions (resend emails, reset link, block, delete) and an activity log of every admin change |
/staff, /staff/stamp?c=<card> | Till screens for paired staff devices: find a member, add stamps, redeem rewards, undo |
Guests
| Path | What |
|---|---|
/s?i=<code>&s=<source> | Landing page (server-rendered, venue theme applied before first paint). cta=box|anon forces the feedback-card A/B variant. |
/menu?i=<code> | Hosted menu, products or services: search, section jump bar; food businesses also get the allergen filter and dietary tags. Each business group has its own looks (cards with photos or a printed menu; a product grid or a simple list; a service list or a rate card), and shops and services can add "Ask/Book on WhatsApp" buttons |
/card/<id>?t=<token> | Member's web card (linked from the enrolment email), where they can also delete their card and details |
/consent?token= | Old offers-confirmation links (offers consent is no longer asked; joining is agreeing to the membership) |
/privacy, /terms, /refunds, /shipping, /contact | Privacy policy (DPDP), terms, cancellation and refunds, delivery policy, contact |
/unsubscribe?token= | Stop birthday emails from one venue (also one-click from mail apps) |
/media/<venue>/<file> | Images uploaded by owners |
APIs: api/auth/*, api/account, api/dashboard/venues/* and api/admin/* are cookie-authenticated, and their writes are refused from other origins. api/billing/webhook is signed by Razorpay. api/google/connect and api/google/callback run the Google sign-in (the callback checks a one-time state token bound to the owner and venue); api/google/notifications takes Pub/Sub pushes and checks GOOGLE_PUSH_TOKEN. The guest APIs (api/venues, api/loyalty, api/feedback, api/guests, api/consent, api/events) need no sign-in and are rate limited.
sessions. A password reset or change signs out other devices.src/proxy.ts only does an early redirect to the login page.src/lib/venue/schema.ts): http(s) links only, hex colours only, length limits.src/server/crypto.ts). Disconnecting revokes the token at Google and deletes every review and reply saved for the venue. Review text goes to the AI with an instruction to treat it as data, and pre-connection reviews are never answered automatically.src/app/ holds the routes: pages and api/* handlers.src/components/landing/ holds the guest page. src/components/dashboard/ holds the owner UI: editors, wizard and shell.src/lib/ holds pure logic shared by client and server: plans, venue schema and entitlements, theme, feature rules, validation, i18n, sudoku, API contracts.src/server/ holds the database, auth, repositories and services. Only route handlers and server pages import it.RESEND_API_KEYMAIL_FROMRAZORPAY_PLAN_ID.RAZORPAY_KEY_ID and RAZORPAY_KEY_SECRET.https://<your-domain>/api/billing/webhook for the subscription.* events (activated, charged, pending, halted, cancelled, completed, paused, resumed), and set RAZORPAY_WEBHOOK_SECRET.ADMIN_EMAILS to your own address(es), and NEXT_PUBLIC_SUPPORT_EMAIL to the address owners and guests should write to (read at build time, so rebuild after changing it).pg_dump.DATA_DIR if you kept them on disk./terms, /privacy (DPDP Act 2023 notice for guests and owners), /refunds (cancellation and refunds), /shipping (delivery policy for an online service) and /contact (business details and where to send complaints). Razorpay checks all five before activating payments.POLICIES_UPDATED in config/legal.ts whenever a policy's wording changes.GSTIN then.SENTRY_DSN (optionally SENTRY_TRACES_SAMPLE_RATE, default 0.1). Server only: query strings, cookies, headers and request bodies are stripped before anything is sent.Server-Timing header (database time, query count, total), shown in the browser's network panel.https://www.googleapis.com/auth/business.manage, and a Web client whose redirect URI is https://<your-domain>/api/google/callback. Set GOOGLE_OAUTH_CLIENT_ID, GOOGLE_OAUTH_CLIENT_SECRET, and TOKEN_ENCRYPTION_KEY (openssl rand -base64 32; changing it disconnects every venue).[email protected] as Publisher, and a push subscription to https://<your-domain>/api/google/notifications?token=<GOOGLE_PUSH_TOKEN>. Set GOOGLE_PUBSUB_TOPIC (projects/<project>/topics/<topic>) and GOOGLE_PUSH_TOKEN. Without them a daily job checks instead.ADMIN_EMAILS |
Comma-separated super admins (the email must be verified). They can make other accounts admins from /admin/accounts |
RESEND_API_KEY | Sends email through Resend; without it emails only go to the outbox table and log |
MAIL_FROM | Sender for all emails, e.g. Tapmore <[email protected]> (an address on the domain verified in Resend) |
RAZORPAY_KEY_ID, RAZORPAY_KEY_SECRET, RAZORPAY_PLAN_ID | Razorpay Subscriptions for the yearly plan |
RAZORPAY_WEBHOOK_SECRET | Verifies /api/billing/webhook |
BILLING_DEV_MODE | 1 allows dev-mode billing in a production build (for staging only) |
GROQ_API_KEY | Turns on AI menu import (photos, up to 3 at a time) and "What's this?" drafts using Groq; used first when set |
GROQ_TEXT_MODEL, GROQ_VISION_MODEL | Groq models (defaults openai/gpt-oss-120b for text, qwen/qwen3.8-27b for menu photos) |
ANTHROPIC_API_KEY | Alternative AI provider (Claude); also reads PDF menus and up to 5 photos |
ANTHROPIC_MODEL | Claude model (default claude-opus-5-5) |
AI_PROVIDER | groq or anthropic, to choose when both keys are set |
NEXT_PUBLIC_TURNSTILE_SITE_KEY, TURNSTILE_SECRET_KEY | Cloudflare Turnstile captcha on sign-up and password reset; off without them |
TRUSTED_PROXY_COUNT, CLIENT_IP_HEADER | Where rate limits read the visitor's address (default: the last X-Forwarded-For entry) |
CRON_SECRET | Enables POST /api/cron/run for an external scheduler (Authorization: Bearer …) |
DISABLE_JOBS | 1 stops the in-process hourly job ticker (birthday emails, daily clean-up), e.g. when running more than one server. On Vercel it's always off: vercel.json runs the jobs daily at 09:00 UTC through Vercel Cron, which needs CRON_SECRET set |
NEXT_PUBLIC_BRAND_NAME | Product name in the footer and titles (default "Tapmore") |
GOOGLE_OAUTH_CLIENT_ID, GOOGLE_OAUTH_CLIENT_SECRET | Google OAuth client for Google review replies (Business Profile); the feature is hidden without them |
TOKEN_ENCRYPTION_KEY | 32 random bytes, base64: encrypts owners' Google access at rest |
GOOGLE_PUBSUB_TOPIC, GOOGLE_PUSH_TOKEN | Pub/Sub topic for new-review alerts, and the secret in its push URL; without them reviews are checked daily |
GOOGLE_NL_API_KEY | Optional: Google Cloud Natural Language for sentiment (a built-in lexicon is used otherwise) |