Website Tracking
Track page views and custom events on your website with a lightweight JavaScript snippet. Tie anonymous visitors to known contacts.
PushMail's tracking snippet is a lightweight JavaScript tag (~1.5 KB) that you embed on your website. It automatically tracks page views, supports custom events, and links anonymous visitors to known contacts when they identify themselves (e.g. via a signup form).
Quick setup
Get your site ID
Every site in PushMail has a numeric ID. Find it in the dashboard under Sites, or via the API:
curl https://pushmail.dev/api/v1/sites \
-H "Authorization: Bearer pm_live_YOUR_KEY"Note the id field in the response.
Embed the tracking snippet
Add this script tag to every page you want to track, just before the closing </body> tag:
<script src="https://pushmail.dev/api/v1/tracking/script?siteId=YOUR_SITE_ID"></script>Replace YOUR_SITE_ID with your actual site ID (e.g. 1, 42).
That's it. Page views are now being tracked automatically, including SPA navigation via the History API.
Identify visitors (optional)
When a visitor logs in or submits a form, call pushmail.identify() to link their anonymous session to a contact:
<script>
pushmail.identify('user@example.com', {
firstName: 'Jane',
plan: 'pro'
});
</script>This creates or updates the contact and re-attributes all past anonymous page views to them.
JavaScript API
The snippet exposes a global pushmail object with three methods:
pushmail.track(event, properties?)
Track a custom event.
// Track a custom event
pushmail.track('viewed_pricing');
// With properties
pushmail.track('added_to_cart', {
product: 'Enterprise Plan',
value: 299
});Page views are tracked automatically on load and on SPA navigation. You can also call pushmail.page() manually.
pushmail.identify(email, properties?)
Link the current anonymous visitor to a contact by email. If the contact does not exist, it is created automatically with source: "tracking_snippet".
pushmail.identify('jane@example.com', {
firstName: 'Jane',
lastName: 'Doe',
plan: 'starter',
company: 'Acme Inc'
});Properties are merged into the contact's customFields. The special keys firstName and lastName are mapped to the corresponding contact fields when creating a new contact.
pushmail.page(properties?)
Explicitly track a page view. This is called automatically on page load and SPA navigation, but you can call it manually if needed.
pushmail.page({ section: 'docs' });REST API endpoints
The tracking snippet communicates with two public endpoints. You can also call these directly from your backend if you prefer server-side tracking.
POST /api/v1/tracking/event
Record a tracking event.
curl -X POST https://pushmail.dev/api/v1/tracking/event \
-H "Content-Type: application/json" \
-d '{
"siteId": 1,
"anonymousId": "pm_abc123",
"event": "page_view",
"url": "https://example.com/pricing",
"referrer": "https://google.com",
"properties": { "campaign": "spring-sale" }
}'| Field | Type | Required | Description |
|---|---|---|---|
siteId | number | Yes | Your site ID |
event | string | Yes | Event name (page_view, custom names) |
anonymousId | string | No* | Anonymous visitor ID (from localStorage) |
email | string | No* | Contact email (alternative to anonymousId) |
url | string | No | Page URL |
referrer | string | No | Referrer URL |
properties | object | No | Custom event properties (stored as JSON) |
* At least one of anonymousId or email is required.
POST /api/v1/tracking/identify
Link an anonymous visitor to a known contact.
curl -X POST https://pushmail.dev/api/v1/tracking/identify \
-H "Content-Type: application/json" \
-d '{
"siteId": 1,
"email": "jane@example.com",
"anonymousId": "pm_abc123",
"properties": {
"firstName": "Jane",
"plan": "pro"
}
}'| Field | Type | Required | Description |
|---|---|---|---|
siteId | number | Yes | Your site ID |
email | string | Yes | Contact email address |
anonymousId | string | No | Anonymous ID to re-attribute past events |
properties | object | No | Custom fields to set on the contact |
GET /api/v1/tracking/script
Returns the JavaScript tracking snippet. Not typically called directly -- use the <script> tag instead.
| Param | Type | Required | Description |
|---|---|---|---|
siteId | number | Yes | Your site ID (query parameter) |
How it works
- When the script loads, it generates a random anonymous ID and stores it in
localStorageunder the keypm_aid. - On every page load (and SPA navigation), it sends a
page_viewevent to/api/v1/tracking/eventwith the anonymous ID, URL, and referrer. - When you call
pushmail.identify(email), it sends the email and anonymous ID to/api/v1/tracking/identify. PushMail looks up or creates the contact, then re-attributes all past anonymous page views to them. - After identification, subsequent
track()calls still use the anonymous ID, but the server can join them to the contact via thepage_viewstable.
Rate limits
The tracking endpoints are rate limited per site to prevent abuse:
| Endpoint | Limit |
|---|---|
/tracking/event | 100 requests/minute per site |
/tracking/identify | 30 requests/minute per site |
These are public endpoints and do not require an API key. They are validated by siteId.
Security
- The tracking endpoints are intentionally public (no API key needed) so the snippet can run on any website.
- The
siteIdis visible in the script tag URL. This is safe -- it only allows writing tracking events, not reading data. - Rate limiting prevents abuse.
- No cookies are used. The anonymous ID is stored in
localStorageonly.
Framework integration examples
Next.js (App Router)
import Script from 'next/script';
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html>
<body>
{children}
<Script
src="https://pushmail.dev/api/v1/tracking/script?siteId=1"
strategy="afterInteractive"
/>
</body>
</html>
);
}React SPA
import { useEffect } from 'react';
function App() {
useEffect(() => {
const script = document.createElement('script');
script.src = 'https://pushmail.dev/api/v1/tracking/script?siteId=1';
script.async = true;
document.body.appendChild(script);
}, []);
// After user logs in:
function onLogin(user: { email: string; name: string }) {
window.pushmail?.identify(user.email, { firstName: user.name });
}
return <div>...</div>;
}Server-side (Node.js)
If you prefer server-side tracking, call the endpoints directly:
async function trackEvent(siteId: number, email: string, event: string) {
await fetch('https://pushmail.dev/api/v1/tracking/event', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ siteId, email, event }),
});
}
// Track a purchase
await trackEvent(1, 'jane@example.com', 'purchase');DNS Setup & Troubleshooting
Configure DNS records for email authentication, click tracking, and deliverability. Provider-specific instructions and fixes for common issues.
Email Click & Open Tracking
Track clicks and opens in your emails with custom branded domains and automatic SSL. No CDN setup, no support tickets.