Client Onboarding
End-to-end runbook for bringing a new client from zero to live on LayerKick. Designed to be walked through with Claude — steps are marked as human (requires manual action) or AI (Claude handles it).
Before you start
Section titled “Before you start”Gather this information about the client:
| Field | Example | Notes |
|---|---|---|
| Client name | Bare Babe | Display name |
| Slug | brbb | 4-letter abbreviation |
| Shopify domain | bare-babe.myshopify.com | The .myshopify.com domain |
| Theme ID | 154775126243 | Numeric ID from Shopify admin → Themes |
| Store password | (if applicable) | For password-protected stores |
You’ll also need:
- Cloudflare API token with KV + D1 + Workers permissions
- Shopify app credentials (client ID, client secret) — from the LayerKick Shopify app in Partners
- Access to the WorkOS dashboard for user invites
Phase 1: Platform Registration
Section titled “Phase 1: Platform Registration”Step 1 — Insert client into production D1 [AI]
Section titled “Step 1 — Insert client into production D1 [AI]”Create the client row in the layerkick-config D1 database with status onboarding:
eval "$(mise env)"npx wrangler d1 execute layerkick-config --remote \ --command="INSERT INTO clients ( slug, name, status, myshopify_domain, admin_store, shopify_origin, app_client_id, app_client_secret, templates_json, features_json, ab_json ) VALUES ( '<slug>', '<Client Name>', 'onboarding', '<store>.myshopify.com', '<store>.myshopify.com', 'https://<store>.myshopify.com', '<shopify_app_client_id>', '<shopify_app_client_secret>', '[]', '{}', '{}' )"Templates start as [] — everything proxies to Shopify until templates are explicitly enabled.
Step 2 — Insert domain row [AI]
Section titled “Step 2 — Insert domain row [AI]”npx wrangler d1 execute layerkick-config --remote \ --command="INSERT INTO domains (hostname, slug) VALUES ('<slug>.layerkick.com', '<slug>')"Step 3 — Shopify OAuth [Human]
Section titled “Step 3 — Shopify OAuth [Human]”The client needs to authorize the LayerKick app on their Shopify store:
- Send the client the install URL:
https://app.layerkick.com?shop=<store>.myshopify.com - They authorize the app — this saves
access_tokento D1 automatically - Verify the token was saved:
Terminal window npx wrangler d1 execute layerkick-config --remote \--command="SELECT access_token IS NOT NULL as has_token FROM clients WHERE slug = '<slug>'" --json
If doing this yourself (you have Shopify admin access), just click through the OAuth flow directly.
Phase 2: Theme Pull & Transpile
Section titled “Phase 2: Theme Pull & Transpile”Step 4 — Download the Shopify theme [AI]
Section titled “Step 4 — Download the Shopify theme [AI]”Pull the live theme’s Liquid files into the transpiler’s theme directory:
eval "$(mise env)"pnpm pull:theme --slug=<slug> --theme-id=<themeId>This downloads all .liquid files (sections, snippets, blocks, layout, templates, config) into packages/transpiler/themes/<slug>/<themeId>/liquid/.
Step 5 — Create .mise.local.toml [AI]
Section titled “Step 5 — Create .mise.local.toml [AI]”Create (or update) the repo-root .mise.local.toml:
[env]CLIENT_SLUG = "<slug>"THEME_ID = "<themeId>"# WT_PORT_OFFSET = "50" # if using a worktreeThen load into the shell:
eval "$(mise env)"Step 6 — Create client config directory [AI]
Section titled “Step 6 — Create client config directory [AI]”mkdir -p clients/<slug>Create clients/<slug>/ssr.json with an empty config to start:
{}Step 7 — Build the registry shim [AI]
Section titled “Step 7 — Build the registry shim [AI]”Transpile all Liquid to TS and generate the component registry:
pnpm build:shimThis takes ~2-3 minutes. Generates registry.ts that maps section/snippet names to render functions.
Phase 3: Local Dev & Data
Section titled “Phase 3: Local Dev & Data”Step 8 — Start local dev server [AI]
Section titled “Step 8 — Start local dev server [AI]”pnpm dev --resetThis starts the renderer (port 8787) + dashboard (port 3000) + liquid watcher, and seeds local D1/KV from scratch.
Step 9 — Seed local KV with Shopify data [AI]
Section titled “Step 9 — Seed local KV with Shopify data [AI]”Build a fetch manifest by scanning transpiled sections for metafield access patterns, then seed:
pnpm seed:kv --slug=<slug>If specific metafields are needed:
pnpm seed:kv --slug=<slug> --manifest='{"products":{"metafields":[{"namespace":"<ns>","key":"<key>"}]}}'Step 10 — Verify locally [Human]
Section titled “Step 10 — Verify locally [Human]”Check that pages render at http://localhost:8787:
- Homepage (
/) - A collection page (
/collections/<handle>) - A product page (
/products/<handle>) - A content page (
/pages/<handle>)
At this stage everything proxies to Shopify — confirm the proxy path works before deploying.
Phase 4: Deploy (Proxy-First)
Section titled “Phase 4: Deploy (Proxy-First)”Step 11 — Deploy theme worker [AI]
Section titled “Step 11 — Deploy theme worker [AI]”Build and deploy the theme worker, promoting it as the production deploy:
eval "$(mise env)"pnpm deploy:theme <slug> --promoteThis creates a worker named <slug>-<adj>-<noun> (e.g., brbb-silver-pine), uploads the bundle + KV data, and sets it as the production pointer in D1.
Step 12 — Deploy dispatch worker [AI]
Section titled “Step 12 — Deploy dispatch worker [AI]”Deploy the dispatch (edge router) worker and set up the custom domain:
pnpm deploy:dispatch <slug>This creates <slug>-dispatch, sets up the <slug>.layerkick.com route, and configures service bindings to the theme worker.
Step 13 — Hydrate production KV [AI]
Section titled “Step 13 — Hydrate production KV [AI]”Seed production KV with all Shopify data (products, collections, pages, blogs, menus):
pnpm hydrate:kv --slug=<slug>Step 14 — Register webhooks [AI]
Section titled “Step 14 — Register webhooks [AI]”Register Shopify webhooks so product/collection/theme changes auto-invalidate KV:
pnpm register:webhooks --slug=<slug>Step 15 — Verify proxy is live [Human]
Section titled “Step 15 — Verify proxy is live [Human]”curl -sI "https://<slug>.layerkick.com/" | grep -E "x-rendered-by|HTTP/"Should show x-rendered-by: layerkick-proxy — meaning all traffic is proxied to Shopify through our edge. The site should look identical to the Shopify store.
Phase 5: Client Access & Status
Section titled “Phase 5: Client Access & Status”Step 16 — Update status to ready [AI]
Section titled “Step 16 — Update status to ready [AI]”npx wrangler d1 execute layerkick-config --remote \ --command="UPDATE clients SET status = 'ready' WHERE slug = '<slug>'"Step 17 — Invite client to dashboard [Human]
Section titled “Step 17 — Invite client to dashboard [Human]”- Go to the WorkOS Dashboard → Users → Send Invitation
- Enter the client’s email address
- Once they sign up, get their WorkOS user ID (
user_...) - Grant them access to their store:
Terminal window npx wrangler kv put "user-store:<user_id>" '["<slug>"]' \--binding LAYERKICK_KV --preview false - They can now log in at
https://app.layerkick.comand see their dashboard
Step 18 — Confirm with client [Human]
Section titled “Step 18 — Confirm with client [Human]”Let the client know:
- Their dashboard is live at
https://app.layerkick.com - Their preview site is at
https://<slug>.layerkick.com - All traffic is currently proxied through to Shopify (no rendering changes yet)
Phase 6: Template Enablement (Later)
Section titled “Phase 6: Template Enablement (Later)”Once parity is verified for a template type, enable it for rendering:
Enable a template [AI]
Section titled “Enable a template [AI]”# 1. Check current whitelistnpx wrangler d1 execute layerkick-config --remote \ --command="SELECT templates_json FROM clients WHERE slug = '<slug>'" --json \ | jq '.[0].results[0].templates_json | fromjson'
# 2. Add template (use full suffixed name)npx wrangler d1 execute layerkick-config --remote \ --command="UPDATE clients SET templates_json = '[\"index\"]' WHERE slug = '<slug>'"
# 3. Deploy theme worker (if code changed)pnpm deploy:theme <slug> --promote
# 4. Re-seed KV for affected content typepnpm hydrate:kv --slug=<slug> --only=pages
# 5. Purge CDN cachecurl -s -X POST "https://api.cloudflare.com/client/v4/zones/c50da9d954b9ef97d2b53f4db0e21d1c/purge_cache" \ -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ -d '{"hosts":["<slug>.layerkick.com"]}' | jq .success
# 6. Verify (wait 60s for D1 cache TTL)curl -sI "https://<slug>.layerkick.com/" | grep x-rendered-by# Should show: layerkick (NOT layerkick-proxy)Use the full template name with suffix — e.g., "product.ge-optimized-color" not "product". Base templates like "page", "index", "article" don’t have suffixes.
Quick Reference
Section titled “Quick Reference”| Phase | Steps | Who | Duration |
|---|---|---|---|
| Registration | 1-3 | AI + Human (OAuth) | ~10 min |
| Theme & Build | 4-7 | AI | ~5 min |
| Local Dev | 8-10 | AI + Human (verify) | ~5 min |
| Deploy | 11-15 | AI + Human (verify) | ~10 min |
| Client Access | 16-18 | AI + Human (invite) | ~5 min |
| Template Enable | Per template | AI | ~5 min each |