Publishing & Editorial Event Spec
This page defines standard events for news sites, blogs, magazines, and content publishers. These events cover content consumption, search, newsletter subscriptions, paywall interactions, and reader engagement.
All events are sent via _df.track(). Properties listed here are sent inside the properties object.
Content Browsing Events
article_viewed
Fired when a user loads an article or content page. Send this as soon as the page renders, regardless of scroll depth.
_df.track('article_viewed', {
article_id: 'art-20260311-001',
title: 'The Future of Server-Side Tracking',
author: 'Jane Holloway',
category: 'Technology',
tags: ['tracking', 'privacy', 'adtech'],
published_at: '2026-03-11T09:00:00Z',
word_count: 1840,
paywall: false
})| Property | Type | Required | Description |
|---|---|---|---|
article_id | string | Required | Unique identifier for the article |
title | string | Optional | Article headline |
author | string | Optional | Author name or byline |
category | string | Optional | Primary section or category (e.g. Technology, Sport) |
tags | string[] | Optional | Array of topic tags associated with the article |
published_at | string | Optional | ISO 8601 publication date (e.g. 2026-03-11T09:00:00Z) |
word_count | number | Optional | Total word count of the article body |
paywall | boolean | Optional | Whether the article is behind a paywall or content gate |
article_read
Fired once when a user has scrolled to at least 80% of the article. Fire this event a maximum of once per article per session.
_df.track('article_read', {
article_id: 'art-20260311-001',
title: 'The Future of Server-Side Tracking',
read_time_seconds: 312,
scroll_depth: 87
})| Property | Type | Required | Description |
|---|---|---|---|
article_id | string | Required | Unique identifier for the article |
title | string | Optional | Article headline |
read_time_seconds | number | Optional | Time in seconds from article_viewed to reaching the read threshold |
scroll_depth | number | Optional | Maximum scroll depth reached, expressed as a percentage (0–100) |
Use article_read (not article_viewed) as the signal for engaged reads when optimising campaigns. A user who scrolls to 80% is a meaningfully different signal from a page bounce.
article_shared
Fired when a user shares an article via a share button or copy-link action.
_df.track('article_shared', {
article_id: 'art-20260311-001',
share_method: 'twitter'
})| Property | Type | Required | Description |
|---|---|---|---|
article_id | string | Required | Unique identifier for the article being shared |
share_method | string | Optional | Share channel. One of: twitter, facebook, email, copy_link |
article_bookmarked
Fired when a user saves an article to their bookmarks or reading list.
_df.track('article_bookmarked', {
article_id: 'art-20260311-001',
title: 'The Future of Server-Side Tracking'
})| Property | Type | Required | Description |
|---|---|---|---|
article_id | string | Required | Unique identifier for the bookmarked article |
title | string | Optional | Article headline |
category_viewed
Fired when a user browses a section, topic, or category listing page.
_df.track('category_viewed', {
category: 'Technology',
page: 2
})| Property | Type | Required | Description |
|---|---|---|---|
category | string | Required | Section or category name |
page | number | Optional | Pagination page number (1-indexed). Omit or set to 1 for the first page. |
Search Events
search_performed
Fired when a user submits a search query on the site.
_df.track('search_performed', {
query: 'climate policy',
results_count: 47,
filters: {
date_range: 'last_30_days',
category: 'Environment'
}
})| Property | Type | Required | Description |
|---|---|---|---|
query | string | Required | The search term entered by the user |
results_count | number | Optional | Number of results returned for the query |
filters | object | Optional | Key-value map of any filters applied to the search |
Newsletter Events
newsletter_subscribed
Fired when a user successfully subscribes to a newsletter or mailing list.
_df.track('newsletter_subscribed', {
list_id: 'daily-briefing',
email_hash: 'b4c9a...f1e2',
source: 'article'
})| Property | Type | Required | Description |
|---|---|---|---|
list_id | string | Optional | Identifier of the newsletter or list subscribed to |
email_hash | string | Optional | SHA-256 hash of the subscriber’s email address. Never send raw email addresses in event properties. |
source | string | Optional | Where the subscription was triggered. One of: inline, popup, footer, article |
Never pass a raw email address in event properties. Always hash with SHA-256 (lowercase, trimmed) before sending. This hashed value can be used by Meta CAPI and other platforms for identity matching.
newsletter_unsubscribed
Fired when a user unsubscribes from a newsletter or mailing list.
_df.track('newsletter_unsubscribed', {
list_id: 'daily-briefing',
reason: 'too_frequent'
})| Property | Type | Required | Description |
|---|---|---|---|
list_id | string | Optional | Identifier of the newsletter or list unsubscribed from |
reason | string | Optional | Reason provided by the user for unsubscribing |
Paywall & Subscription Events
paywall_viewed
Fired when a user encounters a content gate or paywall prompt, whether inline (content cut-off) or as a modal overlay.
_df.track('paywall_viewed', {
article_id: 'art-20260311-044',
subscription_type: 'digital',
position: 'modal'
})| Property | Type | Required | Description |
|---|---|---|---|
article_id | string | Required | Article that triggered the paywall |
subscription_type | string | Optional | Subscription tier or product required to access the content |
position | string | Optional | How the gate was presented. One of: inline, modal |
subscription_started
Fired when a user successfully starts a subscription (paid or trial).
_df.track('subscription_started', {
plan_id: 'digital-monthly',
plan_name: 'Digital Monthly',
revenue: 9.99,
currency: 'GBP',
trial: false
})| Property | Type | Required | Description |
|---|---|---|---|
plan_id | string | Optional | Machine-readable plan identifier |
plan_name | string | Optional | Human-readable plan name |
revenue | number | Optional | Subscription price charged at start |
currency | string | Optional | ISO 4217 currency code |
trial | boolean | Optional | Whether this is the start of a free trial |
subscription_cancelled
Fired when a user cancels their subscription.
_df.track('subscription_cancelled', {
plan_id: 'digital-monthly',
reason: 'too_expensive'
})| Property | Type | Required | Description |
|---|---|---|---|
plan_id | string | Optional | Identifier of the cancelled plan |
reason | string | Optional | Cancellation reason provided by the user |
Engagement Events
comment_posted
Fired when a user successfully submits a comment on an article.
_df.track('comment_posted', {
article_id: 'art-20260311-001',
comment_id: 'cmt-77241',
reply_to: 'cmt-77190'
})| Property | Type | Required | Description |
|---|---|---|---|
article_id | string | Required | Article the comment was posted on |
comment_id | string | Optional | Unique identifier for the new comment |
reply_to | string | Optional | comment_id of the parent comment, if this is a reply |
author_followed
Fired when a user follows or subscribes to updates from an author.
_df.track('author_followed', {
author_id: 'author-jane-holloway',
author_name: 'Jane Holloway'
})| Property | Type | Required | Description |
|---|---|---|---|
author_id | string | Required | Unique identifier for the author |
author_name | string | Optional | Display name of the author |