SaaS / B2B Event Spec
This page defines all standard events for SaaS and B2B software products on Datafly Signal. The taxonomy covers the full customer lifecycle — discovery and lead capture, sign-up, trial, onboarding, subscription, in-product usage, retention, and team collaboration. Implement these events to enable accurate server-side delivery to advertising and analytics platforms.
Event names reuse GA4’s recommended names wherever there is an overlap (view_item, add_to_cart, purchase, search, sign_up, login, generate_lead). If you’re migrating from Google Tag Manager, those event names work as-is. SaaS-specific events use snake_case and map to vendor-native conversions in the Vendor Mapping table.
All events are sent via datafly.track(). Properties are passed as a flat object in the second argument. All event names and property names use snake_case.
Never put raw PII (email, phone, name) in track() properties. Hash identifiers with SHA-256 (trim + lowercase first) before sending, and rely on Signal’s first-party identity layer for user stitching.
Discovery & Marketing
Events fired on your marketing site and pricing pages, before a user has an account.
view_item
Fired when a visitor views a marketing page for a specific product, plan, or feature. Also accepted as view_content.
datafly.track('view_item', {
item_id: 'plan_pro',
item_name: 'Pro',
item_category: 'pricing',
content_type: 'product'
})| Property | Type | Required | Description |
|---|---|---|---|
item_id | string | Optional | Identifier of the product, plan, or feature viewed |
item_name | string | Optional | Human-readable name (e.g. Pro) |
item_category | string | Optional | Page or content category (e.g. pricing, feature) |
content_type | string | Optional | Type of content viewed (e.g. product, plan, feature) |
plan_viewed
Fired when a user views a specific pricing plan or tier on the pricing page.
datafly.track('plan_viewed', {
plan_id: 'plan_pro',
plan_name: 'Pro',
billing_cycle: 'monthly',
price: 49.00,
currency: 'GBP'
})| Property | Type | Required | Description |
|---|---|---|---|
plan_id | string | Optional | Internal identifier for the plan |
plan_name | string | Optional | Human-readable plan name (e.g. Pro) |
billing_cycle | string | Optional | Cycle shown (monthly or annual) |
price | number | Optional | Displayed price of the plan |
currency | string | Optional | ISO 4217 currency code (e.g. GBP, USD) |
search
Fired when a user submits a search query against your site, docs, or in-app search.
datafly.track('search', {
search_term: 'webhook integration',
context: 'docs'
})| Property | Type | Required | Description |
|---|---|---|---|
search_term | string | Required | The search term entered by the user |
context | string | Optional | Where the search ran (e.g. marketing, docs, app) |
view_promotion
Fired when a promotional banner, offer, or campaign creative is visible to the user.
datafly.track('view_promotion', {
promotion_id: 'LAUNCH20',
promotion_name: 'Launch 20% Off',
creative_name: 'pricing-banner-v2',
creative_slot: 'pricing-hero'
})| Property | Type | Required | Description |
|---|---|---|---|
promotion_id | string | Optional | Identifier for the promotion |
promotion_name | string | Optional | Human-readable name of the promotion |
creative_name | string | Optional | Name or URL of the creative asset shown |
creative_slot | string | Optional | Slot or placement where the promotion appeared |
Lead Generation
Events that capture marketing-qualified intent: demo requests, content downloads, and form fills.
generate_lead
Fired when a user submits a lead-capture form (contact sales, newsletter, gated content). This is the primary top-of-funnel conversion for B2B campaigns.
datafly.track('generate_lead', {
lead_source: 'pricing_page',
form_id: 'contact_sales',
campaign: 'q3_enterprise',
value: 0,
currency: 'GBP'
})| Property | Type | Required | Description |
|---|---|---|---|
lead_source | string | Optional | Where the lead originated (e.g. pricing_page, blog, webinar) |
form_id | string | Optional | Identifier of the form submitted |
campaign | string | Optional | Marketing campaign associated with the lead |
value | number | Optional | Estimated lead value for value-based bidding |
currency | string | Optional | ISO 4217 currency code |
Do not include the submitted email, phone, or name in generate_lead properties. Signal hashes and forwards contact identifiers separately via the identity layer.
demo_requested
Fired when a user requests a product demo or books a sales call.
datafly.track('demo_requested', {
lead_source: 'homepage',
company_size: '50-200',
plan_interest: 'enterprise',
campaign: 'q3_enterprise'
})| Property | Type | Required | Description |
|---|---|---|---|
lead_source | string | Optional | Where the demo request originated |
company_size | string | Optional | Self-reported company size band (e.g. 50-200) |
plan_interest | string | Optional | Plan or tier the prospect is interested in |
campaign | string | Optional | Marketing campaign associated with the request |
content_downloaded
Fired when a user downloads gated content such as a whitepaper, ebook, or template.
datafly.track('content_downloaded', {
content_id: 'wp_saas_attribution',
content_name: 'The SaaS Attribution Playbook',
content_type: 'whitepaper',
lead_source: 'blog'
})| Property | Type | Required | Description |
|---|---|---|---|
content_id | string | Optional | Stable identifier for the content asset |
content_name | string | Optional | Human-readable title of the content |
content_type | string | Optional | Type of asset (e.g. whitepaper, ebook, template) |
lead_source | string | Optional | Where the download was triggered |
Authentication
Events relating to user sign-up, sign-in, and account access.
sign_up
A new user has created an account.
datafly.track('sign_up', {
method: 'google',
plan: 'pro',
referral_code: 'FRIEND2026'
})| Property | Type | Required | Description |
|---|---|---|---|
method | string | Optional | Registration method: email, google, sso |
plan | string | Optional | Plan selected at sign-up, if any (e.g. starter, pro) |
invited_by | string | Optional | User ID of the person who sent the invitation |
referral_code | string | Optional | Referral code applied during sign-up |
login
A user has successfully authenticated.
datafly.track('login', {
method: 'sso'
})| Property | Type | Required | Description |
|---|---|---|---|
method | string | Optional | Authentication method (e.g. email, sso, magic_link) |
logout
A user has ended their session.
datafly.track('logout')No additional properties.
password_reset_requested
A user has requested a password reset email.
datafly.track('password_reset_requested')No additional properties.
Onboarding
Events tracking activation — the steps between sign-up and first value.
onboarding_started
A user has begun the onboarding or setup flow after creating an account.
datafly.track('onboarding_started', {
flow_id: 'default_onboarding',
plan: 'pro',
total_steps: 5
})| Property | Type | Required | Description |
|---|---|---|---|
flow_id | string | Optional | Identifier of the onboarding flow entered |
plan | string | Optional | Plan the user is onboarding onto |
total_steps | number | Optional | Number of steps in the onboarding flow |
onboarding_step_completed
A user has completed a single step within the onboarding flow.
datafly.track('onboarding_step_completed', {
flow_id: 'default_onboarding',
step_id: 'connect_data_source',
step_index: 2,
total_steps: 5
})| Property | Type | Required | Description |
|---|---|---|---|
flow_id | string | Optional | Identifier of the onboarding flow |
step_id | string | Optional | Identifier of the completed step |
step_index | number | Optional | Position of the step in the flow (0-indexed) |
total_steps | number | Optional | Number of steps in the onboarding flow |
onboarding_completed
A user has completed the full onboarding flow and reached activation.
datafly.track('onboarding_completed', {
flow_id: 'default_onboarding',
plan: 'pro',
steps_completed: 5,
time_to_complete_seconds: 420
})| Property | Type | Required | Description |
|---|---|---|---|
flow_id | string | Optional | Identifier of the onboarding flow completed |
plan | string | Optional | Plan the user onboarded onto |
steps_completed | number | Optional | Number of steps the user completed |
time_to_complete_seconds | number | Optional | Total time from start to completion in seconds |
Trial & Subscription
Events relating to trial activation, conversion, and subscription lifecycle.
trial_started
A user has entered a free trial period.
datafly.track('trial_started', {
plan_id: 'plan_pro',
plan_name: 'Pro',
trial_length_days: 14
})| Property | Type | Required | Description |
|---|---|---|---|
plan_id | string | Optional | Internal identifier for the plan |
plan_name | string | Optional | Human-readable plan name (e.g. Pro Trial) |
trial_length_days | number | Optional | Number of days in the trial |
trial_converted
A trial user has upgraded to a paid subscription.
datafly.track('trial_converted', {
plan_id: 'plan_pro_monthly',
plan_name: 'Pro Monthly',
revenue: 49.00,
currency: 'GBP',
payment_method: 'card'
})| Property | Type | Required | Description |
|---|---|---|---|
plan_id | string | Optional | Plan converted onto |
plan_name | string | Optional | Human-readable plan name |
revenue | number | Optional | First payment amount |
currency | string | Optional | ISO 4217 currency code (e.g. GBP, USD) |
payment_method | string | Optional | Payment method used (e.g. card, invoice) |
trial_expired
A trial period ended without conversion.
datafly.track('trial_expired', {
plan_id: 'plan_pro',
days_used: 9,
features_used: ['reporting', 'integrations']
})| Property | Type | Required | Description |
|---|---|---|---|
plan_id | string | Optional | Plan that expired |
days_used | number | Optional | Number of trial days the user was active |
features_used | string[] | Optional | List of feature IDs used during the trial |
subscription_started
A new paid subscription has been activated. This is the primary revenue conversion event for SaaS.
datafly.track('subscription_started', {
plan_id: 'plan_pro_annual',
plan_name: 'Pro Annual',
revenue: 468.00,
currency: 'GBP',
billing_cycle: 'annual',
coupon: 'LAUNCH20'
})| Property | Type | Required | Description |
|---|---|---|---|
plan_id | string | Yes | Plan identifier |
plan_name | string | Optional | Human-readable plan name |
revenue | number | Optional | Amount charged |
currency | string | Optional | ISO 4217 currency code |
billing_cycle | string | Optional | monthly or annual |
coupon | string | Optional | Coupon or promo code applied |
Send a stable subscription or transaction identifier where possible so Meta CAPI and GA4 can deduplicate against browser-side conversions.
subscription_upgraded
A subscriber has moved to a higher-tier plan.
datafly.track('subscription_upgraded', {
previous_plan_id: 'plan_starter_monthly',
new_plan_id: 'plan_pro_monthly',
revenue_delta: 30.00,
currency: 'GBP'
})| Property | Type | Required | Description |
|---|---|---|---|
previous_plan_id | string | Optional | Plan being upgraded from |
new_plan_id | string | Yes | Plan being upgraded to |
revenue_delta | number | Optional | Additional revenue from the upgrade (prorated or full cycle) |
currency | string | Optional | ISO 4217 currency code |
subscription_downgraded
A subscriber has moved to a lower-tier plan.
datafly.track('subscription_downgraded', {
previous_plan_id: 'plan_pro_monthly',
new_plan_id: 'plan_starter_monthly',
reason: 'too_expensive'
})| Property | Type | Required | Description |
|---|---|---|---|
previous_plan_id | string | Optional | Plan being downgraded from |
new_plan_id | string | Yes | Plan being downgraded to |
reason | string | Optional | Reason code or label provided by the user |
subscription_cancelled
A subscription has been cancelled.
datafly.track('subscription_cancelled', {
plan_id: 'plan_pro_monthly',
reason: 'switching_product',
feedback: 'Moving to a tool with better API support'
})| Property | Type | Required | Description |
|---|---|---|---|
plan_id | string | Optional | Plan being cancelled |
reason | string | Optional | Cancellation reason code (e.g. missing_feature, switching_product) |
feedback | string | Optional | Free-text feedback from the user |
Do not send raw email addresses in feedback. If your cancellation flow captures open text, sanitise or omit it before sending.
subscription_renewed
A subscription billing cycle has renewed successfully.
datafly.track('subscription_renewed', {
plan_id: 'plan_pro_annual',
revenue: 468.00,
currency: 'GBP'
})| Property | Type | Required | Description |
|---|---|---|---|
plan_id | string | Optional | Plan that renewed |
revenue | number | Optional | Amount charged on renewal |
currency | string | Optional | ISO 4217 currency code |
Product Usage
Events tracking in-product engagement and feature adoption.
feature_used
A user has interacted with a specific product feature.
datafly.track('feature_used', {
feature_id: 'bulk_export',
feature_name: 'Bulk Export',
context: 'reports'
})| Property | Type | Required | Description |
|---|---|---|---|
feature_id | string | Yes | Stable identifier for the feature (e.g. bulk_export) |
feature_name | string | Optional | Human-readable feature name |
context | string | Optional | Where in the UI the feature was used (e.g. dashboard, settings) |
report_viewed
A user has opened or refreshed a report.
datafly.track('report_viewed', {
report_id: 'rpt_monthly_revenue',
report_name: 'Monthly Revenue',
filters: { date_range: 'last_30_days', segment: 'pro_users' }
})| Property | Type | Required | Description |
|---|---|---|---|
report_id | string | Optional | Stable identifier for the report |
report_name | string | Optional | Display name (e.g. Monthly Revenue) |
filters | object | Optional | Active filter state at time of view |
export_completed
A data export has finished successfully.
datafly.track('export_completed', {
export_type: 'csv',
record_count: 1450
})| Property | Type | Required | Description |
|---|---|---|---|
export_type | string | Optional | Format of the export: csv, pdf, xlsx |
record_count | number | Optional | Number of records included in the export |
integration_connected
A user has successfully connected a third-party integration.
datafly.track('integration_connected', {
integration_id: 'int_salesforce',
integration_name: 'Salesforce',
provider: 'salesforce'
})| Property | Type | Required | Description |
|---|---|---|---|
integration_id | string | Yes | Stable identifier for the integration |
integration_name | string | Optional | Display name (e.g. Salesforce) |
provider | string | Optional | Provider/vendor name |
api_key_created
A user has generated a new API key.
datafly.track('api_key_created', {
scope: 'read_only'
})| Property | Type | Required | Description |
|---|---|---|---|
scope | string | Optional | Permission scope of the key (e.g. read_only, full_access) |
Retention & Support
Events relating to ongoing engagement, support interactions, and account health.
support_ticket_created
A user has opened a support ticket or contacted support.
datafly.track('support_ticket_created', {
ticket_id: 'tkt_84021',
category: 'billing',
priority: 'high',
channel: 'in_app'
})| Property | Type | Required | Description |
|---|---|---|---|
ticket_id | string | Optional | Identifier of the created ticket |
category | string | Optional | Ticket category (e.g. billing, technical, account) |
priority | string | Optional | Priority level (e.g. low, normal, high) |
channel | string | Optional | Channel the ticket came in on (e.g. in_app, email, chat) |
feedback_submitted
A user has submitted in-product feedback, an NPS score, or a survey response.
datafly.track('feedback_submitted', {
survey_id: 'nps_q3',
feedback_type: 'nps',
score: 9
})| Property | Type | Required | Description |
|---|---|---|---|
survey_id | string | Optional | Identifier of the survey or prompt |
feedback_type | string | Optional | Type of feedback (e.g. nps, csat, feature_request) |
score | number | Optional | Numeric score where applicable (e.g. NPS 0–10) |
Do not include free-text feedback containing personal data in event properties. Send only the score and survey identifiers.
referral_sent
An existing user has referred another person to the product.
datafly.track('referral_sent', {
referral_code: 'FRIEND2026',
channel: 'email',
reward_type: 'account_credit'
})| Property | Type | Required | Description |
|---|---|---|---|
referral_code | string | Optional | Referral code shared by the user |
channel | string | Optional | Channel used to share the referral (e.g. email, link, social) |
reward_type | string | Optional | Reward associated with the referral (e.g. account_credit, discount) |
Collaboration & Team
Events relating to team invitations, seat management, and multi-user activity.
invitation_sent
A team member has been invited to the workspace.
datafly.track('invitation_sent', {
invitee_email_hash: 'b3a8e0e1...', // SHA-256 of invitee email
role: 'member',
seats_total: 8
})| Property | Type | Required | Description |
|---|---|---|---|
invitee_email_hash | string | Optional | SHA-256 hash of the invitee’s email address |
role | string | Optional | Role assigned to the invitee (e.g. admin, member, viewer) |
seats_total | number | Optional | Total seat count on the account after sending the invitation |
Never send raw email addresses as event properties. Hash using SHA-256 (trim + lowercase first) before tracking.
invitation_accepted
An invited user has accepted their invitation and joined the workspace.
datafly.track('invitation_accepted', {
inviter_id: 'user_123',
role: 'member'
})| Property | Type | Required | Description |
|---|---|---|---|
inviter_id | string | Optional | User ID of the person who sent the invitation |
role | string | Optional | Role the new user was assigned |
seat_added
A new seat has been added to the account.
datafly.track('seat_added', {
new_seat_count: 10
})| Property | Type | Required | Description |
|---|---|---|---|
new_seat_count | number | Optional | Total seat count after the addition |
seat_removed
A seat has been removed from the account.
datafly.track('seat_removed', {
new_seat_count: 9,
reason: 'user_offboarded'
})| Property | Type | Required | Description |
|---|---|---|---|
new_seat_count | number | Optional | Total seat count after the removal |
reason | string | Optional | Reason for removal (e.g. user_offboarded, downgrade) |
Vendor Mapping
The table below shows how Datafly Signal maps SaaS events to the native event names expected by each vendor’s server-side API. Signal uses the GA4-style event names as its canonical format, and blueprints handle the transformation to vendor-specific payloads. *(custom)* indicates the vendor has no native equivalent, so Signal delivers the event under its own name as a custom event.
| Signal Event | GA4 | Meta CAPI | TikTok Events API |
|---|---|---|---|
view_item | view_item | ViewContent | ViewContent |
plan_viewed | view_item | ViewContent | ViewContent |
search | search | Search | Search |
view_promotion | view_promotion | (custom) | (custom) |
generate_lead | generate_lead | Lead | SubmitForm |
demo_requested | generate_lead | Lead | SubmitForm |
content_downloaded | generate_lead | Lead | Download |
sign_up | sign_up | CompleteRegistration | CompleteRegistration |
login | login | (custom) | (custom) |
logout | (custom) | (custom) | (custom) |
password_reset_requested | (custom) | (custom) | (custom) |
onboarding_started | tutorial_begin | (custom) | (custom) |
onboarding_step_completed | (custom) | (custom) | (custom) |
onboarding_completed | tutorial_complete | CompleteRegistration | CompleteRegistration |
trial_started | begin_checkout | StartTrial | StartTrial |
trial_converted | purchase | Subscribe | Subscribe |
trial_expired | (custom) | (custom) | (custom) |
subscription_started | purchase | Subscribe | Subscribe |
subscription_upgraded | purchase | Subscribe | Subscribe |
subscription_downgraded | (custom) | (custom) | (custom) |
subscription_cancelled | (custom) | (custom) | (custom) |
subscription_renewed | purchase | Subscribe | Subscribe |
feature_used | feature_used | (custom) | (custom) |
report_viewed | view_item | ViewContent | ViewContent |
export_completed | (custom) | (custom) | (custom) |
integration_connected | (custom) | (custom) | (custom) |
api_key_created | (custom) | (custom) | (custom) |
support_ticket_created | contact | Contact | Contact |
feedback_submitted | (custom) | (custom) | (custom) |
referral_sent | share | (custom) | (custom) |
invitation_sent | share | (custom) | (custom) |
invitation_accepted | join_group | CompleteRegistration | CompleteRegistration |
seat_added | (custom) | (custom) | (custom) |
seat_removed | (custom) | (custom) | (custom) |