What's new
Changelog
A running log of features, improvements, and fixes we've shipped.
[1.2.0] - 2026-04-17
Added
- Help documentation: new "Finding a Brand Domain" section under
/dashboard/help/find-domaincovering the AI suggestion patterns, brand safety filter, purchase/renewal flow, and transfer-out. Getting Started section and Domains section updated to reference both shared system domains (xpol.link,xpol.app) and cross-link to the domain finder. - AI-powered brand domain finder — enter a brand name and AI generates the shortest, most memorable domain suggestions (vowel-dropping, TLD-as-word, abbreviations, brand-adjacent words).
- Brand protection and content safety — blocklists for ~200 major brands and offensive terms, applied to both user input and AI output.
- Dashboard domain finder page (
/dashboard/domains/find) with search, results grid, and purchase dialog with contact info form. - Marketing domain finder page (
/find-domain) with hero, search, results, "How it works" section, and sign-up CTAs for unauthenticated users. - "Find Your Brand Domain" card on the domains page linking to the domain finder.
- Admin Registered Domains page (
/dashboard/admin/registered-domains) with status filters (active, expiring, orphaned, failed) and actions (reclaim as shared, let expire). - internal fields flag on domains table to distinguish purchased vs. BYOD domains.
- internal fields plan limit flag (Pro and Business only).
- Price markup system configurable via
DOMAIN_MARKUP_PERCENTandDOMAIN_MARKUP_MIN_CENTSenv vars. - "Find Domain" link in marketing navigation header.
- "Registered Domains" admin sidebar item.
- "Find Domain" link in marketing footer under the Product section.
- Full-screen mobile navigation menu on marketing pages — "Get Started Free" in the header bar, Navigate section (Pricing, Find Domain, Blog, Sign In), and Developers section (API, CLI, AI Integrations).
Changed
- Enlarged the search box on the marketing Find Domain page — taller input, bigger type, wider max width.
- Dashboard, Links list, and New Link pages parallelise the shared-domain usage count with the rest of their data fetching instead of awaiting it sequentially.
Added
- our codebase generates an SEO-optimised sitemap covering the home page, pricing, find-domain, blog index + all posts, docs (API, CLI, integrations index), all 6 AI integration guides, contact, sign-up / sign-in, and legal pages. Priorities and change frequencies follow typical marketing-site conventions; blog posts use their published date for
lastModified. - our codebase serves
/robots.txtwith sitemap reference and disallow rules for/dashboard,/api, sign-in/up, and invite routes. - Branded 404 page (our codebase) — "We shortened it a little too much" headline with a broken-link Lucide icon, CTAs to home / contact / pricing / find-domain / blog.
- Branded error boundary (our codebase) with "That redirect didn't redirect" copy, retry + home + contact buttons, and an error-digest display for support.
- Root
global-error.tsxfallback for crashes above the root layout (inline styles only, does not depend on app CSS). - Public
/securitypage outlining encryption, authentication, platform hardening, sub-processors, responsible-disclosure policy, and reporting contact. /.well-known/security.txtat the canonical location, pointing at the security policy and reporting address.- Public
/legal/dpaData Processing Addendum page covering roles, sub-processors table, international transfer safeguards, and breach-notification commitments. - Public
/changelogpage that rendersCHANGELOG.mdwith a lightweight in-house markdown renderer. At build time every entry is passed through our codebase which drops lines referencing internal env-var names, admin-bypass mechanics, production-cutover details, migration script filenames, and internal subdomains, collapses### Securitysubsections into a single generic bullet, and rewrites backticked file paths / internal function names into generic nouns. The sourceCHANGELOG.mdstays engineer-friendly; the public page is the scrubbed view. - Branded
/offlinefallback page for future PWA / service-worker usage. - Branded
/thank-youconfirmation page suitable for post-sign-up and post-purchase conversion tracking. - Marketing footer expanded under Legal with links to Data Processing Addendum, Security, System Status, and Changelog.
- Admin overview page (
/dashboard/admin) with stat cards (users, teams, links, clicks, domains), plan breakdown badges, alert card for flagged links/open tickets, and recent signups list. - Admin teams list page (
/dashboard/admin/teams) with search, paginated table (name, owner, plan, links, domains, created date), and navigation to team detail. - Admin team detail page (
/dashboard/admin/teams/[teamId]) with stats, subscription info, admin actions (change plan, reset link counter), members/domains/recent links tables. - Admin sidebar expanded with Overview, Teams, Users, Domains, Flagged Links, and Messages navigation items (previously only Messages).
- Admin API route
PATCH /api/admin/teams/[teamId]for changing a team's plan and resetting their link counter. - Admin API route
PATCH /api/admin/links/[linkId]for reviewing or disabling flagged links. - Admin API route
DELETE /api/admin/domains/[domainId]for removing domains. - Admin Users page (
/dashboard/admin/users) with paginated user list, search by email/name, team links, and role badges. - Admin Domains page (
/dashboard/admin/domains) with paginated domain list, search by hostname/team, verified/pending badges, and shared indicator. - Admin Flagged Links page (
/dashboard/admin/flagged) showing suspicious links from the last 7 days with dismiss and disable actions. - In-dashboard support form (
/dashboard/support) for logged-in users, pre-populated with account context (name, email, team, plan). Submits tocontactMessageswith internal fields for admin team-linking. - Support context API (
/api/support/context) returns current user's name, email, team, and plan. - Admin query functions (our codebase) for system-wide stats, teams, users, domains, flagged links, message status management, and admin actions.
- internal fields column on
linkstable for flagged link review tracking. statuscolumn oncontactMessagestable (replacesisReadboolean) with values:new,in_progress,closed.- internal fields column on
contactMessagestable for dashboard-submitted support requests.
Changed
- Admin messages page (
/dashboard/admin/messages) rewritten with ticket workflow: New → In Progress → Closed status, colored status badges, Open/Closed filter tabs, team context link for dashboard-submitted messages. - Admin messages API (
/api/admin/messages/[id]) PATCH handler now accepts{ status: "new" | "in_progress" | "closed" }instead of{ isRead: boolean }. - Contact API (
/api/contact) now accepts optional internal fields for linking support requests to teams.
Changed
- All marketing content pages (blog posts, docs, integrations, contact, terms, privacy) now use
max-w-6xlcontainer width to match the blog index, pricing, and home page layouts. - Replaced
mailto:hello@xpolink.appemail links with/contactpage links across 10 marketing pages (API docs, CLI docs, 6 integration guides, integrations index, pricing). Terms page email kept as-is for legal purposes.
Added
- Admin messages dashboard (
/dashboard/admin/messages) visible only to admin emails. Supports mark read/unread and delete actions. - "Admin" section in dashboard sidebar with "Messages" link, only shown to admin users.
- Integrations index page with card grid and setup-type badges (
/docs/integrations). - "Integrations" column in marketing footer linking to all 6 platform guides.
- "AI Integrations" link added to the Developers footer column.
- Help page "AI Agents & Skills" section rewritten with platform comparison table and links to full guides.
Fixed
- Blog "Click Tracking 101": analytics retention corrected from 30 days/1 year/unlimited to 7 days/90 days/1 year; free plan analytics depth corrected to countries-only (basic); device/browser/referrer corrected to Pro+ only; API export corrected to Business-only CSV export.
- Blog "Smart Links": geo-routing corrected from "Pro and Business" to Business only.
- Blog "Link in Bio": "Starter plan" corrected to "free plan".
- Blog "URL Shortener API": API base URL corrected from
api.xpolink.com/v1toxpolink.app/api/v1; batch endpoint corrected from/links/batch(100 limit) to/links/import(500 limit, Pro/Business);slugfield corrected toshortCode; list endpoint query params corrected topage/search. - Blog "How to Create Short Links": API base URL corrected;
slugfield corrected toshortCode.
- Xpolink CLI (our codebase) prepared for npm publishing as
@xpolink/cliwithpublishConfig,files,engines,keywords,homepage,repository,bugs,LICENSE, andREADME.md. - CLI commands:
whoami,logout,link get,link update(with--url,--title,--active,--redirectflags),link list --search. - CLI commands:
link export(click analytics CSV),link emails(list captured emails),link emails-export(captured emails CSV) — all Business-plan-gated server-side. - CLI commands:
domain get(shows DNS records),domain delete. - CLI commands:
api-key list,api-key create,api-key deletefor managing API keys from the terminal. - CLI
domain listnow shows domain IDs and DNS records for unverified domains. - CLI
domain verifynow re-displays expected DNS records on failure with propagation guidance. - CLI
domain addnow shows domain ID and the exact verify command to run next. - Input validation for the
importcommand (checks file exists and valid JSON before sending). - Plan limits section in CLI docs page explaining server-side enforcement.
Security
- Routine security hardening across authentication, the public API, webhook handlers, and platform-level defences.
Changed
- CLI package name changed from
xpolinkto@xpolink/clito match docs and reserve npm scope. - CLI version synced to main app version (1.1.2) — CLI and app share the same version number going forward.
- Dashboard sidebar version now reads from
package.jsonviaNEXT_PUBLIC_APP_VERSIONenv var (set innext.config.ts) instead of being hardcoded. - CLI docs page (
/docs/cli) fully rewritten to document all commands including exports, emails, domain lifecycle, and API key management. - API docs page (
/docs/api) fixed: removed non-existentlimitquery param from list links, addedredirectTypeto update endpoint, corrected delete description. - Help page "API & CLI" section updated with complete endpoint table (18 endpoints) and full CLI command list.
- Xpolink AI skill (our codebase) expanded from create-only to full API coverage: list, get, update, delete, stats, export, emails, domains, import, and API key management.
Fixed
Fixed
[1.1.2] - 2026-04-15
Changed
- Bumped
package.jsonversion from0.1.0to1.1.2to mark the first security-hardened, billing-ready release. Sidebar footer in the dashboard now displaysXpolink v1.1.2.
Security
- Routine security hardening across authentication, the public API, webhook handlers, and platform-level defences.
Security
- Routine security hardening across authentication, the public API, webhook handlers, and platform-level defences.
Added
samples/links-import-sample.csv— 20-row sample covering plain rows, custom slugs, long URLs with UTM params, quoted titles, HTTP and IDN hosts — for end-to-end testing of the bulk importer- our codebase — shared per-instance rate limiter used by both interstitial gates
- our codebase —
csvSafehelper to neutralise CSV formula injection across all exports
Fixed
- Edit Link dialog on the
/dashboard/linkslist now pre-populates existing geo routes. Previously the list page called<LinkActions>without thecurrentGeoRoutesprop, so the dialog opened with an empty routes list even when the link had routes saved. Fixed by batch-fetching geo routes for all geo-enabled links on the page in a single query (getGeoRoutesForLinks) and passing the resulting map through. The detail page was already correct. - UTM tracking now reads from the incoming short-link request URL (where marketers actually put
utm_source/utm_medium/utm_campaign) instead of the HTTP Referer header, which rarely contains them. The Referer stays as a fallback. - Short-link redirects now forward incoming query parameters to the destination URL so UTMs (and any other params on the short link) reach downstream analytics tools like GA and Mixpanel. Params already on the destination URL win to preserve marketer-set defaults; internal
_xpo_*keys are stripped. 301 responses with query params are now marked no-cache to keep analytics accurate.
Added
- Marketing surfaces (homepage features grid, pricing page, dashboard billing page, sign-in left panel) now promote: editable destination URL (Pro+), captured-email CSV export (Business), click-level analytics CSV export (Business), editing Business features on existing links (Business), and automatic abuse/phishing screening (all plans)
- Help section updated with: editing an existing link, reusing inactive short codes, captured email CSV export, clicks CSV export, editing Business features without recreating the link, and the safe-short-codes abuse screening explainer
- Profanity filter for short codes: blocks offensive/crude slugs across dashboard, public API, and CSV import with leet-speak normalization and a false-positive allowlist; auto-generated slugs also retry on blocked candidates
- Short-code blocklist now split into four categories — profanity, brand impersonation (PayPal/Amazon/crypto wallets/banks), phishing/scam lure words (verify/refund/login/wallet/kyc), and reserved routing words (api/admin/wp-admin/etc)
- Auto-generated short codes now use a vowel-less alphabet (
bcdfghjkmnpqrstvwxyz23456789) so random slugs can't accidentally spell real words; applied to bothgenerateShortCodeandfindShortestAvailableSlug - Inline profanity warning under the shortCode input in the create link form; submit button disables while the slug is blocked
Changed
- Redesigned email gate and password gate interstitials: white background, dark slate-900 card with blue glow, inline paper-plane icon (no outer box), Xpolink logo in "Powered by" footer
- Edit link dialog now supports toggling email gate, password, and expired redirect — gated to Business plan with a locked fieldset and upgrade CTA for free/pro users
- Edit link dialog: destination URL is editable on Pro and Business; expiry date, scheduled launch date, and geo-routing remain Business-only with a locked fieldset and upgrade CTA for other plans
- Edit dialog expires-at and scheduled-at now use enable toggles — the datetime input only appears once the checkbox is ticked, matching the create flow
- Create link form now supports setting an expiry date (Business plan) via the same toggle pattern
- Countdown (scheduled link) interstitial restyled to match the email gate: white card with blue glow, dark-blue clock icon, slate text
- Captured emails list shows for any link with historical submissions (not just links that currently have the gate enabled), so toggling a link off still preserves the analytics
Added
- Blog post topics: Bitly alternatives (KD 13), vanity URLs (KD 5), smart links/geo-targeting (KD 22), password-protected links (KD 24), click tracking (KD 24), link in bio tools (KD 31), how to create short links (KD 47), dynamic QR codes (KD 62), URL shortener API (KD 20), link management for teams (KD 72)
- Dynamic blog post route with generateMetadata for SEO title/description/OG tags and generateStaticParams for static generation
- Blog link added to marketing header nav and footer
- Each post includes internal cross-links, Xpolink feature mentions, and sign-up CTA
- GDPR compliance: lawful basis for processing, full GDPR rights enumeration, 30-day response commitment, data breach notification process (72-hour reporting)
- CCPA/CPRA compliance: California consumer rights, "Do Not Sell or Share" declaration, CCPA data categories table, 45-day response commitment
- Cookie consent banner on all pages with Accept/Decline buttons, linking to Privacy Policy cookies section
Added
- Email gate (lead capture): require visitors to enter their email before accessing a link (Business plan)
- Email gate owner notifications: optional email alert each time a visitor submits their email
- Scheduled links with countdown timer: set a go-live datetime; visitors see a live countdown until the link activates (Business plan)
- Geo-routing: route visitors to different destination URLs based on their country using a single short link (Business plan)
- Custom expired link redirects: redirect visitors to a fallback URL when a link expires instead of showing a 410 error (Business plan)
- Interstitial page architecture at
/i/[linkId]for email gate, password, and countdown screens - Captured emails API endpoints (
GET /api/v1/links/:id/emails,GET /api/v1/links/:id/emails/export) - Business feature gate component: shows locked features to Free/Pro users as upgrade prompts in the link creation form
- Geo-routing editor component with country dropdown and URL mapping
- Countries utility (our codebase) with 38 common countries
- Business feature status cards on link detail page (email count, password status, schedule, geo-routes, expired redirect)
- Business-only features shown as greyed-out items on Free/Pro pricing columns
- Interactive "Business Plan" vertical tabs on marketing landing page under "Everything you need" — auto-advancing with animated visuals for each feature (email gate, password, countdown, geo-routing, expired redirects), progress indicator, and click-to-pause
Added
- API documentation page (
/docs/api) with full REST API reference: authentication, all endpoints (links, domains, API keys), request/response examples, error codes, and rate limits - CLI documentation page (
/docs/cli) with installation, authentication, link/domain/import commands, and configuration reference - Privacy Policy page (
/privacy) covering data collection (account, link, click analytics, email gate, payment), usage, sharing, retention, cookies, security, and user rights - Terms of Service page (
/terms) with comprehensive abuse/acceptable use policy informed by Bitly and Rebrandly ToS, covering prohibited content (spam, phishing, malware, hate speech, doxxing, impersonation), prohibited practices (redirect chaining, circumvention), enforcement rights (terminate without notice at sole discretion), liability cap (12 months of fees), indemnification, and IP provisions - Footer links now active for API Documentation, CLI Tool, Privacy Policy, and Terms of Service
- Multi-page help section at
/dashboard/help/[section]with 10 pages: Getting Started, Adding a Domain, Creating Links, Analytics, Importing Links, Team Management, API & CLI, Business Features, AI Agents & Skills, and Billing & Plans - Help page left-side navigation with active state highlighting and prev/next page links at the bottom of each section
- Help link in sidebar footer (above theme toggle) with HelpCircle icon
- AI agent skill file — downloadable skill for Claude Code, Cursor, and other AI coding tools that creates short links via
/xpolinkcommand; stores API key in user's global~/.zshenv - Cross-domain dashboard on analytics page for Business users — shows per-domain link count, total clicks, and average clicks per link
- Non-Business users see a locked card prompting upgrade for the cross-domain dashboard
- Business features upsell on sign-in page left panel (email gate, password protection, scheduled links, geo-routing, expired redirects)
Fixed
- Bulk import now gated to Pro and Business plans — Free users see an upgrade prompt on the import page
- Browser breakdown chart now rendered in analytics UI (was queried but never displayed)
- Added OS breakdown pie chart to analytics for Pro/Business users (data was tracked but never aggregated or shown)
- Added Top Cities list to analytics for Pro/Business users (data was tracked but never aggregated or shown)
Changed
- Billing settings page updated to match marketing pricing page with new Business features and greyed-out excluded features on Free/Pro
- Business features section in create-link form is always visible (not collapsible) to maximize upgrade prompt visibility
- Redirect route now joins teams table to check plan for Business feature enforcement
- Links table schema expanded with emailGateEnabled, emailGateNotify, passwordHash, scheduledAt, geoRoutingEnabled, expiredRedirectUrl columns
- New database and database database tables
- Plan limits now include Business feature flags (emailGate, passwordLinks, scheduledLinks, geoRouting, expiredRedirect)
- Create/update link actions and API routes accept and validate Business feature fields with plan gating
- Compact theme toggle in marketing header (desktop nav + mobile)
Changed
- Home page link examples: xpol.link/launch (1st), xpol.app/demo (2nd), nisi.pro/closepusa (3rd); replaces go.acme.com and link.startup.io with Xpolink-branded domains
- Free plan link allowance lowered from 100 to 50 links/month; Pro plan lowered from 1,500 to 1,000 links/month (source of truth in
plan-limits.ts, also updated on marketing pricing page and in-app billing settings) - Create Link form card is full-width (removed
max-w-xlcap) and has extra padding below the Title field - Actions column heading in links table is now left-aligned like all other headings
- Dashboard top bar height bumped to h-16 so the sidebar logo can breathe; border stays continuous across sidebar and main header
- Add Domain placeholder now reads
xpol.appto hint at short TLDs
- "Before you buy a domain" helper block above the Add Domain form — explains short-TLD trade-offs, single-purpose use, and why reusing a website domain will break the website (rendered as its own card with lightbulb header and a 3-column grid of tips with coloured icon chips — blue for the positives, amber for the warning)
- Added spacing between the refresh icon and the "Verify" label on the domain card button
- Fixed missing space between bold "Verify" and the word "button" in the step-2/3 instruction text
- Domain add/verify/remove is now restricted to team owners at both the server-action layer and the UI layer; non-owners see a read-only list with a "contact your account owner" notice and no Add Domain form or action buttons
- Redesigned the empty Links list state with a large circular icon, headline, helper copy, and a primary "Create your first link" button (separate search-empty state with a search icon)
- Short Code Generate button now available for every domain in the create-link form (previously only for the shared domain);
/api/links/generate-slugaccepts an optionaldomainIdquery param and validates team ownership for custom domains - Auto-copy the freshly-created short URL to the clipboard after a successful Create Link submission
- Copy button in each row of the Links list (ghost button with copy icon that flips to a check on success)
- Instant search bar on the Links page (searches by short code, title, and destination URL); updates results as you type via URL query params with a clear button
- Filter the Links list by domain and by status (Active / Inactive) via a "Filter by" dropdown that reveals a second dropdown (domains or statuses) once a filter kind is picked; only options that actually have links appear (domains without links are hidden, Active/Inactive only shows if both exist); filters sync to URL query params and reset pagination, with a Clear filters button; the empty state shows "No matching links" when filters are active instead of "No links yet"
Fixed
- Edit link dialog: edit title and toggle active/inactive status from the ⋯ actions menu on both the links table and detail page; includes a destructive "Delete link" button with confirmation
- QR code generator: accessible from the ⋯ actions menu on every link; renders a QR code in a dialog with Download PNG (1024px canvas) and Download SVG export buttons
- Full ⋯ actions menu on every row in the links table (Edit, Copy, Test, QR Code, Deactivate/Activate, Delete) replacing the old inline Test + Copy buttons
- Test button in the Links table appends
?_xpo_test=1to the short URL; the redirect handler skips click tracking when that param is present, so testing from the dashboard doesn't pollute analytics - Hero gradient overlay on the home page now has
pointer-events-noneso the Start Free / View Pricing buttons are clickable
Removed
- Stray vertical separator next to the sidebar toggle in the dashboard header
ThemeToggleaccepts acompactprop for icon-only rendering
Added
- Theme toggle in dashboard sidebar (Light / Dark / System) via next-themes
- Blue-600 primary color tokens across light and dark themes so the dashboard inherits the marketing accent
- Teams system — each user belongs to one team; data (links, domains, API keys) scoped to teams
- Teams table with plan, billing, and usage tracking (moved from users table)
- Team members table with owner/member roles
- Team invites table with token-based invite links (7-day expiry)
- Team management page (
/dashboard/settings/team) — view members, invite, remove - Invite accept page (
/invite/[token]) — sign up/in and join a team - Owner-only guards on billing, team management, and invite actions
- "Team" nav item in dashboard sidebar
Changed
- Migrate all data queries from user-scoped (internal fields) to team-scoped (internal fields) — links, domains, API keys, and subscriptions are now owned by teams
- Rename
getLinksByUsertogetLinksByTeam,getDomainsByUsertogetDomainsByTeam,getDomainCount(userId)togetDomainCount(teamId),getRecentLinks(userId)togetRecentLinks(teamId),getLinkStats(userId)togetLinkStats(teamId) createLinkandcreateDomainnow accept both internal fields (owner) and internal fields (created by)- All dashboard pages use
requireTeamUserfor team-aware auth context - All API v1 routes use
team.idfor data scoping andteam.planfor plan limits - API key routes (
/api/v1/api-keys) userequireTeamUserand scope keys to teams - Plan limits and usage counters (internal fields) now read from and write to the
teamstable
Removed
- Remove
incrementLinksUsedandupdateUserPlanfrom user queries (billing fields moved to teams table) - Remove local
requireUserhelper from dashboard actions (replaced byrequireTeamUser)
Security
- Routine security hardening across authentication, the public API, webhook handlers, and platform-level defences.
Changed
Fixed
- Split
buttonVariantsout of our codebase into a newbutton-variants.tsso Server Components can import it without crossing the"use client"boundary (Next 16 RSC error) - Remove
"use server"directive from our codebase (the file isn't a Server Actions module;isOwneris sync) — replaced withimport "server-only" - Remove
ssr: falsedynamic import onLinkAnalyticsChartsindashboard/links/[id]/page.tsx(forbidden in Server Components in Next 16); direct client-component import replaces it - Remove unsupported
afterSignOutUrlprop from UserButton component - Remove default Next.js root page.tsx (conflicts with marketing route group)
- Combine redirect handler into single joined DB query (was 2 sequential round-trips on hot path)
- Stream analytics section via Suspense boundary (page shell renders before charts load)
Added
- CSV export endpoint for Business tier (
GET /api/v1/links/:id/export) - Export CSV button on link analytics page (Business plan only)
- Domain-based routing via proxy.ts for multi-tenant custom domain support
- Redirect handler with async click tracking via after
- Dashboard with sidebar navigation and overview stats
- Link CRUD with create form, list view, detail page with analytics
- DNS verification flow with automatic cron job (every 10 minutes)
- Tiered analytics: basic (free), full (pro), full + export (business)
- Plan limit enforcement for links per month and domains per account
- Monthly link counter reset via cron job
- REST API (v1) with API key authentication for links and domains
- Bulk import endpoint supporting CSV/JSON with Rebrandly/Bitly/Short.io column mapping
- Import UI in dashboard with CSV upload and result reporting
- API key management UI with create, list, and delete
- CLI tool (packages/cli) using commander for programmatic link/domain management
- Marketing landing page with hero, features, metrics, and how-it-works sections
- Pricing page with Free, Pro ($19/mo), and Business ($49/mo) tiers
- Dark mode by default throughout the application
- Environment variable template (.env.example)