Introduction
Convrs is a lightweight website analytics platform built for modern SaaS products.
Add a single script to your website and start tracking visitors, pageviews, conversions, custom events, and revenue attribution in real time.
See the Quickstart guide to get set up in under two minutes.
Quick Start
Get started with Convrs in under two minutes.
1. Create a Website
Create a website in your Convrs dashboard and copy your Website ID.
2. Add the Script
Paste the script into your website's <head> or before the closing </body> tag.
<script
async
src="https://convrs.dev/script.js"
data-website-id="YOUR_WEBSITE_ID">
</script>
3. Open Your Dashboard
Visit your Convrs dashboard and start exploring your analytics.
Data typically appears within a few seconds of the first pageview.
Replace
YOUR_WEBSITE_IDwith the Website ID from your Convrs dashboard.
HTML & Vanilla JS
The simplest installation — add the Convrs tracking script anywhere in your HTML.
<!DOCTYPE html>
<html>
<head>
<title>My Site</title>
<!-- Convrs Analytics -->
<script
async
src="https://convrs.dev/script.js"
data-website-id="YOUR_WEBSITE_ID">
</script>
</head>
<body>
<!-- Your content -->
</body>
</html>
Using
asyncensures the script never blocks page rendering.
Next.js
Use the built-in Next.js Script component so Convrs loads with the correct strategy.
App Router (Next.js 13+)
Add this to your app/layout.tsx:
import Script from 'next/script';
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en">
<body>
{children}
<Script
src="https://convrs.dev/script.js"
data-website-id="YOUR_WEBSITE_ID"
strategy="afterInteractive"
/>
</body>
</html>
);
}
Pages Router
Add this to pages/_app.tsx:
import Script from 'next/script';
import type { AppProps } from 'next/app';
export default function App({
Component,
pageProps,
}: AppProps) {
return (
<>
<Component {...pageProps} />
<Script
src="https://convrs.dev/script.js"
data-website-id="YOUR_WEBSITE_ID"
strategy="afterInteractive"
/>
</>
);
}
Convrs automatically tracks client-side route changes in Next.js.
React (Vite / CRA)
Install Convrs in your React application to start tracking pageviews, visitors, and custom events automatically.
Option A — index.html (Recommended)
The simplest approach is to add the Convrs script directly to your index.html file.
Vite
<!-- index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>My App</title>
</head>
<body>
<div id="root"></div>
<script
async
src="https://convrs.dev/script.js"
data-website-id="YOUR_WEBSITE_ID">
</script>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
Create React App
<!-- public/index.html -->
<body>
<div id="root"></div>
<script
async
src="https://convrs.dev/script.js"
data-website-id="YOUR_WEBSITE_ID">
</script>
</body>
Option B — Load via useEffect
If you prefer to load Convrs dynamically, add the script once when your application mounts.
import { useEffect } from 'react';
export default function App() {
useEffect(() => {
const script = document.createElement('script');
script.src = 'https://convrs.dev/script.js';
script.async = true;
script.setAttribute(
'data-website-id',
'YOUR_WEBSITE_ID'
);
document.head.appendChild(script);
return () => {
document.head.removeChild(script);
};
}, []);
return <div>Your App</div>;
}
Vue.js
index.html (Recommended)
<body>
<div id="app"></div>
<script
async
src="https://convrs.dev/script.js"
data-website-id="YOUR_WEBSITE_ID">
</script>
<script type="module" src="/src/main.ts"></script>
</body>
App.vue
<script setup>
import { onMounted } from 'vue';
onMounted(() => {
const script = document.createElement('script');
script.src = 'https://convrs.dev/script.js';
script.async = true;
script.setAttribute(
'data-website-id',
'YOUR_WEBSITE_ID'
);
document.head.appendChild(script);
});
</script>
<template>
<RouterView />
</template>
Convrs automatically tracks Vue Router navigation.
Nuxt.js
Use Nuxt's built-in head management to load Convrs on every page.
nuxt.config.ts (Recommended)
export default defineNuxtConfig({
app: {
head: {
script: [
{
src: 'https://convrs.dev/script.js',
'data-website-id': 'YOUR_WEBSITE_ID',
async: true,
},
],
},
},
});
app.vue
<script setup>
useHead({
script: [
{
src: 'https://convrs.dev/script.js',
'data-website-id': 'YOUR_WEBSITE_ID',
async: true,
},
],
});
</script>
<template>
<NuxtPage />
</template>
Convrs automatically tracks Nuxt route changes.
SvelteKit
Add the Convrs script to your root layout.
<!-- src/routes/+layout.svelte -->
<svelte:head>
<script
async
src="https://convrs.dev/script.js"
data-website-id="YOUR_WEBSITE_ID">
</script>
</svelte:head>
<slot />
Convrs automatically tracks client-side navigation and page changes.
Remix
Add Convrs to your root application layout.
import {
Links,
Meta,
Outlet,
Scripts,
ScrollRestoration,
} from "@remix-run/react";
export default function App() {
return (
<html lang="en">
<head>
<Meta />
<Links />
<script
async
src="https://convrs.dev/script.js"
data-website-id="YOUR_WEBSITE_ID"
/>
</head>
<body>
<Outlet />
<ScrollRestoration />
<Scripts />
</body>
</html>
);
}
Convrs automatically tracks route transitions in Remix applications.
Astro
Add Convrs to your base layout.
---
---
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<script
async
src="https://convrs.dev/script.js"
data-website-id="YOUR_WEBSITE_ID">
</script>
<slot name="head" />
</head>
<body>
<slot />
</body>
</html>
If you're using Astro View Transitions, add
transition:persistto the script tag to prevent reinitialization.
Gatsby
Install Convrs globally using Gatsby SSR APIs.
gatsby-ssr.js
import React from "react";
export const onRenderBody = ({
setHeadComponents,
}) => {
setHeadComponents([
<script
key="convrs"
async
src="https://convrs.dev/script.js"
data-website-id="YOUR_WEBSITE_ID"
/>,
]);
};
gatsby-browser.js
// Convrs automatically tracks route changes.
// No additional setup is required.
Gatsby uses the History API, which Convrs tracks automatically.
WordPress
There are two ways to install Convrs on WordPress.
Option A — functions.php
function convrs_analytics_script() {
echo '<script async src="https://convrs.dev/script.js" data-website-id="YOUR_WEBSITE_ID"></script>';
}
add_action('wp_head', 'convrs_analytics_script');
Option B — Plugin
Install a plugin such as:
- Insert Headers and Footers
- WPCode
Then paste:
<script
async
src="https://convrs.dev/script.js"
data-website-id="YOUR_WEBSITE_ID">
</script>
into the Header section.
If you're using caching plugins such as WP Rocket or W3 Total Cache, ensure the script remains present in cached pages.
Webflow
Webflow allows custom code to be added globally across your site.
Installation
- Open your Webflow project.
- Click Project Settings.
- Open the Custom Code tab.
- Paste the following into Head Code:
<script
async
src="https://convrs.dev/script.js"
data-website-id="YOUR_WEBSITE_ID">
</script>
- Save changes.
- Republish your website.
Convrs will immediately begin tracking pageviews once your site is published.
Shopify
Install Convrs across your entire Shopify storefront.
Installation
- Open your Shopify Admin.
- Navigate to Online Store → Themes.
- Select Actions → Edit Code.
- Open
layout/theme.liquid. - Paste the following before the closing
</head>tag:
<script
async
src="https://convrs.dev/script.js"
data-website-id="YOUR_WEBSITE_ID">
</script>
- Save the file.
Convrs automatically tracks storefront pages including product pages, collection pages, cart pages, and landing pages.
Convrs does not track activity inside the Shopify Admin dashboard.
NPM SDK
Install Convrs as an npm package for type-safe analytics tracking in JavaScript and TypeScript applications.
Perfect for React, Next.js, Vue, Nuxt, SvelteKit, Remix, Astro, and other modern web frameworks.
Why use the SDK?
- TypeScript support
- Framework agnostic
- Automatic pageview tracking
- User identification
- Offline-ready
Installation
npm install @convrs/sdk
Quick Start
import { initConvrs } from "@convrs/sdk";
const convrs = await initConvrs({
websiteId: "YOUR_WEBSITE_ID",
});
Track Events
convrs.track("signup", {
source: "homepage",
});
Identify Users
convrs.identify("user_123", {
email: "user@example.com",
plan: "pro",
});
React / Next.js Example
lib/analytics.ts
import { initConvrs } from "@convrs/sdk";
let convrs: any = null;
export async function getAnalytics() {
if (!convrs) {
convrs = await initConvrs({
websiteId:
process.env.NEXT_PUBLIC_CONVRS_WEBSITE_ID!,
autoCapturePageviews: true,
});
}
return convrs;
}
Automatic Pageview Tracking
const convrs = await initConvrs({
websiteId: "YOUR_WEBSITE_ID",
autoCapturePageviews: true,
});
Configuration Options
| Option | Type | Description |
|---|---|---|
websiteId | string | Your Convrs website ID |
domain | string | Override the current hostname |
apiUrl | string | Custom ingestion endpoint |
debug | boolean | Enable debug logging |
allowLocalhost | boolean | Enable localhost tracking |
allowIframe | boolean | Enable tracking inside iframes |
allowedHostnames | string[] | Domains used for cross-domain tracking |
Example Configuration
const convrs = await initConvrs({
websiteId: "YOUR_WEBSITE_ID",
debug: true,
allowLocalhost: true,
autoCapturePageviews: {
trackHashChanges: true,
},
});
See the API Reference for track(), identify(), trackPageview(), flush(), reset(), and cross-domain tracking.
Script Options
The Convrs tracking script accepts several optional data-* attributes to customize its behavior.
| Attribute | Required | Description |
|---|---|---|
data-website-id | Yes | Your unique Website ID from the Convrs dashboard. |
data-api | No | Override the default tracking endpoint. Useful for custom domains, proxies, or self-hosted ingestion. |
data-debug | No | Enable debug logging in the browser console. |
data-allow-localhost | No | Enable tracking while running on localhost. |
Example
<script
async
src="https://convrs.dev/script.js"
data-website-id="YOUR_WEBSITE_ID"
data-api="https://analytics.yourdomain.com/api/track"
data-debug="true"
data-allow-localhost="true"
data-allow-file-protocol="true">
</script>
Only
data-website-idis required. All other attributes are optional and should only be used when needed.
API Reference
The Convrs SDK exposes a simple API for tracking events, pageviews, and user identification.
track()
Track a custom event.
Syntax
convrs.track(eventName, properties?)
Parameters
| Parameter | Type | Description |
|---|---|---|
eventName | string | Name of the event. |
properties | object | Optional event metadata. |
Examples
convrs.track("signup");
convrs.track("purchase", {
amount: 99,
currency: "USD",
plan: "Pro",
});
identify()
Associate events with a user.
Syntax
convrs.identify(userId, traits?)
Parameters
| Parameter | Type | Description |
|---|---|---|
userId | string | Unique identifier for the user. |
traits | object | Optional user attributes. |
Examples
convrs.identify("user_123");
convrs.identify("user_123", {
email: "user@example.com",
name: "John Doe",
plan: "Pro",
});
trackPageview()
Manually track a pageview.
Most applications do not need this because pageviews are tracked automatically when
autoCapturePageviewsis enabled.
Track Current Page
convrs.trackPageview();
Track Custom Path
convrs.trackPageview("/pricing");
flush()
Immediately send all queued events.
Syntax
await convrs.flush();
Example
await convrs.flush();
Useful before:
- Page unloads
- Checkout redirects
- Critical conversion events
reset()
Reset the current visitor and session.
Syntax
convrs.reset();
Example
function logout() {
convrs.reset();
}
Useful when a user logs out.
getConvrsClient()
Access the initialized Convrs client anywhere in your application.
Syntax
import { getConvrsClient } from "@convrs/sdk";
const convrs = getConvrsClient();
Example
import { getConvrsClient } from "@convrs/sdk";
const convrs = getConvrsClient();
convrs.track("feature_used");
buildCrossDomainUrl()
Generate a URL containing Convrs tracking parameters.
Syntax
convrs.buildCrossDomainUrl(url);
Example
const url = convrs.buildCrossDomainUrl(
"https://app.example.com/signup"
);
Result:
https://app.example.com/signup?_cv_vid=xxx&_cv_sid=xxx
getTrackingParams()
Retrieve the current visitor and session identifiers.
Syntax
const params = convrs.getTrackingParams();
Example
const params = convrs.getTrackingParams();
console.log(params);
Result:
{
visitorId: "visitor_123",
sessionId: "session_456"
}
Complete Example
import { initConvrs } from "@convrs/sdk";
const convrs = await initConvrs({
websiteId: "YOUR_WEBSITE_ID",
});
convrs.identify("user_123", {
plan: "Pro",
});
convrs.track("signup");
convrs.track("purchase", {
amount: 99,
currency: "USD",
});
await convrs.flush();
Tracking Events
Beyond automatic pageview tracking, Convrs lets you send custom events to measure important user actions throughout your application.
Custom events can be used to track:
- Signups
- Purchases
- Feature usage
- Form submissions
- Button clicks
- Checkout flow events
- Marketing conversions
All custom events appear in your Convrs dashboard and can be used for funnels, conversion tracking, and revenue attribution.
Syntax
window.convrs(eventName, properties)
| Parameter | Type | Description |
|---|---|---|
eventName | string | Name of the event to track. |
properties | object | Optional metadata associated with the event. |
Event names should be descriptive and consistent. Examples:
signup,purchase,invite_sent,checkout_started.
Event Examples
Button Click
<button onclick="window.convrs('cta_clicked', { location: 'hero' })">
Get Started
</button>
Form Submission
document
.querySelector('#signup-form')
.addEventListener('submit', () => {
window.convrs('signup_submitted');
});
React — Button Click
function UpgradeButton() {
function handleClick() {
window.convrs('upgrade_clicked', {
plan: 'pro',
source: 'pricing_page',
});
// Open checkout
}
return (
<button onClick={handleClick}>
Upgrade to Pro
</button>
);
}
E-commerce — Add to Cart
window.convrs('add_to_cart', {
product_id: 'sku_1234',
product_name: 'Wireless Headphones',
price: 79.99,
currency: 'USD',
});
Purchase Completed
window.convrs('purchase', {
order_id: 'order_123',
amount: 99,
currency: 'USD',
plan: 'pro',
});
Video Played
document
.querySelector('#hero-video')
.addEventListener('play', () => {
window.convrs('video_played', {
video: 'hero_demo',
});
});
Checkout Started
window.convrs('checkout_started', {
plan: 'pro',
billing_cycle: 'monthly',
});
Invite Sent
window.convrs('invite_sent', {
team_size: 5,
});
Event Properties
Properties provide additional context for an event.
window.convrs('signup', {
plan: 'pro',
source: 'pricing_page',
campaign: 'summer_launch',
});
These properties can be used to better understand user behavior and conversion patterns.
Best Practices
- Use lowercase event names.
- Use snake_case for event names.
- Keep event names consistent across your application.
- Attach meaningful properties to important events.
- Avoid sending sensitive user information.
Good examples:
window.convrs('signup');
window.convrs('checkout_started');
window.convrs('purchase');
window.convrs('invite_sent');
Avoid:
window.convrs('Clicked Button');
window.convrs('Event 1');
window.convrs('test');
Convrs automatically tracks pageviews and route changes. Custom events should be used for actions that represent meaningful user behavior.
Modern AI tools read your site before most humans do. ChatGPT fetches your pricing page to answer a question about it, Googlebot re-indexes your docs, GPTBot pulls your blog for training data — Bot traffic tracking shows you all of it, broken down by who's asking and why.
Bot traffic tracking is included in your Convrs plan. It isn't a separate add-on.
Get started
Install the server-side package, add one call to your backend, deploy. This runs separately from Convrs's normal browser tracking script — bots skip your frontend JavaScript, so this has to run server-side.
Install @convrs/ai-bot-sdk on npm
1. Install
npm install @convrs/ai-bot-sdk
2. Add it to your server
Most common setup — Next.js middleware:
// middleware.ts
import { createBotTrackingMiddleware } from "@convrs/ai-bot-sdk";
const trackBots = createBotTrackingMiddleware({
siteId: "YOUR_SITE_ID",
});
export function middleware(
request: Request,
context: { waitUntil: (p: Promise<unknown>) => void }
) {
trackBots(request, context);
}
export const config = {
// Keep crawler-facing files reachable: robots.txt, llms.txt, sitemaps.
matcher: ["/((?!api|_next/static|_next/image|favicon.ico).*)"],
};
Don't await trackBots(...). Call it, let it run in the background, and
return your response immediately. Passing context lets the SDK use
waitUntil internally so tracking never adds latency to the real response.
3. Deploy and check your dashboard
After deploying, open your Convrs dashboard and look for the Bot traffic card. Filter by AI Answers, Indexing, or Training, and switch between the timeline and the per-provider breakdown.
If nothing shows up right away, that's expected — you're waiting for a real
crawler to visit, not a human pageview. Try curl-ing your own site with a
known bot's user-agent to confirm the pipeline end to end (see
Testing your setup below).
What gets tracked
Every detected bot is classified into one of four categories:
| Category | Example | What it tells you |
|---|---|---|
AI answers (answer_agent) | A user asks ChatGPT about your product; ChatGPT-User fetches your pricing page to answer accurately. | Which pages AI assistants pull in real time to answer user questions. |
Indexing (index_crawler) | Googlebot or PerplexityBot requests a page to refresh a search or answer index. | Who's discovering and re-indexing your content. |
Training (training_crawler) | GPTBot, ClaudeBot, Applebot, or Bytespider requests public pages. | Who's collecting your content for model training or large datasets. |
Other (other) | Anything recognized as bot traffic that doesn't cleanly fit the three buckets above. | Catch-all so nothing silently disappears from the dashboard. |
Classification covers 20+ vendors out of the box — OpenAI, Anthropic, Google, Perplexity, Microsoft, Apple, Amazon, Meta, xAI, Mistral, Baidu, Alibaba, ByteDance, DeepSeek, Cohere, Common Crawl, and more. New vendors get added to the SDK's registry over time with no config change needed on your end.
If a crawler keeps requesting a page that doesn't exist — /pricing-old,
/docs/quickstart — that's a signal worth acting on. It usually means
users or agents expect that page to exist somewhere.
Crawler-facing files
These are always tracked when a known bot requests them, even though they'd normally be filtered out as "not a real page":
/robots.txt/llms.txtand/llms-full.txt/sitemap.xmland other sitemap files- Any path ending in
.xmlunder/sitemap/or/sitemaps/
Everything else under your default-ignored prefixes (/api, /_next,
/static, /assets, and similar) and default-ignored file extensions
(.css, .js, .png, etc.) is skipped, since those are near-never what a
person actually means by "did a bot visit my site."
How it works
On every request, the SDK checks the method, the User-Agent, and the path — in that order, bailing out as early as possible — before deciding whether to send anything:
- Is this a
GET/HEADrequest? (configurable) - Does the User-Agent match a known bot? If not, stop here — nothing is sent.
- Is the path a static asset, an ignored prefix, or over the length limit?
- Send a small event in the background and return immediately.
The event itself only carries the raw User-Agent, IP, URL, and status code — vendor and category are always re-derived on the server, not trusted from the client. That keeps a caller from spoofing, say, a training crawler as an answer agent, and means crawler definitions can be updated on Convrs's side without every integration needing a package bump.
Tracking is best-effort. If the ingest endpoint is slow or unreachable, the SDK gives up quietly after a short timeout — it will never delay or break your actual response.
Platform examples
Next.js middleware
See Get started above.
Express
import express from "express";
import { createExpressBotMiddleware } from "@convrs/ai-bot-sdk";
const app = express();
app.use(createExpressBotMiddleware({ siteId: "YOUR_SITE_ID" }));
The Express adapter calls next() immediately, then attaches a finish
listener and sends the event after your response has already gone out — your
request handler never waits on it.
Generic Request/Response handler
If your framework gives you a request object up front and a response object later:
import { withBotTracking } from "@convrs/ai-bot-sdk";
export const GET = withBotTracking(
async (request: Request) => {
return new Response("ok");
},
{ siteId: "YOUR_SITE_ID" }
);
If you only have the request and not the eventual response, request-only tracking still captures which page the bot hit — it just won't include a status code:
import { trackBotRequest } from "@convrs/ai-bot-sdk";
export function middleware(request: Request, context: { waitUntil: Function }) {
trackBotRequest(request, context, { siteId: "YOUR_SITE_ID" });
}
Custom hosts behind an internal hostname
If your runtime hands the SDK an internal address (localhost,
0.0.0.0) instead of your public one, set it explicitly:
trackBotRequest(request, context, {
siteId: "YOUR_SITE_ID",
publicOrigin: "https://example.com",
});
Convrs still validates the resulting hostname against your site configuration and preserves the original path and query string.
Use without Node.js
Any backend that can send an HTTPS POST can report crawler traffic directly — no npm package required.
POST https://ingest.convrs.dev/api/ai-crawls
Content-Type: application/json
Authorization: Bearer cvbot_... (only if you've enabled required auth)
Request body:
{
"siteId": "YOUR_SITE_ID",
"domain": "example.com",
"url": "https://example.com/docs/get-started",
"referrer": null,
"bot": {
"userAgent": "Mozilla/5.0 ... ChatGPT-User/1.0",
"ip": "203.0.113.10",
"statusCode": 200,
"source": "server_middleware"
}
}
| Field | Required | Notes |
|---|---|---|
siteId | Yes | Your project token. websiteId is accepted as an alias. |
url | Yes | Absolute URL the crawler requested. Hostname must belong to your site. |
domain | Recommended | Hostname that received the request. Falls back to the URL's own hostname if omitted. |
bot.userAgent | Yes | Full raw User-Agent string. Classification happens from this alone. |
bot.ip | Recommended | Crawler's source IP as seen by your server. |
bot.statusCode | Optional | Your response status, 100-599. Omit if unavailable. |
bot.source | Optional | Free-form label for where this came from, e.g. server_middleware. |
Don't send vendor, agentName, or category — even if you compute them
yourself. They're always re-derived server-side and any value you send is
ignored, by design.
A tracked-or-safely-ignored request returns 202 with a body indicating
whether it was actually recognized as a bot:
{ "success": true, "tracked": true, "category": "training_crawler" }
or, for a user-agent that isn't a known bot:
{ "success": true, "tracked": false, "reason": "not_a_bot" }
Optional request authentication
Add a site-specific token without touching your existing integration:
- Open your Bot traffic card settings and generate a token.
- Add it to your SDK config or raw HTTP header.
- Once deployed, flip on Reject unauthenticated requests.
createBotTrackingMiddleware({
siteId: "YOUR_SITE_ID",
authToken: process.env.CONVRS_BOT_TOKEN,
});
Keep the cvbot_... token server-side only — never in frontend code, a
public repo, logs, or a URL. Enforcement is off until you explicitly enable
it, so adding a token doesn't change existing behavior on its own. Rotating
or deleting the token takes effect immediately.
Filtering categories
By default every category is tracked. Turn individual ones off if you only care about specific crawler types:
createBotTrackingMiddleware({
siteId: "YOUR_SITE_ID",
skipAnswerAgents: true,
skipIndexCrawlers: true,
skipTrainingCrawlers: true,
skipOtherBots: true,
});
Most sites should leave the defaults on and filter later in the dashboard — turning a category off here means that traffic is never sent at all, not just hidden.
Privacy checklist
- Call the ingest endpoint (directly or via the SDK) only from your backend. Never from browser JavaScript, and never for regular human traffic.
- The SDK only ever sends the fields listed above — it doesn't forward cookies, auth headers, request bodies, or other visitor data.
- Prefer URLs without query parameters where you can. If query params carry anything sensitive, strip them before the URL reaches the SDK.
- Treat any IP you pass as untrusted unless it's coming through a proxy you control that strips client-supplied forwarding headers.
Testing your setup
curl -X POST https://ingest.convrs.dev/api/ai-crawls \
-H "Content-Type: application/json" \
-d '{"siteId":"YOUR_SITE_ID","url":"https://example.com/","bot":{"userAgent":"GPTBot/1.1","source":"manual-test"}}'
Expect 202 with tracked: true. Then check your dashboard's Bot traffic
card for the new entry — it should land within a few seconds.
Where to find the data
Open your dashboard and look for the Bot traffic card. Switch between AI Answers, Indexing, and Training tabs, see the timeline for the selected category, and check the provider breakdown on the side for exact counts per vendor.
See which tweets drive your traffic and revenue
Referrer data full of generic t.co links doesn't tell you much. Convrs's X integration matches those links back to the actual tweets, so you can see exactly which posts are sending traffic — and revenue — to your site.
Requires a Convrs Growth plan.
Why this matters
A shortened link tells you nothing about what's actually working. With tweet-level attribution, you can:
- Spot your best-performing accounts — see who's consistently sending high-value traffic.
- See ROI at the tweet level — know which specific posts convert, not just which ones get clicks.
- Track campaigns properly — follow a hashtag push or social promo through to real traffic and revenue numbers.
How it works
Convrs continuously scans X for links to your site and ties the resulting traffic and revenue back to the tweet that sent it. To see it in action:
- Open the Referrers card on your main dashboard.
- Add an X filter to isolate that traffic.
There are two ways Convrs attributes X traffic:
1. Posts that link to your site
Any time someone tweets a link to your website, Convrs picks it up automatically — nothing to configure.
2. Link-in-bio attribution
If your site is linked in an X profile's bio (yours, a teammate's, or an affiliate's), add that handle so Convrs can attribute the traffic it sends:
- Go to Settings → Integrations.
- Find the Twitter / X card.
- Click the Link attribution tab.
- Add the handle(s) that link to your site, e.g.
@convrsdev.
Only add accounts that genuinely have your website link in their profile — your own account, or a trusted affiliate account. Attribution for existing traffic and revenue can take a few hours to backfill once you add a handle.
Go further
Once you know which tweets are driving results, pair this with X mentions tracking to catch every mention of your brand and product — even the ones that aren't linking directly to your site yet.
Stripe Revenue Attribution
Connect Stripe with Convrs to attribute revenue back to the campaigns, traffic sources, and visitors that generated it.
Once connected, Convrs automatically links successful payments to the original visitor and session, allowing you to see which channels, campaigns, and pages generate revenue.
How It Works
- A visitor lands on your website.
- Convrs assigns a unique visitor and session ID.
- You pass those IDs to Stripe when creating a checkout session or payment intent.
- Stripe sends the payment data to Convrs.
- Convrs attributes revenue to the original visitor, campaign, and traffic source.
Required Metadata
When creating a payment, include the following metadata:
| Key | Description |
|---|---|
convrs_visitor_id | Unique visitor identifier assigned by Convrs. |
convrs_session_id | Session identifier assigned by Convrs. |
These values allow Convrs to connect a Stripe payment to the visitor who originally started the journey.
Stripe Checkout
If you're using Stripe Checkout, include the Convrs identifiers in the checkout session metadata.
const session = await stripe.checkout.sessions.create({
line_items: [
{
price: "price_123",
quantity: 1,
},
],
mode: "payment",
success_url: "https://yourapp.com/success",
cancel_url: "https://yourapp.com/cancel",
metadata: {
convrs_visitor_id: visitorId,
convrs_session_id: sessionId,
},
});
Payment Intents
For custom checkout flows, pass the identifiers when creating the payment intent.
const paymentIntent =
await stripe.paymentIntents.create({
amount: 5000,
currency: "usd",
metadata: {
convrs_visitor_id: visitorId,
convrs_session_id: sessionId,
},
});
Getting Visitor Information
If you're using the Convrs SDK, retrieve the tracking identifiers before creating the checkout session.
const tracking =
convrs.getTrackingParams();
console.log(tracking);
Example response:
{
visitorId: "visitor_123",
sessionId: "session_456"
}
Recommended Flow
Visitor arrives
↓
Convrs creates visitor + session
↓
User starts checkout
↓
Pass IDs to Stripe metadata
↓
Payment succeeds
↓
Revenue appears in Convrs
Verify Revenue Attribution
After a successful payment, open your Convrs dashboard and navigate to:
Revenue Attribution → Payments
You should see:
- Revenue amount
- Customer information
- Original traffic source
- Campaign attribution
- Landing page
- Visitor journey
Revenue attribution works for one-time payments, subscriptions, upgrades, and renewals as long as the Convrs identifiers are included in the Stripe metadata.