Onboarding a new shop
The end-to-end process for taking a new bike shop from "signed contract" to "first transaction in Helm production." Targets 1-3 days for a clean shop, more for a complex AIM migration.
Drafted from planning · v0.1
This runbook will be templatized into scripts/onboard-shop.sh once we onboard our second shop and the patterns stabilize.
Pre-day-1 (sales conversation outputs)
Before we start the technical onboarding:
- Signed Master Services Agreement
- Shop slug agreed (e.g.,
mike-s-bikesfor Mike's Bikes — kebab-case) - Stripe Connect Standard onboarding initiated by shop
- Twilio number procured (Kvick handles)
- AIM data dump received from shop's prior system (or "no migration; greenfield")
- Hardware list confirmed: terminals, printers, scanners, drawer, tablets
Day 1: Cloudflare resources
# All in the Cloudflare hello@kvick.ca account
wrangler d1 create helm-{slug}-db
wrangler r2 bucket create helm-{slug}-assets
wrangler kv:namespace create "helm-{slug}-kv"
# Note the IDs returned; needed for wrangler.jsonc
Day 1: Repo branch
git switch main
git pull
git switch -c shops/{slug}
# Edit wrangler.jsonc — add env.{slug} block with the IDs
# Edit shop-overrides/{slug}/branding.json — colors, logo, copy
git commit -am "Onboard {slug}: bindings + branding"
git push -u origin shops/{slug}
# CI deploys
Day 1: Schema + seeds
wrangler d1 migrations apply helm-{slug}-db --remote
wrangler d1 execute helm-{slug}-db --remote --file=seed_permissions.sql
# Sets shop_config row, default roles, screens, role_permissions
# Creates auto Sys Admin staff with default PIN-reset code
Day 1: Hostname + Access
# Add the route in wrangler.jsonc env.{slug} (already done above)
# Verify DNS: helm-{slug}.kvick.bike resolves to Workers
# Optional: add Cloudflare Access policy if shop owner wants extra gating
Day 1-2: AIM migration (if applicable)
# Dry run first — local
python migrate_aim.py --all --target=local
# Verify counts + spot checks
python verify_schema.py --shop {slug}
# Owner reviews; signs off
# Cutover — point at production D1
python migrate_aim.py --all --target=remote --shop {slug}
Day 1: Owner sign-in
The shop owner signs in:
- Visits
https://helm-{slug}.kvick.bike - Sign-in overlay appears
- Enters PIN-reset admin code (currently
466687); signs in as Sys Admin - Sets their own PIN
- Adds initial staff with their PINs
Day 2: Stripe Terminal hardware setup
- Pair the BBPOS WisePOS E reader (per Stripe's provisioning flow)
- Register the reader in the shop's Stripe account (Reader Locations, etc.)
- Test charge $1 + refund
Day 2: Twilio sub-account
- Provision Twilio sub-account
- Attach the procured phone number
- Configure Helm secret bindings:
TWILIO_SUB_ACCOUNT_SID,TWILIO_AUTH_TOKEN(stored viawrangler secret put --env {slug}) - Test SMS to shop owner's phone
Day 2-3: Training
- Walk owner through Today / Customers / Service / Sales (whatever is built)
- Show the in-situ editing pattern (the helm-editable dashed-underline cells + composite-till modals)
- Show audit log
- Show data export (Settings → Data → Export)
- Hand over runbooks for daily ops
Day 3+: Shadow week
The shop runs Helm alongside their old system for 5-7 business days. Daily reconciliation cron compares totals. Discrepancies investigated; usually data quality on the migrated side.
After shadow week is clean, old system is read-only. Helm becomes primary.
Cutover checklist
- All Helm endpoints respond OK from the shop's Wi-Fi
- Owner has signed in and set PINs for all staff
- At least one test sale rung (cash + card) and refunded
- At least one service ticket dropped off and cashed out
- Daily backup cron has run successfully once
- Audit chain verifies clean
- R2 bucket has at least one receipt PDF
- Owner has the export bundle download URL