Part of the Taskly reference library — see README.md for the doc index. This is the end-to-end product spec: what Taskly is, who uses it, and every feature along both journeys, as actually shipped in code. Where a marketing doc describes an aspiration, this file describes the running product. Canonical tie-breakers on conflict: ARCHITECTURE.md (identity/payments system-of-record), BUSINESS-MODEL.md (operating model), BRAND-VOICE.md §5 (honesty boundary).
The get-it-done network for the Greater Toronto Area. Taskly is a two-sided home-services task marketplace: customers describe a task in plain words, vetted Taskers make offers, and an accepted offer becomes an escrow-held booking. Taskly keeps a flat 20% commission on completed jobs; the Tasker keeps ~80%. A curated set of fixed-price "vetted flows" sits alongside the open marketplace in code but is dark at launch (see §2.4).
One Next.js 16 app serves every surface — landing pages, the post-a-task funnel, the customer dashboard, the Tasker workspace, and the admin console — gated by Supabase auth + row-level security, paid through Stripe Connect, and assisted throughout by a Groq LLM layer that never dead-ends the user.
- Who uses Taskly
- Core concepts
- The Customer (Poster) journey
- The Tasker journey
- The shared job lifecycle
- Money, fees & escrow
- The AI layer
- Trust, safety & moderation
- Notifications & email
- Community (Spaces)
- Admin console & operations
- Honesty guardrails — what Taskly does & doesn't claim
- Feature index
Taskly has one account type with three faces:
| Persona | Who they are | Where they live in the app |
|---|---|---|
| Poster (Customer) | Anyone with a task to get done. Every member is a customer by default. | /app/** — the customer dashboard |
| Tasker | A member who has been reviewed and approved to earn. "Tasker" is a capability, not a separate account — any customer can opt in. | /provider/** — the Tasker workspace |
| Admin | Taskly staff who review tasks & Taskers, resolve disputes, and run ops. | /admin/** — the admin console |
A single person can be both a poster and a Tasker on one login — they post tasks in /app and earn in /provider. The only hard fork is admin, who is bounced out of the customer/tasker shells.
profiles.role is only customer or admin. The old provider role was collapsed into customer (migration 042). "Tasker" is a capability derived from a provider_profiles row, never a role.
getTaskerStatus(supabase, userId)→none | pending | approved | suspended | rejected(lib/api/auth.ts).getCapabilities()is the single source of truth used app-wide (via theuseCapabilitieshook):{ role, isAdmin, isMember, isTasker, taskerStatus, canOffer, requireApproval, identityFeePaid, identityStatus }.- Three independent gates that are easy to conflate:
- Approval — can this person appear as a Tasker & make live offers? (needs a profile photo + at least one skill).
- Identity — Stripe Identity gov-ID + selfie ($5 fee, waived for Founding Taskers). Gates payout, not approval.
- Payout rail — a Stripe Connect Express account with
payouts_enabled.
user_metadatais only for cosmetic/onboarding flags — never trusted for gating.
The primary, live product path:
Customer posts a task → admin approves it → it goes live on the feed
→ Taskers make offers (bids) → customer picks one & pays into escrow
→ Tasker does the work (door-OTP verified) → customer confirms
→ escrow releases 80% to the Tasker, Taskly keeps 20%
Backed by the tasks + task_bids tables and lib/marketplace/**. All money is integer cents.
Bidding's fatal flaw is a poster who waits and gets no offers. Taskly solves it with a hybrid model (lib/marketplace/pricing-mode.ts):
- Task > 2 days out: opens for offers; auto-converts to a fixed price for the final 2 days (fixed price = median of real offers received). The customer can accept any offer anytime.
- Task ≤ 2 days out / ASAP: straight to fixed price (no time to bid).
- An always-present customer toggle: Compare offers vs Book now at fixed price.
Launch note: the auto-convert cron is currently disabled — every posted task stays
pricing_mode='offers'and open for offers. The founder concierges/hand-matches the fixed-phase long tail until first-come-first-served liquidity exists.
- Open marketplace (
tasks/task_bids) — free-text tasks, poster picks one offer. This is the live launch experience. - Curated fixed-price "vetted flows" (UC) (
lib/booking/**, the/bookwizard) — 9 curated verticals with catalog pricing. Gated behindUC_ENABLED(lib/flags.ts), which is OFF. Both curated surfacesredirect('/app')at launch. Documented here for completeness but not part of the live journey.
The 9 curated verticals (used live only for the AI taxonomy roll-up and Tasker skill selection): Cleaning, Maintenance/Handyman, Cooking, Seasonal, PSW for Seniors, Tutoring, Organizing, Salon & Spa, Pet Care.
The home page (app/page.tsx) is an Airtasker-style "post any task" landing: a HeroPostTask "describe your task" entry, a live TaskFeed for social proof, HowItWorks, SafeByDesign, MeetOurPros, an EarnAsTasker cross-sell, and a closing CTA. Signed-in users are redirected to their dashboard (with a /?home escape hatch).
Supporting marketing routes: the-taskly-advantages, pricing / cost / price-guide, how-it-works, browse-tasks (public feed preview), reviews, compare, services, posters, blog, about, contact, support, plus legal (terms, privacy) and SEO landing directories.
One page (app/login) handles every auth state. Default tab is Create Account (most arrivals are new posters). Copy adapts when the user is mid-post ("Your task is saved — create a free account to post it live").
Methods:
- Email + password →
/api/auth/signuprequires full name, a Cloudflare Turnstile token, and a Terms/Privacy consent checkbox → a 6-digit OTP emailed and verified in-app. - Google OAuth →
/auth/callback(PKCE). - Passwordless magic code — "Email me a code instead" (also works for WhatsApp-bot-created accounts).
- Forgot password — 6-digit recovery code → set a new password.
Deep-link niceties: ?email= pre-fills, ?next / ?redirect round-trips through the whole auth flow, expired links surface a "send a fresh code" nudge. On signup a profiles row + customer_profiles row are auto-created by a DB trigger. Post-auth routing: explicit ?next wins, else admin → /admin, Tasker → /provider, everyone else → /app.
A 5-step wizard (PostTaskProvider + steps/Step*.tsx). The draft persists to localStorage (24h TTL) so it survives a refresh and the anonymous→login round-trip; the step lives in the URL (?step=N) so browser Back works; every step emits funnel analytics (/admin/funnels).
Validation floors: title 6–100 chars, description 25–6000 chars, budget $10–$50,000 (all cents).
| Step | Screen | What's collected | AI assistance (all degrade gracefully) |
|---|---|---|---|
| 0 | Your task (StepDescribe) |
Title, description, up to 6 photos (≤20MB, task-photos bucket) |
AI description draft (/api/suggest-description), Gmail-style inline ghost-text (/api/complete-description), one-tap "Clean up my words" rewrite (/api/enhance-task), tap-select scope pills (/api/suggest-scope), live category classification. AI image generation ("Nano Banana", /api/generate-image) offered when the task needs a photo and none is attached. |
| 1 | Category & skills (StepCategorySkills) |
Category + skills (≤12), pre-filled from AI, editable, optional | Live AI label wins until the poster picks manually |
| 2 | Where & when (StepWhereWhen) |
Mode (in-person/remote), address autocomplete (lat/lng fuzzed to ~110m) OR "flexible location" OR remote; timing (asap/flexible/on_date/before_date) + date + optional time window |
Mode auto-prefilled from category |
| 3 | Your budget (StepBudget) — the USP screen |
Budget (AI-seeded, never overwrites a poster-set amount); boost toggles | Live market estimate as a low–high band (/api/suggest-budget → /api/predict-price with a "why this price" breakdown) |
| 4 | Review & post (StepReview) |
Final editable everything + a live preview card | Inline /api/enhance-task with Undo |
Step-3 boost toggles: Urgent (+$5, ⚡ badge, collected only on hire so posting stays free), Tasker brings materials (educational, no fee), Licensed professional required, Repeat booking welcome, Photo proof required (before/after photos gate escrow release).
Autopost across auth: an anonymous poster who hits "Post it live" is sent to login and the wizard auto-completes the post on return.
On submit (POST /api/tasks): client + server content moderation (see §8), contact-info stripping, length/budget validation, UTM attribution stamping, and a computed poster_name ("First L."). The task lands in the tasks table with status='pending_review' — hidden from the feed until an admin approves it (→ open). This is a deliberate divergence from older docs: posts are curated before they go live.
The success screen offers a referral nudge and an SMS-updates opt-in.
At launch the dashboard renders PosterHome:
TaskComposer— a ChatGPT-style auto-growing textarea with a rotating placeholder, an "Enhance with AI" button, and a Post button that hands off to the wizard.ActiveBookingsBand— live in-flight jobs with status pills and the inline OTP arrival code when the Tasker isarrived.ResumeOnboardingCard(resume a WIP Tasker application),PushPrompt(web-push opt-in),UpcomingList,StartEarningCard(become-a-Tasker upsell),PromoBanner(refer-a-friend),TrustStrip.
My Tasks hub (/app/bookings) — unified activity with 5 count-badged tabs: Posted (live tasks), Under review (pending_review), Needs changes (rejected, with the rejection reason + "Fix & repost"), Assigned (active bookings), History (past bookings, with rebook + review).
Post detail (/app/my-posts/[id]) — the poster's control center for one task: status narrative + a TaskJourney progress rail, stats (Offers / Views / Budget), full details, the Offers section, and public Q&A.
Every offer (BidView) shows the Tasker's avatar (gold ring + check if vetted), name, a trust line (★ rating + review count or a "New Pro" badge, jobs done, online-now / "replies in ~Xm"), the pitch message, portfolio thumbnails, the offer price, and a BudgetChip framing it vs the poster's budget ("$20 under" / "On budget" / "$X over").
- Ranking:
offerScoreblends within-budget, rating, jobs, verified, and price-fit → a "Best value" badge; sort by Best / Lowest / Top rated. - A client-side shortlist (bookmark/star) with a "Saved" filter.
- Per-offer actions: Message (pre-hire chat), Save, Accept (→ Hire & pay), and Dismiss (a soft hide — the Tasker is never told; restorable). A real rejection only happens when the poster hires someone else.
- A profile "peek" sheet shows the full case: rating / jobs / on-time / experience, bio, skills, recent work, link to the public profile.
- Q&A (
TaskComments) — a public per-task thread with realtime updates; anonymous questions are stashed across sign-in and auto-posted on return.
"Accept & pay" (/api/tasks/[id]/accept-and-pay) does not accept the bid yet — it validates (poster owns task, bid active & not stale, not self-bid, Tasker payouts-ready, same-day double-booking soft-warn) and mints a pending marketplace booking. The customer then goes through a hosted Stripe Checkout (/api/stripe/marketplace-checkout) — the full deal is captured immediately into platform escrow (not a pre-auth, because tasks can be weeks out), with allow_promotion_codes on for coupons/referral credit. Only when payment succeeds does the Stripe webhook accept the bid and assign the task ("resolve before release") — so the task stays open and others keep bidding until the poster actually pays. A 100%-off ($0) booking skips Stripe entirely.
- Referrals (
/app/profile/referrals) — "Give $25, Get $25." A shareabletaskly.ca/r/<CODE>link (copy / WhatsApp / SMS), a stats grid (Signed up / Available credit / Friends booked). Credit auto-applies as a Stripe promo at checkout. - Profile hub (
/app/profile) — identity panel (avatar, name, bookings count, customer rating), a capability-aware Earn row, a dark-mode toggle, and Account / Rewards / Support menus (addresses, payments, notifications, edit). - Public member profile (
/u/[id]) — the public face shown in offers when a Tasker doesn't yet have a/pro/<handle>page.
/taskers— the canonical Pro recruitment landing, framed for a Pro burned by lead fees: "No lead fees, 20% on completed jobs, paid through escrow." Sections walk through what ends (lead fees), how it works, a worked math example ($150 cleaning → 20% take → "there's nothing else"), how escrow pays out, and the safety net. Copy is honesty-clean — identity-verified via Stripe Identity is real; no background-check / insurance / guarantee claims./apply→ redirects to/onboarding/tasker(auth-first door)./join→ permanent 308 redirect to/taskers(the old page carried fabricated stats and was retired).
The door /onboarding/tasker routes by state (anonymous → login; approved/pending → straight to /provider; member with no row → the wizard). The 3-step wizard (/onboarding):
- Identity — full name, date of birth (must be 18+), phone with OTP (Twilio, locked to Canada +1 at launch).
- Skills + experience + credentials — a 9-category skill taxonomy (mirrors the curated verticals) with subcategories and granular task registries, chosen via an Uber-style type-ahead (
SkillSearch, backed by/api/suggest-skillsfor clean canonical labels). Experience level (New / Skilled / Expert). Conditional credentials collected inline: cooking → food-handler cert; electrical/plumbing → trade licence numbers (309A/442A electrician, 306A plumber). - Location — city/neighbourhood, lat/lng, service radius or discrete zones (Google Maps autocomplete).
Verification checklist (/onboarding/verify): profile photo (client-side quality validation + circle crop), identity verification via Stripe Identity (gov ID + selfie, $5 fee waived for Founding Taskers, deferrable), conditional professional certifications (up to 4 files), and availability. On submit, a provider_profiles row is created with status pending.
The approval bar (lib/marketplace/approval.ts, canApprove()): a profile photo + at least one skill/service category. Identity and trade-licence verification are separate downstream gates.
The TaskerGate upsell — when a member who isn't yet a Tasker taps a Tasker-only CTA (e.g. "make an offer"), a BecomeTaskerModal pops instead of a hard redirect, stores a returnTo, and routes them through onboarding — then resumes the original action after approval.
Gated on getTaskerStatus ∈ {approved, pending}. ProviderShell nav: Home, Browse, Community, Messages, Earnings (+ Account).
- Pending Taskers see a
PendingApprovalView— a 5-step journey (Profile submitted → Payment received → Verification → Training → Go live), a 24–48h ETA, and locked preview tiles. - Approved Taskers see
ProHome— an online toggle, a this-week net-earnings ribbon + sparkline, and stats (rating, jobs completed, completion rate, response time).
Browsing & offering: /provider/browse reuses the customer feed (the Tasker is a non-owner on every task, so the task detail opens the offer composer). Offers (task_bids, lib/marketplace/bids.ts): moderation precheck, amount floor $5 / ceiling $50,000, task must be open and not their own. A QuickApplyModal is the fast path for anonymous/ineligible visitors (asks location instead of skills; skills seed from the task on bid). The offer/jobs UI always shows payout after the 20% fee.
Migration 133 couples three DB-enforced mechanisms:
- AI-driven licensed gate — the classifier (not a poster toggle) sets
tasks.requires_licensed_provider+required_trade(electrical|plumbing|gas|hvac|null).checkCredentialGate()requires a Tasker to have an uploaded, non-expired licence for that trade to bid (a null trade accepts any uploaded licence). Uploaded is enough to bid; admin verifies asynchronously. A failed gate returns403 certification_required, which drops the Tasker into the cert-collection step. - Hold-until-approved — a pending Tasker's bid is
held_for_review: invisible to the poster and excluded from the offer count until admin approval un-holds it (and notifies the poster). A pending Tasker can't un-hold their own bid. - Rejection feedback loop — a rejected Tasker sees why (
rejection_reason,rejected_fields) on their dashboard and can fix + resubmit (rejected → pendingself-transition allowed; all other self-status changes blocked).
/provider/jobs — tabs Available / Upcoming / Completed. Available cards have Accept / Decline and show payout "after Taskly fees."
Job detail (/provider/jobs/[id]) — the workflow shape depends on booking type:
- Marketplace in-person (6 steps):
accepted → en_route → arrived → otp_verify → in_progress → completed(door OTP, no photo documentation). - Marketplace remote (3 steps):
accepted → in_progress → completed(no address/OTP; a session-confirm handshake instead). - Curated (8 steps): adds before/after photos (dark at launch).
Key Tasker mechanics: OTP arrival verification (customer reads a 4-digit code; submitted with best-effort GPS for a soft geofence that never blocks), realtime location sharing while en-route (customer watches the Tasker move on a map), Taskly-brokered calls/messages/video (contacts stay private), Navigate (Google Maps directions), "Can't make it — release job" (returns it to the pool pre-work), reschedule (propose/confirm/decline), safety reporting, and a dispute-response card.
- Fee math (
lib/marketplace/fees.ts): poster pays the deal (+$5 if urgent); commission = 20% (HST-inclusive; Taskly remits the embedded HST); Tasker payout = ~80%. The Tasker's payout is never reduced by coupons — discounts come off Taskly's take first, then out of Taskly's pocket (a $0 booking still pays the Tasker in full). - Stripe Connect (
/api/provider/stripe/connect) — an Express account (country: CA, individual, card_payments + transfers). Mirror columns (stripe_payouts_enabled, etc.) are written via service-role and surfaced in the Earnings page. - Escrow release (
lib/marketplace/release.ts) — transfers the Tasker's 80% to their Connect account viasource_transactionon the captured charge, idempotently keyed so exactly one transfer fires. Triggered by poster confirmation, a 3-day auto-release cron, or admin dispute resolution. Chargeback clawback debt is auto-deducted from the next payout; coupon-subsidized shortfalls are absorbed from the platform balance. - Earnings page (
/provider/earnings) — money buckets (In escrow / Needs attention / Paid out), period tabs (Today/Week/Month) with a NET headline, gross billed, Taskly fee · 20%, charts (earnings, best-times heatmap, category, top zones), and a link to/provider/tax-summary(per-year CSV export).
- Editor (
/provider/profile/public) — builds the shareable/pro/<handle>page (link + QR): headline, about/bio, languages, skills, interests, experience, portfolio photos, project embeds, social links, service areas, availability, publish toggle. - AI enhancement —
/api/enhance-bio(first-person About, invents nothing),/api/enhance-headline,/api/parse-resume("Import from CV" — PDF/text → structured fields, extract-only). - Enrichment — LinkedIn OAuth import, Google Places (business autocomplete +
place_id), portfolio/project uploads. - Public profile (
/pro/[handle]) — headline/about/skills/languages/portfolio/experience + service-area map, share hub, OG image, visiting card.
bookings.status: pending → assigned → accepted → en_route → arrived → in_progress → completed → paid, with cancelled / disputed branches. Every transition appends to booking_timeline (realtime). Marketplace bookings start pending and are only accepted once payment captures.
From the customer's booking detail (/app/bookings/[id]): a live status banner + Uber-style LiveTracker, a provider card (Call / Message, contacts private), a LiveTrackingMap while en-route, the arrival OTP code, a live checklist + service-photo gallery, the status timeline, and — on completion — a Confirm & release escrow banner (or 3-day auto-release) and a review CTA (two-way ratings). Actions: Report a problem (DisputeModal → holds escrow), Safety issue, Report damage (within 14 days), Reschedule (mutual-agreement), Cancel (marketplace cancellable through in_progress).
All money is integer cents CAD. Two distinct fee engines exist; only the marketplace one is live:
A) Marketplace commission (lib/marketplace/fees.ts) — LIVE
COMMISSION_RATE = 0.20(HST-inclusive),HST_RATE = 0.13,URGENT_FEE_CENTS = 500.- Poster pays the deal (+ urgent fee); Tasker gets ~80%; Taskly keeps 20% and remits the embedded HST.
- Coupons discount Taskly's take, never the Tasker's payout; a $0 booking still pays the Tasker in full.
B) Curated "Trust & Support" fee (lib/booking/pricing.ts) — DARK (UC_ENABLED off)
- Single Trust & Support fee = 8% of the post-discount base, clamped $5.99–$24.99 (replaced the old regressive
$5.99 platform + $9.00 conveniencepair). SAME_DAY_SURCHARGE_CENTS = 2500(snow exempt). Recurring discounts: weekly 20% / bi-weekly 15% / monthly 10% (capped at 10% for wage-sensitive PSW/cooking/tutoring). HST on base + fee.
Escrow (Stripe Connect Express, API pinned 2026-03-25.dahlia):
- Marketplace = capture up front into platform escrow → transfer 80% on release. Curated = pre-auth → capture on completion.
- 3DS step-up forced on charges ≥ $500.
- "Resolve before release": auto-release (
AUTO_RELEASE_DAYS = 3) only fires if no dispute or chargeback is open. An atomic DB claim (claim_booking_for_money_movement,SELECT … FOR UPDATE) + a Stripe idempotency key close the release-vs-dispute race so exactly one transfer happens. - Chargeback lifecycle — freezes the payout, opens an internal dispute, and on a lost chargeback claws the money back from the Tasker's Connect transfer or tracks it as debt against their next payout.
- Admin dispute resolution — three outcomes: release (payout in full), refund (poster made whole, booking cancelled), or partial (split), each with outcome-specific idempotency keys.
- OTP door codes — a 4-digit
otp_codeper in-person booking; remote work uses a session-confirm handshake.
Full detail: ARCHITECTURE.md §16, docs/PAYMENTS_AND_DISPUTES.md, PRICING_v3.md.
LLM features run on Groq with a universal principle: AI never dead-ends the user — every route returns a graceful fallback (503 or fail-open) when GROQ_API_KEY is missing, and the client silently degrades to a manual path.
- Model tiering: 70B (
llama-3.3-70b-versatile, deterministictemp:0/seed:42) for anything that gates a flow — classify, moderate, resolve, enhance-task; 8B (llama-3.1-8b-instant) for cheap cosmetic rewrites — titles, ghost-text, bio/headline, comment moderation. - The classifier spine (
classifyTask(),lib/classify/**) is the single source of truth: crisis check → Layer-0 pre-check → in-memory cache → shared Redis cache (30-day TTL, so a task classified on any instance costs one Groq call globally — the cost scaler at 10k+ users) → one Groq call → validate every candidate against the locked taxonomy → route by confidence (thresholds HIGH=75 / LOW=45 / GAP=15) intoauto | options | confirm | disambiguate | split | coming_soon | ask | block | review | crisis. - The taxonomy anti-hallucination spine (
lib/taxonomy/**,TAXONOMY_VERSION v3-2026-06-13) — ~131 categories / ~1,376 subcategories. One merged structure builds both the classifier prompt and the validator so they can never disagree. The model can only ever emit a locked(category, subcategory)pair; disabled categories surface as coming-soon + demand-log. An embedding pre-filter sends only the top-K relevant categories to keep token cost down. - Deterministic pricing — the model never prices. Structured intake taps ("scope pills") drive a deterministic rate-table lookup × a postal-code region-tier multiplier.
/api/suggest-budgetreuses the classifier cache (often zero extra Groq calls); a Gemini live-pricer covers long-tail categories with no learned rate.
Per-surface AI features: description drafts & Gmail-style ghost-text, "clean up my words" rewrites, live category suggestions, market price estimates with a "why this price" breakdown, AI image generation for tasks, Tasker bio/headline enhancement, resume import, and voice transcription. Full prompt catalog: AI_PROMPTS.md.
Content moderation is layered (lib/moderation/**):
- Layer 0 — deterministic pre-check (0ms, no AI): flags too-short/gibberish text (→ ask for detail), strips contact info (phone/email/URL/@handles replaced with ▮, so deals stay on-platform), and truncates over-long input. Never blocks.
- Crisis path (
looksLikeCrisis()) — self-harm language is checked first and is never a policy violation; it returns supportive copy citing the 988 Suicide Crisis Helpline + 911 and triggers human follow-up. - Layer 1 — Groq gate (
/api/moderate-task, 70B): judges the requested action, not vocabulary. Four verdicts —allow / ask_details / review / block— with explicit prohibited categories (drugs, weapons, violence, sexual services, fraud, hacking, harm to children/vulnerable, animal cruelty) and gray zones that route to humanreview(default-deny on anything off-schema). Regulated trades are allowed but flaggedrequires_licensed_provider. Fails open (allow, still stripping contact) when the key is down — legit posters are never hard-blocked by an AI outage. - Comment moderation (public Q&A / messages) judges interpersonal conduct and fails open.
- Image moderation — a vision check on uploads.
Only block / crisis (and genuinely fragment-short text) hard-stop a post; review is allowed to post and lands in the admin Task-review queue.
Human safety layer: manual Tasker review & approval, Stripe Identity verification, door-OTP arrival codes, masked/brokered calls, two-way ratings, damage claims, safety reports, and a real person decides disputes.
- In-app notifications (
lib/notifications/create.ts) — a ~40-type enum written via service-role;criticaltypes (money, security, disputes) always send, others respect the user'snotification_preferences. A high-value subset also fires Web Push (VAPID). Rows land innotificationsand update live via Realtime. - Email transport (
lib/email/send.ts) — Resend HTTP API first (the reliable serverless path), Gmail SMTP fallback (IPv4-forced), no-op if neither is set. Every send carries a CASLList-Unsubscribeheader. - ~40 transactional templates — customer (welcome, OTP, booked, receipt, review request, task approved/rejected), Tasker (application received/approved/rejected, new offer, job paid, payout failed, identity verified/rejected), and admin (new-task-submission, chargeback, safety incident, damage claim). Canonical firing maps: NOTIFICATIONS-MAP.md +
EMAIL_MAP.md.
A native in-app Community lounge for Taskers (/provider/community, CommunityLounge) — skill-based "infinite-space" rooms a Tasker is auto-joined to based on their task skills, with live messages + presence over a WebSocket gateway (no iframe). Built out across migrations 142–145:
- Events — members create events and RSVP.
- Challenges — prompts + submissions.
- Study rooms — synchronized focus sessions on a shared server deadline (no streamed clock).
- Plus DMs, a leaderboard, GIFs, saved messages, link unfurls, uploads, and AI thread summarize (
/api/community/**). A ticket is minted per session (/api/community/ticket).
Admin console (app/(admin)/admin/**, AdminShell): Dashboard (revenue/bookings/providers-online KPIs, live map, stale-bookings triage), Analytics, Funnels (conversion drop-off), Disputes (release/refund/partial), Payouts (flagged + clawback-debt), Chargebacks, Damage-claims, Safety, Providers (approve/reject applications + docs), Task-review (moderation queue), Bookings, Customers, Reviews, Feedback, Calls (masked-call logs), Settings, and Traffic/UTM attribution.
Cron / ops — a single scheduler tick (/api/cron/run, Bearer CRON_SECRET) fans out ~15 idempotent jobs (money-safety ordered): auto-complete stale jobs, auto-release escrow (the 3-day sweep + transfer-retry path), reconcile captures & Connect status, expire stale bids, convert-to-fixed, completion reminders, escrow reconciliation, dropoff emails, taxonomy embeddings, price calibration, and photo cleanup. A dead-man's-switch heartbeat pings healthchecks.io; unhealthy jobs page a human via alertOps (Slack/Discord + admin email, throttled).
The "safe" half of Taskly's positioning only works if every trust claim is literally true. The rule: show the mechanic, don't assert the adjective. Canonical boundary: BRAND-VOICE.md §5 + Final business plan.md §7.
Taskly does NOT claim (none are operational): criminal / CPIC background checks; liability insurance or property-protection guarantees; satisfaction / re-clean guarantees; WSIB compliance; trade-licence verification (schema scaffolding only); "24-hour payouts"; any take-rate other than 20%; fabricated volume/rating stats or named testimonials.
Taskly DOES claim (all real & shipped): a manual Tasker review & approval gate; identity verification via Stripe Identity (gov photo ID + selfie — verifies who, not criminal history); escrow held until you confirm (or 3-day auto-release); 4-digit door-OTP arrival codes + remote session-confirm; masked/brokered calling; two-way ratings; and a real person decides disputes.
Language rules: Tasker (not worker/gig/contractor), Poster/you (not customer/user), reviewed & approved (not "vetted"), identity-verified (not "background-checked"), offers (not "bids"), held in escrow (not "protected"). Demo content must be labeled illustrative with no invented names/ratings/quotes.
| Area | Feature | Where |
|---|---|---|
| Auth | Email+password, Google OAuth, magic code, forgot-password, Turnstile, 6-digit OTP | app/login, app/api/auth/*, app/auth/callback |
| Post a task | 5-step wizard, draft persistence, autopost-across-auth, funnel analytics | app/post-task/** |
| AI (poster) | description draft, ghost-text, clean-up rewrite, scope pills, category classify, market estimate, image gen | app/api/{suggest-*,complete-description,enhance-task,classify,predict-price,generate-image} |
| Customer dash | task composer, active-bookings band, My Tasks (5 tabs), post detail + journey rail | app/(customer)/app/** |
| Offers | ranked offers, Best-value badge, shortlist, message/save/dismiss, profile peek, Q&A | components/marketplace/OffersDrawer, TaskComments |
| Hire & pay | accept-and-pay, hosted Stripe Checkout, capture-to-escrow, coupons | app/api/tasks/[id]/accept-and-pay, app/api/stripe/marketplace-checkout |
| Referrals | Give $25/Get $25, share links, credit ledger, auto-apply | app/(customer)/app/profile/referrals |
| Become a Tasker | 3-step onboarding, skill taxonomy, Stripe Identity, verification checklist, TaskerGate upsell | app/onboarding/**, components/tasker/TaskerGate |
| Tasker workspace | Home/Browse/Community/Messages/Earnings, offers, QuickApply, licensed bid gate | app/(provider)/provider/**, lib/marketplace/{bids,credentials} |
| Jobs | Available/Upcoming/Completed, OTP verify, live location, calls/video, reschedule | app/(provider)/provider/jobs/** |
| Payouts | Stripe Connect Express, escrow release, 3-day auto-release, clawback, tax summary | lib/marketplace/{fees,release}, app/(provider)/provider/{earnings,tax-summary} |
| Tasker profile | public /pro/<handle>, AI bio/headline, resume import, LinkedIn/Places, portfolio |
app/(provider)/provider/profile/public, app/pro/[handle] |
| Job lifecycle | status machine, timeline, LiveTracker, dispute/safety/damage/reschedule/cancel, reviews | app/(customer)/app/bookings/[id], app/api/bookings/[id]/** |
| Trust & safety | layered moderation, crisis path, contact stripping, escrow, OTP, masked calls | lib/moderation/**, lib/marketplace/release |
| Community | skill rooms, live chat/presence, events, challenges, study rooms, DMs | app/(provider)/provider/community, app/api/community/** |
| Admin | dashboard, funnels, disputes, payouts, chargebacks, safety, provider approval, task-review | app/(admin)/admin/** |
| Ops | single cron tick → ~15 idempotent jobs, heartbeat, ops alerts | app/api/cron/run, lib/ops/alert |
This document reflects the product as shipped at the time of writing (2026-07). When a flow changes, update this file in place — it is the end-to-end map, not an aspirational spec. For system internals, defer to ARCHITECTURE.md; for the operating model, BUSINESS-MODEL.md; for claim wording, BRAND-VOICE.md.