14 — PRODUCT SPECIFICATION
Depends on: 13_PACKAGE_SELECTION_POLICY.md · Counts: 50 FREE + 100 PAID = 150 Features
Phase 1 delivers: 5 features FULLY (marked ✅) + 5 blogs fully + all listed
Every feature = UI + API + DB + Validation + AuthZ + Tests + Docs. No feature without all six.
1. Plans & Limits — Single Source packages/config/plans.ts
| Plan |
Price (EUR/mo) |
Links |
Clicks/mo |
API Keys |
Custom Domains |
Features Included |
| Free |
0 |
50 |
1,000 |
1 |
0 |
50 FREE |
| Starter |
9 |
500 |
10,000 |
3 |
1 |
50 FREE + 20 PAID |
| Pro |
29 |
5,000 |
100,000 |
10 |
5 |
50 + 50 |
| Business |
79 |
50,000 |
1,000,000 |
50 |
20 |
50 + 75 |
| Enterprise |
Custom |
Unlimited |
Unlimited |
Unlimited |
Unlimited |
150 (all) |
Limits enforced at API + shown in UI with upgrade prompts. No duplicated numbers.
2. FREE — 50 Features (All users)
Group FREE-A: Core Link Management (18)
| # |
Feature |
Module |
Notes |
| F01 |
Shorten URL ✅ FULL Phase 1 |
links |
POST /v1/links, base62, validation |
| F02 |
Custom alias |
links |
mybrand, uniqueness check, GET /v1/links/duplicate |
| F03 |
Edit link |
links |
PATCH /v1/links/:id, versioned |
| F04 |
Delete link |
links |
DELETE /v1/links/:id, soft delete + 410 |
| F05 |
Link list (paginated) |
links |
Cursor pagination, search |
| F06 |
Search / filter / sort |
links |
?search=&sort=createdAt:desc |
| F07 |
Duplicate detection |
links |
Suggests existing on same URL |
| F08 |
Link history / versions |
links |
GET /v1/links/:id/versions |
| F09 |
Link title / notes |
links |
Editable title, private notes |
| F10 |
Archive / unarchive |
links |
Hide without delete |
| F11 |
Expiration — datetime |
links |
expiresAt, isExpired check on redirect |
| F12 |
Expiration — max clicks |
links |
expireClicks, decremented |
| F13 |
Password-protected links |
links |
Argon2id, ?password= on redirect |
| F14 |
QR — static ✅ FULL |
qr |
GET /v1/qr?url=... PNG/SVG |
| F15 |
Bulk create (up to 100) |
links |
POST /v1/links/bulk, idempotent |
| F16 |
CSV export (all links) |
links |
GET /v1/links/export.csv, injection-safe |
| F17 |
CSV import (up to 500) |
links |
POST /v1/links/import with error report |
| F18 |
Public share page |
links |
/s/:code read-only preview |
Group FREE-B: Analytics Basic (10)
| # |
Feature |
Module |
Notes |
| F19 |
Click tracking (bot-filtered) ✅ |
analytics |
Counts only real UA |
| F20 |
Total clicks |
analytics |
GET /v1/analytics/overview |
| F21 |
Clicks by day (30d chart) ✅ |
analytics |
ECharts |
| F22 |
Country breakdown |
analytics |
GeoIP-lite |
| F23 |
Device breakdown |
analytics |
ua-parser |
| F24 |
Browser breakdown |
analytics |
ua-parser |
| F25 |
OS breakdown |
analytics |
ua-parser |
| F26 |
Referrer tracking |
analytics |
Host + path |
| F27 |
Recent clicks stream |
analytics |
Last 50 events |
| F28 |
Export clicks CSV |
analytics |
GET /v1/analytics/export.csv |
Group FREE-C: Organization (6)
| # |
Feature |
Module |
| F29 |
Folders (create/rename/move) |
links |
| F30 |
Tags (create/assign/filter) |
links |
| F31 |
Favorites / starred |
links |
| F32 |
Quick search + keyboard ⌘K |
ui |
| F33 |
Dashboard overview cards |
analytics |
| F34 |
Link favicon preview |
links |
Group FREE-D: Account & System (8)
| # |
Feature |
Module |
| F35 |
Register / Login / Logout ✅ |
auth |
| F36 |
Sessions list + revoke |
auth |
| F37 |
Profile (name, email) |
users |
| F38 |
API keys — create/list/revoke ✅ |
api-keys |
| F39 |
Usage limits display |
billing |
| F40 |
Notifications (in-app, link expired) |
notifications |
| F41 |
Activity timeline (own) |
audit |
| F42 |
Help / docs link |
docs |
Group FREE-E: Tools (Basic) (3)
| # |
Feature |
Module |
| F43 |
Password generator (UI) |
tools/password |
| F44 |
UTM builder (basic) |
tools/utm |
| F45 |
Base64 encode/decode |
tools/base64 |
Group FREE-F: Content & Support (5)
| # |
Feature |
Module |
| F46 |
Blog — read articles |
blog |
| F47 |
Search blog |
blog |
| F48 |
Contact form |
marketing |
| F49 |
Status page |
system |
| F50 |
Security.txt + legal pages |
marketing |
Phase 1 ships F01, F14, F19-F22, F35, F38 fully. All 50 have UI shell + OpenAPI stub + docs entry; 5 are executable.
3. PAID — 100 Features (Unlock by plan)
PAID-1: Teams & RBAC (12) — Starter+
| # |
Feature |
Plan |
Module |
| P01 |
Teams / workspaces |
Starter |
teams |
| P02 |
Invite members (email) |
Starter |
teams |
| P03 |
Roles: Owner/Admin/Member/Viewer |
Starter |
teams |
| P04 |
Granular permissions per module |
Pro |
teams |
| P05 |
Team-scoped links |
Starter |
links |
| P06 |
Shared folders |
Business |
teams |
| P07 |
Transfer link ownership |
Pro |
links |
| P08 |
Team analytics rollup |
Pro |
analytics |
| P09 |
Audit logs (immutable) |
Business |
audit |
| P10 |
SSO ready (OAuth state/nonce/PKCE) |
Business |
auth |
| P11 |
MFA TOTP + recovery codes |
Pro |
auth |
| P12 |
IP allowlist (Enterprise) |
Enterprise |
security |
PAID-2: Custom Domains & Branding (10) — Starter+
| # |
Feature |
Plan |
| P13 |
Add custom domain |
Starter (1) → Pro (5) → Business (20) |
| P14 |
DNS verification (TXT) |
Starter |
| P15 |
On-demand TLS (ACME) |
Starter |
| P16 |
Branded short links (go.brand.co/code) |
Starter |
| P17 |
Domain-scoped analytics |
Pro |
| P18 |
Custom 404 for domain |
Pro |
| P19 |
Domain SSL auto-renew |
Starter |
| P20 |
Link with pixel / favicon per domain |
Business |
| P21 |
Domain-level rate limits |
Business |
| P22 |
Bulk domain import |
Enterprise |
PAID-3: Advanced QR (8) — Starter+
| # |
Feature |
Plan |
| P23 |
Dynamic QR (editable destination) |
Starter |
| P24 |
QR with logo overlay |
Pro |
| P25 |
QR color / frame |
Starter |
| P26 |
QR bulk generation (1k) |
Pro |
| P27 |
QR analytics per scan |
Pro |
| P28 |
QR expiry |
Starter |
| P29 |
QR download SVG/EPS |
Starter |
| P30 |
QR API (create via API) |
Pro |
PAID-4: Analytics Pro (15) — Pro+
| # |
Feature |
Plan |
| P31 |
City-level geo |
Pro |
| P32 |
Campaign / UTM analytics |
Pro |
| P33 |
Conversion tracking + postback |
Business |
| P34 |
Webhook on click |
Business |
| P35 |
Scheduled report emails |
Pro |
| P36 |
Export PDF report |
Pro |
| P37 |
Retention (90d → 1y → unlimited) |
Starter → Business → Enterprise |
| P38 |
Bot toggle + filtering settings |
Pro |
| P39 |
Heatmap placeholder (click timeline) |
Business |
| P40 |
A/B destination (2 URLs, 50/50) |
Business |
| P41 |
Link comparison |
Pro |
| P42 |
API analytics per key |
Pro |
| P43 |
Daily rollup materialized analytics_daily |
Pro |
| P44 |
Real-time clicks (SSE/WebSocket) |
Business |
| P45 |
Link health check (destination up) |
Starter |
PAID-5: Bulk & Developer Platform (12) — Pro+
| # |
Feature |
Plan |
| P46 |
Bulk 10k create |
Business |
| P47 |
CSV import 10k with row errors |
Pro |
| P48 |
Webhooks (link created/clicked) |
Business |
| P49 |
Webhook retries + HMAC signing |
Business |
| P50 |
Higher rate limits (500/min) |
Pro → Business |
| P51 |
Pagination 100 per page |
Pro |
| P52 |
OpenAPI SDK — Node |
Pro |
| P53 |
OpenAPI SDK — Python/PHP/Go snippets |
Pro |
| P54 |
Developer dashboard (key scopes, logs) |
Pro |
| P55 |
Idempotency keys on POST |
Pro |
| P56 |
Request logs (7d) |
Business |
| P57 |
Playground in docs (try it) |
Pro |
PAID-6: Bio Pages (Link-in-Bio) (10) — Pro+
| # |
Feature |
Plan |
| P58 |
Bio page builder |
Pro |
| P59 |
Themes (5) |
Pro |
| P60 |
Custom CSS (Business) |
Business |
| P61 |
Analytics per bio |
Pro |
| P62 |
Custom domain for bio |
Business |
| P63 |
Verification badge |
Business |
| P64 |
Scheduled publish |
Starter |
| P65 |
SEO for bio |
Pro |
| P66 |
Embeds (YouTube, Spotify) |
Pro |
| P67 |
Contact button + form |
Starter |
PAID-7: Tools Suite (20) — Pro / Business
Each is modules/tools/<name> with tool.json + UI + POST /v1/tools/<name>/*:
| # |
Tool |
Plan |
Description |
| P68 |
Password generator (advanced) |
Free→Pro |
Length, symbols, pronounceable |
| P69 |
UTM builder (pro) |
Starter |
Presets, bulk UTM |
| P70 |
Base64 / URL encode |
Free |
Already free basic, pro adds file |
| P71 |
UUID generator |
Starter |
v4/v7 bulk |
| P72 |
Hash generator |
Starter |
MD5/SHA/Bcrypt |
| P73 |
Markdown preview |
Starter |
MDX live |
| P74 |
JSON formatter |
Starter |
Validate, tree |
| P75 |
Regex tester |
Pro |
Explain |
| P76 |
Slug generator |
Starter |
SEO slug |
| P77 |
Color picker / palette |
Starter |
OKLCH, contrast |
| P78 |
QR advanced (see P23-30) |
— |
— |
| P79 |
Link checker |
Pro |
Bulk URL health |
| P80 |
Redirect checker |
Pro |
Trace 301 chain |
| P81 |
Meta extractor |
Pro |
OG/Twitter preview |
| P82 |
Sitemap generator |
Pro |
XML from URLs |
| P83 |
Robots generator |
Starter |
robots.txt |
| P84 |
Image compressor |
Pro |
WebP/AVIF |
| P85 |
CSV ↔ JSON |
Pro |
Convert |
| P86 |
Cron tester |
Pro |
Expression → dates |
| P87 |
JWT debugger |
Pro |
Decode/verify (client-side) |
PAID-8: Admin & Security (8) — Business/Enterprise
| # |
Feature |
Plan |
| P88 |
Admin panel (users/links/domains) |
Business |
| P89 |
Abuse reports + queue |
Business |
| P90 |
Kill-switch (410) + audit |
Business |
| P91 |
Security event logs |
Business |
| P92 |
Rate-limit dashboard |
Business |
| P93 |
Blocklist feed (Safe Browsing) |
Business |
| P94 |
Data retention controls |
Enterprise |
| P95 |
DPA / GDPR export/delete |
Business |
PAID-9: Billing & Enterprise (5) — Pro+
| # |
Feature |
Plan |
| P96 |
Stripe billing + invoices |
Pro |
| P97 |
Proration + downgrade flow |
Pro |
| P98 |
Usage alerts (80%/100%) |
Starter |
| P99 |
Annual / monthly toggle |
Pro |
| P100 |
Custom Enterprise (SSO, SLA, on-prem) |
Enterprise |
4. Phase 1 — 5 Features Fully Specified
F01 — Shorten URL
- UI: Dashboard
New Link → input (2048) → custom alias (optional, 3-30, regex) → create → toast + row in table
- API:
POST /v1/links (Zod: originalUrl url, customAlias?, title?, expiresAt?, password?) → 201 { shortCode, url: https://yas.sh/<code>, ... } — 400 validation, 409 alias taken, 429 rate limited
- DB:
Link(userId, originalUrl, shortCode UNIQUE, customAlias UNIQUE?, createdAt) + increment user.linkCount check against plan limit
- Tests: unit duplicate, integration auth/unauth, e2e: shorten → appears in list
Redirect Engine (GET /:code)
- Flow: Lookup
shortCode or customAlias → 404 if missing → 410 if expired/deleted/killed → 401 if password & missing → increment clicks + ClickEvent (bot-filter: exclude known bot UA + ?bot=1 param) → 302 to originalUrl (configurable 301/302)
- Perf: First-hit DB, then Valkey/memory cache 60s; p95 <50ms cached
- Tests: e2e loop shorten→redirect→analytics count 1 → repeat with bot UA count stays 1
F14 — QR Static
- UI: Per-link
QR button → modal with size 256/512/1024, format PNG/SVG, download, copy image
- API:
GET /v1/qr?url=https://yas.sh/abc&size=512&format=png → image/png (qrcode lib, EC M, margin 2) — rate-limited, validates url http(s), size ∈ {128,256,512,1024}
- Tests: content-type + magic bytes, invalid url 400
Analytics Overview (F19-22)
- UI:
/dashboard/analytics → cards (total links, total clicks, avg CTR), 30-day line (ECharts), breakdowns (country/device/referrer top 10), per-link drill GET /v1/analytics/:linkId
- API:
GET /v1/analytics/overview?days=30&linkId? → { totalLinks, totalClicks, series: [{date, clicks}], breakdowns } — auth required, RBAC: owner or team member
- DB: Aggregates from
ClickEvent (phase 1 direct query with indexes; daily rollup added Phase 4)
- Tests: shorten→redirect→overview
totalClicks:1, bot excluded
Auth + API Keys (F35, F38)
- UI:
/login, /register, /dashboard/keys → create (name + scopes multi-select) → show yas_live_... once with copy + warning → revoke with confirm
- API:
POST /v1/auth/register (email, password Argon2id ≥12 chars), POST /v1/auth/login (sets __Host-session, rotation), POST /v1/auth/logout, GET /v1/auth/me, POST /v1/api-keys (stores SHA-256 hash only), DELETE /v1/api-keys/:id, Bearer usage Authorization: Bearer yas_live_...
- Security: Anti-enumeration identical timings, per-IP throttling 10/min, session expiry 30d idle / 90d absolute
- Tests: register→login→me 200, unauth matrix 401, API key create→use
Bearer 200→revoke→401, IDOR: user A cannot delete B's link
5. Blogs — 5 Fully Production (Phase 1)
Each: index.mdx 1,200-2,200 words + hero.webp + cover.webp + thumb.webp + frontmatter (see 07_BLOG_SYSTEM.md) + FAQ + TOC + 4 internal links + JSON-LD
| # |
Slug |
Title |
Author |
Category |
| 1 |
understanding-url-shortening |
Understanding URL Shortening: The Complete Guide |
yas-team |
URL Shortening |
| 2 |
qr-codes-for-business |
QR Codes for Business: From Scan to Conversion |
yas-team |
QR / Marketing |
| 3 |
custom-aliases-seo |
Custom Aliases & SEO: Short Links That Rank |
yas-team |
SEO |
| 4 |
link-analytics-explained |
Link Analytics Explained: What to Measure |
yas-team |
Analytics |
| 5 |
api-keys-best-practices |
API Keys Best Practices for Developers |
yas-team |
Developers |
Remaining stubs (45 more) to reach 50: utm-campaign-guide, branded-links-trust, password-links-use-cases, link-expiration-strategy, bio-pages-conversion, ... (full list in blog/content/_stubs.json).
6. Acceptance Per Feature
Before a feature is marked ✅: UI works → API validates → DB persists → authZ enforced → unit + integration + e2e green → docs example executes → PROJECT_MEMORY.md updated.
All 150 features have module.json + docs.md placeholder at repo init so scripts/gen:modules lists the full roadmap. Only 5 are executable in Phase 1; others return 501 not_implemented with RFC9457 + upgrade CTA.