acme.store
How this site appears across your dashboard, and the timezone its reports use.
Add the script to every page on acme.store. The status updates live.
<script
defer
data-website-id="demo"
src="https://usedatasense.com/js/script.js"
></script>Count signups, checkouts or any action. Goals show up on the overview with their conversion rate.
// From JavaScript, after a key action
window.datasense?.("signup", { plan: "pro" })
// Or declaratively, on click
<button data-ds-goal="checkout" data-ds-goal-plan="pro">Buy</button>Recommended. Filter lists block analytics domains, not acme.store. Proxy a path through your own server: your framework or edge forwards two URLs to us, and the script detects the proxy on its own.
Pick a short, unique path. Filter lists match common analytics paths, not /_ds on your own domain.
// next.config.ts
import type { NextConfig } from "next"
const nextConfig: NextConfig = {
async rewrites() {
return [
{ source: "/_ds/js/script.js", destination: "https://usedatasense.com/js/script.js" },
{ source: "/_ds/api/e", destination: "https://usedatasense.com/api/e" },
]
},
}
export default nextConfig<script
defer
data-website-id="demo"
src="/_ds/js/script.js"
></script>Tie every payment back to the source, campaign and page that brought the buyer. Works with any provider.
// When the visitor starts checkout, send their session along
await fetch("/api/checkout", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
plan: "pro",
datasense_session_id: window.datasense?.sessionId?.(),
}),
})const session = await stripe.checkout.sessions.create({
line_items: [/* … */],
metadata: { datasense_session_id: body.datasense_session_id },
})await fetch("https://usedatasense.com/api/v1/payments", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.DATASENSE_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
amount: 29.99, // major units, not cents
currency: "USD",
transaction_id: payment.id, // makes retries safe
datasense_session_id: payment.metadata.datasense_session_id,
customer_id: payment.customer, // optional
renewal: false, // true for subscription renewals
// refunded: true, // send again with this for a refund
}),
})See when ChatGPT, Claude, Perplexity, Google and training crawlers read acme.store. Crawlers skip JavaScript, so your server reports them from its middleware. Human requests are never sent.
// proxy.ts (middleware.ts before Next.js 16)
import { NextResponse, type NextFetchEvent, type NextRequest } from "next/server"
const CRAWLER = /bot|crawl|spider|slurp|-user\b|claude|anthropic|cohere|meta-external|facebookexternalhit|google|bingpreview/i
export function proxy(request: NextRequest, event: NextFetchEvent) {
const userAgent = request.headers.get("user-agent") ?? ""
if (request.method === "GET" && CRAWLER.test(userAgent)) {
const host = request.headers.get("x-forwarded-host") ?? request.headers.get("host")
// Report in the background; never delay or fail the page.
event.waitUntil(
fetch("https://usedatasense.com/api/crawls", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
websiteId: "demo",
href: `https://${host}${request.nextUrl.pathname}`,
ai: {
userAgent,
// Only trust these headers behind a proxy that sets them (Vercel, Cloudflare, nginx).
ip: request.headers.get("cf-connecting-ip") ?? request.headers.get("x-real-ip") ??
request.headers.get("x-forwarded-for")?.split(",")[0]?.trim(),
source: "nextjs",
},
}),
signal: AbortSignal.timeout(2000),
}).catch(() => {}),
)
}
return NextResponse.next()
}
export const config = {
// Skip assets, but keep robots.txt, llms.txt and sitemaps: crawlers read those first.
matcher: ["/((?!api|_next/static|_next/image|favicon.ico).*)"],
}Raw rows for spreadsheets, notebooks or your warehouse. Your data is always yours.