UTM Data
Purpose and Scope
UTM data in Dub is the campaign-attribution layer that sits on top of the same analytics system used for clicks, leads, sales, and other link events. A UTM parameter is a query parameter such as source, medium, campaign, term, or content that marketers add to destination URLs so they can compare where traffic came from and which campaigns produced outcomes. In Dub, those dimensions are useful because links are already the unit of attribution: the same short link that records a click can also be grouped, filtered, and compared by UTM metadata when campaign reporting is needed.
Sources: apps/web/lib/analytics/types.ts, apps/web/lib/openapi/analytics/index.ts
This page focuses on how UTM reporting fits into the repository-visible analytics surfaces rather than on every visual detail of the dashboard. The supplied source shows a shared analytics type layer, an OpenAPI operation for retrieving analytics, a dashboard analytics shell that renders referrer and UTM cards together, utility exports for export and query-string behavior, and plan-aware client wrappers. The official Dub docs describe UTM analytics as a way to analyze marketing effectiveness by source, medium, campaign, term, and content, with API examples that retrieve top UTM sources by event count.
Relevant Source Files
apps/web/app/app.dub.co/(dashboard)/[slug]/links/analytics/client.tsx- Client boundary for the link analytics route; it checks workspace loading state, event overage state, and plan behavior before rendering analytics content.apps/web/lib/analytics/types.ts- Shared TypeScript types for analytics filters, event types, group-by options, response options, date intervals, and event-stream filters.apps/web/lib/analytics/utils/index.ts- Barrel export for analytics utilities, including CSV conversion, export formatting, query-string editing, interval data, and plan/date-range validation helpers.apps/web/lib/openapi/analytics/index.ts- OpenAPI path definition forGET /analytics, including theretrieveAnalyticsoperation, query schema wiring, response union, tags, and token security.apps/web/ui/analytics/events/index.tsx- Real-time events analytics UI entry point; it wraps events views inAnalyticsProviderand applies upgrade messaging for detailed event streams.apps/web/ui/analytics/index.tsx- Main analytics dashboard component; it composes chart, top-link, referrer/UTM, location, and device sections inside the shared analytics provider.
Core Concepts
Dub analytics starts with an event. In the shared analytics types, AnalyticsResponseOptions includes clicks, leads, sales, and saleAmount, while EventType is derived from the repository’s analytics event constants. UTM reporting should be read in that context: a UTM source report for clicks answers “which campaign source drove traffic,” while the same attribution vocabulary becomes more valuable when paired with leads or sales. The type layer also defines AnalyticsGroupByOptions from VALID_ANALYTICS_ENDPOINTS, which is the public TypeScript bridge between dashboard/API query construction and the set of supported analytics groupings.
Sources: apps/web/lib/analytics/types.ts
The official docs use groupBy: "utm_sources" with event: "clicks", linkId, and interval to retrieve the top UTM sources by event count. That shape mirrors the repository’s OpenAPI structure: the analytics endpoint accepts a query schema rather than a JSON body, and the response type depends on query choices. For readers building reports, the important distinction is that the event determines what is being counted, while the UTM grouping determines how those counts are partitioned. Link, domain, workspace, interval, start, and end filters then scope the population being measured.
System-to-Code Mapping
The user-facing analytics dashboard is assembled in apps/web/ui/analytics/index.tsx. The component wraps its content in AnalyticsProvider, renders an AnalyticsToggle, then places ChartSection and StatsGrid inside the page layout. StatsGrid includes ReferrersUTMs, LocationSection, and DeviceSection, which is the key placement signal for UTM data: campaign dimensions are presented beside referrer, geography, and device breakdowns rather than as a separate reporting product. This makes UTM analysis one dimension of the broader link-attribution dashboard, not a disconnected export-only feature.
Sources: apps/web/ui/analytics/index.tsx
The analytics API is exposed through apps/web/lib/openapi/analytics/index.ts as GET /analytics with operation id retrieveAnalytics and Speakeasy name override retrieve. The operation summary says it retrieves analytics for a link, a domain, or the authenticated workspace, and the description states that response type depends on event and type query parameters. The supplied response union includes count, timeseries, geography, device, referrer, top-link, and top-url families. UTM grouping is therefore best understood as query-driven analytics behavior connected to the same OpenAPI path used by other analytics dimensions.
Sources: apps/web/lib/openapi/analytics/index.ts
Dashboard Flow
A dashboard user typically encounters UTM data through the main analytics surface rather than by starting with the API. The analytics component comments identify three contexts where the component is reused: the workspace analytics page, a public stats page, and a partner program links page. That reuse matters because UTM reporting may be viewed by different audiences: a workspace operator investigating campaign performance, a public stats viewer looking at shared link performance, or a partner-program user reviewing attributed traffic. The same provider-driven component tree supplies the surrounding chart, toggle, and dimension cards across those contexts.
Sources: apps/web/ui/analytics/index.tsx
Plan behavior is part of the experience. The route-level analytics client reads workspace state with useWorkspace; while the workspace is loading it renders LayoutLoader, and when the workspace has exceeded events it renders WorkspaceExceededEvents except for a Pro-plan events-page exception. Inside the main stats grid, Dub hides parts of the dashboard when the selected tab is leads or sales, or when the view is funnel, for free and pro plans. This means UTM reporting should be explained alongside analytics entitlements: readers may be able to view click-oriented campaign breakdowns while deeper conversion or event-stream views require a higher plan.
Sources: apps/web/app/app.dub.co/(dashboard)/[slug]/links/analytics/client.tsx, apps/web/ui/analytics/index.tsx
API Retrieval Pattern
For API users, the repository-visible contract begins at GET /analytics. The OpenAPI object wires analyticsQuerySchema into requestParams.query, returns JSON, applies the Analytics tag, and requires token security. A practical UTM request follows the same pattern as other analytics retrieval calls: choose an event, choose a grouping, scope the query to a link, domain, or workspace, and choose an interval or date range. The official docs example uses event: "clicks", groupBy: "utm_sources", linkId: "clux0rgak00011...", and interval: "30d" to ask for the top UTM sources for a link.
Sources: apps/web/lib/openapi/analytics/index.ts, apps/web/lib/analytics/types.ts
import { Dub } from "dub";
const dub = new Dub({
token: process.env.DUB_API_KEY,
});
const result = await dub.analytics.retrieve({
event: "clicks",
groupBy: "utm_sources",
linkId: "clux0rgak00011...",
interval: "30d",
});Treat the returned shape as selected by the analytics query rather than as a single fixed response model. The OpenAPI definition intentionally declares a union of analytics response families, and the local AnalyticsResponse type maps keys from analyticsResponse to inferred zod types. That approach lets one endpoint power totals, timeseries charts, location tables, device cards, referrer cards, and campaign groupings. When implementing a UTM report, keep the event and grouping explicit in application code so downstream formatting, chart labels, and CSV exports know whether a row represents a campaign source, medium, campaign, term, or content dimension.
Sources: apps/web/lib/openapi/analytics/index.ts, apps/web/lib/analytics/types.ts
Exports, Query State, and Reporting Workflows
UTM reporting often leaves the dashboard: teams compare campaign data in spreadsheets or BI tools, share reports with marketing stakeholders, or reconcile link attribution with paid media systems. The analytics utilities barrel exports convert-to-csv, format-analytics-export, and edit-query-string, which signals that the analytics subsystem has shared helpers for turning analytics data into downloadable formats and for keeping filter state in URLs. Those exports are not UTM-specific in the snippet, but they are the correct repository area to inspect when building or debugging report exports that include UTM-selected analytics views.
Sources: apps/web/lib/analytics/utils/index.ts
The event-stream UI is related but not the same as UTM aggregate reporting. apps/web/ui/analytics/events/index.tsx renders an events page with tabs and a table, and it shows an upgrade overlay for a “Real-time Events Stream” when the workspace plan is free or pro. Aggregate UTM analytics answer questions like “which source drove the most clicks over 30 days,” while the events stream answers row-level questions about individual clicks, QR scans, leads, or sales. Use the aggregate endpoint and ReferrersUTMs dashboard area for campaign summaries; use the events surface when investigating individual event records and real-time detail.
Sources: apps/web/ui/analytics/events/index.tsx
Compact Reference
| Surface | Concrete contract | How it applies to UTM analytics |
|---|---|---|
| OpenAPI path | GET /analytics | Single retrieval endpoint used for link, domain, or workspace analytics. |
| Operation id | retrieveAnalytics | Generated SDKs expose the analytics retrieval operation from this source definition. |
| SDK naming hint | x-speakeasy-name-override: "retrieve" | Aligns generated SDK calls with analytics.retrieve(...) examples. |
| Query schema | analyticsQuerySchema | Carries event, grouping, interval/date, and scope parameters. |
| Event response options | clicks, leads, sales, saleAmount | Defines the main measured outcomes that UTM dimensions can contextualize. |
| Grouping type | AnalyticsGroupByOptions | Type-level representation of valid analytics groupings. |
| Dashboard component | ReferrersUTMs | Renders referrer and UTM dimensions inside the main analytics stats grid. |
| Utility exports | convert-to-csv, format-analytics-export, edit-query-string | Shared helpers for export and filter/query-state workflows. |
Implementation Notes and Next Steps
When adding or changing UTM analytics behavior, start from the shared query and response contracts rather than from an isolated UI card. The dashboard, SDK documentation, and exports should agree on the same event names, grouping names, and date-range semantics. If you add a new UTM grouping or label, verify that the analytics query schema accepts it, the OpenAPI operation documents it, the dashboard can render it within the referrer/UTM section, and export formatting produces understandable column names. Also check plan-gated branches so conversion-oriented UTM reporting does not appear in a dashboard state where the underlying event data is intentionally hidden.
Sources: apps/web/lib/analytics/types.ts, apps/web/lib/openapi/analytics/index.ts, apps/web/ui/analytics/index.tsx, apps/web/lib/analytics/utils/index.ts
Read related analytics pages next if you need the surrounding dimensions: Analytics Overview for the pipeline model, Referrers Data for traffic-source comparisons, Conversion Tracking Events for lead and sale semantics, and Analytics API for the full retrieval contract. For product users, the first task is to standardize UTM parameters on destination URLs, then review the UTM Parameters area in the analytics dashboard or retrieve grouped analytics through analytics.retrieve. For developers, the first task is to trace a concrete query from analyticsQuerySchema through GET /analytics and into the dashboard component that consumes the selected grouping.