Identity Events Test Suite

Test all sources of Possible Person (pp) and Identify (id) events in the ListenLayer SDK

How Identity Detection Works

ListenLayer automatically detects potential person identifiers (emails, names, phones) from various sources:

To debug: Run _ll.debug(true) in console, or use Preview Mode

Note: Test emails use @acmecorp.io domain. The SDK filters out @example.com, @test.com, and similar fake email patterns to avoid capturing test data in production.

New: Comprehensive Form Tests (45+ scenarios) - Test redirects, validation, iframes, Marketo-style injection, platform mocks, and edge cases.

New: Company Reveal Tests (7 scenarios) - Test company detection from identity events with real company domains (KickFire, Microsoft, Google, Salesforce). Includes explicit identifyPerson(), unknown domains, personal emails, and multi-company forms.

pp - Possible Person

Auto-detected from 9 sources

Inferred Identity

id - Identify

Explicit API call

Confirmed Identity

Sources (10 Total) + Company Tests + Blocking Tests

๐Ÿงช Comprehensive Field Tests 1. Form Submit 2. Network Interception 3. dataLayer Push 4. URL Parameters 5. Chat Platforms 6. Scheduling Platforms 7. eCommerce Events (pp + id) 8. Video Platforms 9. Custom Events 10. Explicit Identify (id) 11. Company/Org Data Blocking Tests

๐Ÿงช Comprehensive Field Tests

Purpose: Test ALL supported identity fields in a single event to verify the full extraction and LLM pipeline.

Fields tested: email, first_name, last_name, full_name, phone, company, company_domain, zip_code

URL Parameters (pp event)

Click to load page with all fields as URL parameters. Check console and network for pp event.

Load with All Fields
email, first_name, last_name, phone, company, company_domain, zip_code
Load with camelCase
email, firstName, lastName, phoneNumber, companyName, companyDomain, zipCode
Load with Full Name
email, name (full_name), company, phone

dataLayer Push - pp Event (Flexible Detection)

Push dataLayer events with all fields. These use flexible detection (not person/user_data objects).

dataLayer.push({ event: 'lead_captured', email: 'comprehensive-dl@acmecorp.io', first_name: 'DataLayer', last_name: 'Test', phone: '+1-555-333-4444', company: 'DataLayer Corp', company_domain: 'datalayercorp.io', zip_code: '94102' })
dataLayer.push({ event: 'user_action', user: { email: 'nested-user@acmecorp.io', firstName: 'Nested', lastName: 'User', phone: '+1-555-444-5555', company: 'Nested Corp' } })
dataLayer.push({ event: 'contact_form', contact: { email: 'contact-obj@acmecorp.io', first_name: 'Contact', last_name: 'Person', phone: '+1-555-666-7777', organization: 'Contact Org', postal_code: '60601' } })

dataLayer Push - id Event (Explicit person/user_data)

Push dataLayer events with explicit person or user_data objects. These emit id events (confirmed identity).

dataLayer.push({ event: 'user_login', person: { email: 'person-full@acmecorp.io', first_name: 'Person', last_name: 'Full', phone: '+1-555-888-9999', company: 'Person Corp', company_domain: 'personcorp.io', zip_code: '33101' } })
dataLayer.push({ event: 'profile_complete', user_data: { email: 'userdata-full@acmecorp.io', first_name: 'UserData', last_name: 'Complete', phone_number: '+1-555-111-0000', company: 'UserData Corp', address: { city: 'San Francisco', region: 'CA', postal_code: '94102' } } })
dataLayer.push({ event: 'purchase', user_data: { email_address: 'ga4-style@acmecorp.io', phone_number: '+1-555-222-3333', address: { first_name: 'GA4', last_name: 'Style', city: 'Austin', region: 'TX', postal_code: '78701', country: 'US' } } })

_ll.identifyPerson (id Event)

Explicit API call with all supported fields.

_ll.identifyPerson({ email: 'identify-full@acmecorp.io', firstName: 'Identify', lastName: 'Person', phone: '+1-555-999-0000', company: { name: 'Identify Corp', domain: 'identifycorp.io' }, zipCode: '02101' })
_ll.identifyPerson({ email: 'external-id@acmecorp.io', firstName: 'External', lastName: 'IdUser', externalId: 'CRM-12345', company: { name: 'External Corp' } })

1. Form Submit

pp

SDK monitors form submissions and extracts email/name/phone from form fields.

Trigger: Form submit event with identifiable fields

Source Code: person-registry.ts:registerFromFormData()

Test Form - Submit to trigger pp event

1.1 Comprehensive Semantic Field Forms

These forms test all semantic field types that appear in the fields array of pp events:

first_name, last_name, full_name, phone, company, job_title, address, city, state, zip, country, unknown

Complete Contact Form - All Semantic Types

Tests: first_name, last_name, phone, company, job_title, city, state, zip, country

Expected pp.fields: [{semantic: "first_name"}, {semantic: "last_name"}, {semantic: "phone"}, {semantic: "company"}, {semantic: "job_title"}, {semantic: "address"}, {semantic: "city"}, {semantic: "state"}, {semantic: "zip"}, {semantic: "country"}]

Multi-Person Form - Two People (LLM Entity Organization Test)

Tests: LLM should organize into 2 persons (primary + secondary) linked to 2 companies

Person 1 - Primary (Form Submitter)
Person 2 - Secondary (Referral/Colleague)
Expected LLM output: persons[{role:"primary", email:"sarah...", company_id:"c1"}, {role:"secondary", email:"michael...", company_id:"c2"}], companies[{id:"c1", name:"Acme..."}, {id:"c2", name:"Tech..."}]
fields: [{semantic: "full_name"}]
fields: [{semantic: "first_name"}, {semantic: "last_name"}]
fields: [{semantic: "first_name"}, {semantic: "last_name"}]
fields: [{semantic: "full_name"}]

1.2 Business & Organization Fields

fields: [{semantic: "company"}]
fields: [{semantic: "company"}]
fields: [{semantic: "job_title"}]
fields: [{semantic: "job_title"}]

1.3 Phone Number Variations

fields: [{semantic: "phone"}]
fields: [{semantic: "phone"}]
fields: [{semantic: "phone"}]
fields: [{semantic: "phone"}]

1.4 Location & Address Fields

fields: [{semantic: "city"}]
fields: [{semantic: "state"}]
fields: [{semantic: "zip"}]
fields: [{semantic: "zip"}]

Complete Mailing Address Form

Tests all location semantic types together

Expected: first_name, last_name, address, city, state, zip, country

1.5 Autocomplete Attribute Testing

HTML5 autocomplete attributes also trigger semantic type detection. These forms use autocomplete attributes instead of specific field names.

fields: [{semantic: "first_name"}, {semantic: "last_name"}]
fields: [{semantic: "full_name"}, {semantic: "company"}]
fields: [{semantic: "full_name"}, {semantic: "phone"}]
fields: [{semantic: "full_name"}, {semantic: "zip"}]

1.6 Label-Based Detection

Fields with generic names but semantic labels/placeholders should still be detected correctly.

fields: [{semantic: "full_name"}]
fields: [{semantic: "full_name"}, {semantic: "company"}]
fields: [{semantic: "full_name"}, {semantic: "job_title"}]
fields: [{semantic: "full_name"}, {semantic: "phone"}]

2. Network Interception

pp

SDK intercepts XHR and fetch POST requests, scanning request bodies for email patterns. It checks known email fields AND recursively scans all values for email patterns.

Trigger: XHR/fetch POST with email in request body (JSON, FormData, or URLSearchParams)

Source Code: network/extract.ts:extractIdentity() + findEmailInObject()

2.1 Basic Request Types

fetch('/api/test', { method: 'POST', body: JSON.stringify({ email: 'fetch@acmecorp.io', name: 'Fetch User' }) })
xhr.send(JSON.stringify({ email: 'xhr@acmecorp.io', user_name: 'XHR User' }))
const formData = new FormData(); formData.append('email', 'formdata@acmecorp.io'); fetch('/api/test', { body: formData })
const params = new URLSearchParams(); params.append('email', 'urlsearch@acmecorp.io'); fetch('/api/test', { body: params })
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); xhr.send('email=urlencoded@acmecorp.io&name=Test')
fetch('/api/user/123', { method: 'PUT', body: JSON.stringify({ email: 'put-update@acmecorp.io' }) })

2.2 Known Email Fields

Tests email detection via known field names (email_address, user_email, etc.).

{ email_address: 'emailaddress@acmecorp.io' }
{ user_email: 'useremail@acmecorp.io' }
{ contact_email: 'contactemail@acmecorp.io' }
{ work_email: 'workemail@acmecorp.io' }
{ $email: 'dollaremail@acmecorp.io' }
{ mail: 'mailfield@acmecorp.io' }

2.3 Nested Objects

Tests recursive email scanning in nested object structures.

{ user: { email: 'nested1@acmecorp.io' } }
{ properties: { email: 'props@acmecorp.io' } }
{ traits: { email: 'traits@acmecorp.io' } }
{ profile: { contact: { email: 'nested2@acmecorp.io' } } }
{ data: { form: { fields: { email: 'nested3@acmecorp.io' } } } }
{ checkout: { billing: { contact: { email: 'nested4@acmecorp.io' } } } }

2.4 Arbitrary Field Names (Value Pattern Match)

Tests email detection based on VALUE pattern matching, regardless of field name. SDK scans all values for email patterns.

{ foo: 'arbitrary-foo@acmecorp.io', bar: 123 }
{ data: 'arbitrary-data@acmecorp.io' }
{ x: 'arbitrary-x@acmecorp.io' }
{ value: 'arbitrary-value@acmecorp.io' }
{ identifier: 'arbitrary-id@acmecorp.io' }
{ primary: 'arbitrary-primary@acmecorp.io' }

2.5 Nested Arbitrary Fields

Tests email detection in nested objects with non-standard field names.

{ payload: { val: 'nestarb1@acmecorp.io' } }
{ response: { result: { item: 'nestarb2@acmecorp.io' } } }
{ meta: { custom_xyz: 'nestarb3@acmecorp.io' } }
{ config: { settings: { abc: 'nestarb4@acmecorp.io' } } }

2.6 With Additional PII (Name, Phone, Company)

Tests extraction of additional identity fields (first_name, last_name, phone, company).

{ email: 'names@acmecorp.io', first_name: 'John', last_name: 'Smith' }
{ email: 'fullname@acmecorp.io', name: 'Jane Doe' }
{ email: 'phone@acmecorp.io', phone: '+1-555-123-4567' }
{ email: 'company@acmecorp.io', company: 'Acme Corp' }
{ email, first_name, last_name, phone, company }
{ userEmail, firstName, lastName, phoneNumber, companyName }

2.7 Edge Cases

Tests edge cases and special scenarios.

{ a: { b: { c: { d: { e: { email: 'maxdepth@acmecorp.io' } } } } } }
6 levels deep - SDK scans to depth 5 Should NOT detect this email
{ email: 'first@acmecorp.io', secondary_email: 'second@acmecorp.io' }
{ contacts: ['array@acmecorp.io'] } Arrays not recursed - should NOT detect
{ email: '' } Empty - should NOT detect
{ email: 'not-an-email' } Invalid format - should NOT detect
Note: Network interception scans request bodies using two strategies: 1) Check known email field names (email, user_email, etc.), 2) Recursively scan all object values for email patterns up to 5 levels deep. Arrays are not recursed.

3. dataLayer Push

id pp

SDK monitors dataLayer.push() calls with two-tier detection:

Source Code: datalayer/listener.ts:processEvent()

3.1 Explicit Person Fields (id events)

When dataLayer events include explicit person fields (person object or user_data), the SDK emits an id event with high confidence.

dataLayer.push({ event: 'login', person: { email: 'dl-person@acmecorp.io', first_name: 'John', last_name: 'Doe', phone: '+1-555-123-4567' } }) // Emits: id (explicit person)
dataLayer.push({ event: 'user_signup', person: { email: 'dl-person-email@acmecorp.io' } }) // Emits: id (person.email)
dataLayer.push({ event: 'user_identified', user_data: { email: 'dl-userdata@acmecorp.io', phone_number: '+1-555-234-5678' } }) // Emits: id (GA4 user_data)
dataLayer.push({ event: 'profile_complete', user_data: { email: 'dl-userdata-addr@acmecorp.io', address: { first_name: 'Jane', last_name: 'Smith', city: 'Austin' } } }) // Emits: id (user_data + address)
dataLayer.push({ event: 'identify', person: { email: 'dl-person-name@acmecorp.io', name: 'Full Name Person' } }) // Emits: id (person + name)
dataLayer.push({ event: 'phone_verify', user_data: { phone_number: '+1-555-987-6543' } }) // Emits: id (phone as identifier)

3.2 Flexible Detection (pp events)

When dataLayer events do NOT have person/user_data, the SDK scans all values for email patterns and emits pp events.

dataLayer.push({ event: 'user_identified', email: 'datalayer@acmecorp.io', user_type: 'lead' }) // Emits: pp (flexible scan)
dataLayer.push({ event: 'login', user: { email: 'user.object@acmecorp.io', name: 'DataLayer User' } }) // Emits: pp (user != user_data)
dataLayer.push({ event: 'form_complete', form_data: { fields: { email_address: 'nested@acmecorp.io' } } }) // Emits: pp (nested scan)
dataLayer.push({ event: 'customer_update', customer: { email: 'dl-customer@acmecorp.io', tier: 'gold' } }) // Emits: pp (customer != person)
dataLayer.push({ event: 'contact_form', contact: { email: 'dl-contact@acmecorp.io', subject: 'Inquiry' } }) // Emits: pp (contact != person)
dataLayer.push({ event: 'visitor_identified', visitor: { email: 'dl-visitor@acmecorp.io' } }) // Emits: pp (visitor != person)

3.3 Arbitrary Field Names (Value Pattern Match)

Tests email detection based on VALUE pattern, regardless of field name.

dataLayer.push({ event: 'test', foo: 'dl-arbitrary-foo@acmecorp.io' })
dataLayer.push({ event: 'track', data: 'dl-arbitrary-data@acmecorp.io' })
dataLayer.push({ event: 'custom', x: 'dl-arbitrary-x@acmecorp.io' })
dataLayer.push({ event: 'data_push', payload: { val: 'dl-arb-nested@acmecorp.io' } })

3.4 Deep Nesting

Tests recursive scanning through deeply nested objects.

dataLayer.push({ event: 'deep3', a: { b: { c: { email: 'dl-deep3@acmecorp.io' } } } })
dataLayer.push({ event: 'deep4', a: { b: { c: { d: { email: 'dl-deep4@acmecorp.io' } } } } })
dataLayer.push({ event: 'segment_identify', properties: { traits: { email: 'dl-traits@acmecorp.io' } } })
dataLayer.push({ event: 'app_context', context: { user: { profile: { email: 'dl-context@acmecorp.io' } } } })

3.5 With Additional PII

Tests extraction of names, phone, and company along with email.

dataLayer.push({ event: 'signup', email: 'dl-names@acmecorp.io', first_name: 'Test', last_name: 'User' })
dataLayer.push({ event: 'verify', email: 'dl-phone@acmecorp.io', phone: '+1-555-999-8888' })
dataLayer.push({ event: 'b2b_lead', email: 'dl-company@acmecorp.io', company: 'DataLayer Corp' })
dataLayer.push({ event: 'complete_profile', email: 'dl-full@acmecorp.io', first_name: 'Complete', last_name: 'Profile', phone: '+1-555-777-6666', company: 'Full Corp' })

3.6 Edge Cases

dataLayer.push({ 'gtm.start': Date.now(), event: 'gtm.js' }) // Should be IGNORED (GTM system event)
dataLayer.push({ event: 'test', person: {} }) // No email - should NOT emit id
dataLayer.push({ event: 'multi', email: 'dl-first@acmecorp.io', secondary_email: 'dl-second@acmecorp.io' })
dataLayer.push({ event: 'dual', person: { email: 'dl-person-wins@acmecorp.io' }, email: 'dl-toplevel@acmecorp.io' }) // person takes priority โ†’ id event
Two-Tier Priority: Explicit person/user_data are checked first โ†’ id event. If not found, flexible scanning โ†’ pp event. Objects like user, customer, visitor, contact are NOT considered explicit and go through flexible detection.

4. URL Parameters

pp

SDK scans URL query parameters for email addresses on page load. It checks both explicit email parameter names AND scans all parameter values for email patterns.

Trigger: Page load with email in any query parameter

Source Code: person-registry.ts:registerFromUrlParams()

Standard Email Parameters

Load with ?email=...
?email=urlparam@acmecorp.io
Load with ?user_email=...
?user_email=user_email_param@acmecorp.io
Load with ?contact_email=...
?contact_email=contact@acmecorp.io
Load with ?subscriber_email=...
?subscriber_email=subscriber@acmecorp.io
Load with ?customer_email=...
?customer_email=customer@acmecorp.io
Load with ?billing_email=...
?billing_email=billing@acmecorp.io

Short/Ambiguous Parameters

These test email pattern detection in non-obvious parameter names.

Load with ?e=...
?e=short-e@acmecorp.io
Load with ?_e=...
?_e=underscore-e@acmecorp.io
Load with ?em=...
?em=short-em@acmecorp.io
Load with ?mail=...
?mail=just-mail@acmecorp.io
Load with ?addr=...
?addr=addr-param@acmecorp.io
Load with ?u=...
?u=user-abbrev@acmecorp.io

Marketing/Platform Parameters

Common email parameters from marketing platforms and CRMs.

Load with ?recipient=...
?recipient=recipient@acmecorp.io
Load with ?to=...
?to=to-param@acmecorp.io
Load with ?lead_email=...
?lead_email=lead@acmecorp.io
Load with ?Email=...
?Email=capitalized@acmecorp.io
Load with ?EMAIL=...
?EMAIL=uppercase@acmecorp.io
Load with ?email_address=...
?email_address=full-address@acmecorp.io

Arbitrary Parameters (Value Pattern Match)

These test detection based on email pattern in value, regardless of parameter name.

Load with ?foo=...
?foo=random-foo@acmecorp.io
Load with ?data=...
?data=generic-data@acmecorp.io
Load with ?id=...
?id=id-param@acmecorp.io
Load with ?ref=...
?ref=ref-param@acmecorp.io
Load with ?token=...
?token=token-param@acmecorp.io
Load with ?x=...
?x=minimal-x@acmecorp.io
Note: URL param detection happens on page load. Click a link to test - you'll navigate to this page with the email in the URL. The SDK detects emails both by parameter name (email, user_email, etc.) AND by scanning all parameter values for email patterns.

5. Chat Platforms

pp

SDK captures visitor email via _ll.chat.emailCaptured() method.

Trigger: Chat platform callback with visitor email

Source Code: chat/methods.ts:emailCaptured() โ†’ extractAndEmitFromPayload()

_ll.chat.emailCaptured({ platform: 'dft', email: 'drift@acmecorp.io', firstName: 'Drift', lastName: 'Visitor' })
_ll.chat.emailCaptured({ platform: 'ic', email: 'intercom@acmecorp.io', firstName: 'Intercom', lastName: 'User' })
_ll.chat.emailCaptured({ platform: 'lc', email: 'livechat@acmecorp.io' })

6. Scheduling Platforms

pp

SDK captures emails via _ll.scheduling.scheduled() method.

Trigger: Booking confirmation with invitee email

Source Code: scheduling/methods.ts:schedulingScheduled() โ†’ extractAndEmitFromPayload()

_ll.scheduling.scheduled({ platform: 'cal', email: 'calendly@acmecorp.io', firstName: 'Calendly', lastName: 'User', scheduledTime: '2024-01-15T10:00:00Z' })
_ll.scheduling.scheduled({ platform: 'hsm', email: 'hubspot-meeting@acmecorp.io', firstName: 'HubSpot', lastName: 'Lead', scheduledTime: '2024-01-15T10:00:00Z' })
_ll.scheduling.scheduled({ platform: 'cp', email: 'chilipiper@acmecorp.io', scheduledTime: '2024-01-15T10:00:00Z' })

7. eCommerce Events

id pp

SDK extracts identity from ecommerce events using two strategies:

High-Value Events: purchase, begin_checkout, add_payment_info, add_shipping_info

Source Code: ecommerce/listener.ts:processIdentity()

7.1 Explicit Person Fields (id events)

When ecommerce events include explicit person fields (person object, user_data, or direct email/phone), the SDK emits an id event with high confidence.

dataLayer.push({ event: 'purchase', person: { email: 'buyer@acmecorp.io', first_name: 'John', last_name: 'Buyer', phone: '+1-555-123-4567' }, ecommerce: { ... } }) // Emits: id (confirmed identity)
dataLayer.push({ event: 'purchase', user_data: { email: 'ga4user@acmecorp.io', phone_number: '+1-555-234-5678', company: 'Acme Corp', company_domain: 'acmecorp.io', address: { first_name: 'Jane' } }, ecommerce: { ... } }) // Emits: id (GA4 + company)
dataLayer.push({ event: 'begin_checkout', person: { email: 'checkout@acmecorp.io', name: 'Sam Checkout' }, ecommerce: { ... } }) // Emits: id (explicit person)
dataLayer.push({ event: 'add_shipping_info', person: { email: 'shipping@acmecorp.io', first_name: 'Alex', address: { city: 'Austin' } }, ecommerce: { ... } }) // Emits: id (with address)
dataLayer.push({ event: 'begin_checkout', user_data: { email: 'checkout-ga4@acmecorp.io', phone_number: '+1-555-444-5555' }, ecommerce: { ... } }) // Emits: id (GA4 user_data)
dataLayer.push({ event: 'add_shipping_info', user_data: { email: 'shipping-ga4@acmecorp.io', address: { first_name: 'Ship', last_name: 'Recipient' } }, ecommerce: { ... } }) // Emits: id (user_data + address)
dataLayer.push({ event: 'add_payment_info', user_data: { email: 'payment-ga4@acmecorp.io', phone_number: '+1-555-666-7777' }, ecommerce: { ... } }) // Emits: id (GA4 standard)
_ll.ecommerce.purchase({ person: { email: 'sdk-buyer@acmecorp.io', first_name: 'SDK', last_name: 'Customer' }, transaction_id: 'SDK-TXN-001', value: 399.99 }) // Emits: id (SDK method)

7.2 Flexible Detection (pp events)

When ecommerce events contain person data in any location (SDK methods, nested objects, top-level fields not in person/user_data), the SDK scans for email patterns and emits a pp event.

_ll.ecommerce.purchase({ order_id: 'ORD-12345', customer_email: 'buyer@acmecorp.io', total: 99.99, items: [...] }) // Emits: pp (SDK method)
_ll.ecommerce.beginCheckout({ email: 'checkout@acmecorp.io', value: 149.99, items: [...] }) // Emits: pp (SDK method)
_ll.ecommerce.addPaymentInfo({ billing_email: 'billing@acmecorp.io', payment_type: 'credit_card' }) // Emits: pp (SDK method)
_ll.ecommerce.purchase({ order_id: 'ORD-67890', total: 249.99, email: 'sarah.chen@acmecorp.io', first_name: 'Sarah', last_name: 'Chen', phone: '+1-555-867-5309', company: 'Acme Corp', items: [...] }) // Emits: pp (NOT id โ€” no person/user_data wrapper)
dataLayer.push({ event: 'purchase', ecommerce: { transaction_id: 'TXN-002', billing: { email: 'billing-nested@acmecorp.io' } } }) // Emits: pp (nested object)
dataLayer.push({ event: 'begin_checkout', ecommerce: { shipping: { email_address: 'shipping-nested@acmecorp.io' } } }) // Emits: pp (nested scan)
dataLayer.push({ event: 'purchase', customer: { email: 'customer-obj@acmecorp.io', first_name: 'Customer', company: 'Customer Corp', company_domain: 'customercorp.io' }, ecommerce: { ... } }) // Emits: pp (with company)
dataLayer.push({ event: 'add_payment_info', ecommerce: { checkout: { billing_details: { contact_info: { email: 'deep-nested@acmecorp.io' } } } } }) // Emits: pp (deep scan)
dataLayer.push({ event: 'add_payment_info', email: 'payment-toplevel@acmecorp.io', phone: '+1-555-345-6789', ecommerce: { ... } }) // Emits: pp (not in person/user_data)
Priority: Explicit person fields are checked first. If found, an id event is emitted and generic detection is skipped. This ensures high-confidence data from documented fields takes precedence.

8. Video Platforms

pp

SDK captures lead information from video platform features like Wistia Turnstile forms, Vidyard viewer gates, or any gated video content. Emits a vle (video_lead) event AND a pp event when email is present.

Trigger: Video platform email gate, form submission, or manual _ll.video.lead() call

Source Code: video/methods.ts:videoLead() โ†’ extractAndEmitFromPayload()

_ll.video.lead({ platform: 'wis', email: 'wistia@acmecorp.io', firstName: 'Wistia', lastName: 'Viewer', video_id: 'abc123' }) // Emits: vle + pp (video lead)
_ll.video.lead({ platform: 'vy', email: 'vidyard@acmecorp.io', firstName: 'Vidyard', lastName: 'Lead', title: 'Product Demo' }) // Emits: vle + pp (video lead)
_ll.video.lead({ email: 'videogated@acmecorp.io', firstName: 'Video', lastName: 'Lead', video_id: 'webinar-2024' }) // Emits: vle + pp (video lead)

9. Custom Events

pp

When _ll.track() is called, the SDK emits a cus (custom) event AND automatically scans the params for email fields. If an email is found, a pp event is also emitted with src: 'custom'.

Trigger: _ll.track('event', { email: '...' })

Events Emitted: cus (always) + pp (if email found)

Source Code: api/track.ts:track() โ†’ extractAndEmitFromPayload()

_ll.track('lead_captured', { email: 'custom@acmecorp.io', source: 'landing_page', campaign: 'spring_2024' }) // Emits: cus + pp (src: 'custom')
_ll.track('signup_complete', { user_email: 'signup@acmecorp.io', plan: 'pro' }) // Emits: cus + pp (src: 'custom')
_ll.track('form_submitted', { form_id: 'contact-123', data: { email_address: 'nested-custom@acmecorp.io' } }) // Emits: cus + pp (src: 'custom')
_ll.track('button_clicked', { button_id: 'cta-signup', page: 'pricing' }) // Emits: cus ONLY (no pp - no email)
Dual Event Emission: Each _ll.track() call now emits a cus event (always) AND a pp event (when email is detected in params). The pp event has src: 'custom' to indicate it came from a custom event.

10. Explicit Identify (id event)

id

Explicit API call to identify a known person. Emits an id event (not pp).

Trigger: _ll.identifyPerson({ email: '...' })

Event Type: id (identity confirmed)

_ll.identifyPerson({ email: 'identified@acmecorp.io' })
_ll.identifyPerson({ email: 'full-profile@acmecorp.io', first_name: 'Jane', last_name: 'Smith', phone: '+1-555-987-6543', company: 'Acme Corp', title: 'VP Marketing' })
_ll.identifyPerson({ email: 'external-id@acmecorp.io', external_id: 'CRM-USER-12345', source: 'salesforce' })
_ll.identifyPerson({ email: 'custom-props@acmecorp.io', properties: { plan_type: 'enterprise', signup_date: '2024-01-15', lifetime_value: 5000 } })
_ll.identifyPerson({ email: 'address@acmecorp.io', first_name: 'Sarah', last_name: 'Location', address: '123 Main Street', address2: 'Suite 400', city: 'San Francisco', state: 'CA', country: 'USA', postal_code: '94102' })
_ll.identifyPerson({ email: 'fullname@acmecorp.io', full_name: 'Dr. Michael Richardson III', title: 'Chief Technology Officer' })
_ll.identifyPerson({ email: 'custom@acmecorp.io', first_name: 'Custom', last_name: 'User', customFields: { plan_tier: 'enterprise', account_manager: 'John Smith', renewal_date: '2025-01-15', seats: 50, is_beta_user: true } })
_ll.identifyPerson({ email: 'extphone@acmecorp.io', phone: '+1-800-555-1234', external_id: 'HUB-CONTACT-98765', first_name: 'Hub', last_name: 'Contact' })
_ll.identifyPerson({ email: 'complete@acmecorp.io', first_name: 'Complete', last_name: 'Profile', full_name: 'Complete Profile Jr.', phone: '+1-555-COMPLETE', title: 'Director of Everything', address: '456 Enterprise Blvd', address2: 'Floor 42, Office A', city: 'New York', state: 'NY', country: 'United States', postal_code: '10001', external_id: 'CRM-COMPLETE-12345', company: { domain: 'acmecorp.io', name: 'Acme Corporation' }, customFields: { lead_source: 'Trade Show', lead_score: 95, industry: 'Technology', employee_count: '500-1000', is_qualified: true, assigned_rep: 'sales@acmecorp.io', notes: 'VIP customer, handle with care' } })

11. Company/Organization Data

company

Tests company/organization data detection across all identity sources.

Supported Field Patterns: company, company_name, organization, organisation, org, business, business_name

Events: pp (auto-detected with company field) or id (explicit with structured company)

11.1 Explicit identifyPerson with Company (id event)

Tests the structured company format: { domain: string, name?: string }

_ll.identifyPerson({ email: 'ceo@acmecorp.io', company: { domain: 'acmecorp.io', name: 'Acme Corporation' } })
_ll.identifyPerson({ email: 'user@bigtech.com', company: { domain: 'bigtech.com' } })
_ll.identifyPerson({ email: 'vp@startup.io', firstName: 'Sarah', lastName: 'Johnson', phone: '+1-555-111-2222', company: { domain: 'startup.io', name: 'Startup Inc' } })

11.2 Network Requests with Company (pp event)

Tests company field detection in XHR/fetch request bodies using various field patterns.

{ email: 'emp@acmecorp.io', company: 'Acme Corporation' }
{ email: 'sales@bizco.io', company_name: 'BizCo Inc' }
{ email: 'dev@nonprof.org', organization: 'Tech Nonprofit' }
{ email: 'london@ukbiz.co.uk', organisation: 'UK Business Ltd' }
{ email: 'quick@short.io', org: 'ShortOrg' }
{ email: 'owner@shop.io', business: 'Local Shop LLC' }
{ email: 'mgr@store.io', business_name: 'Main Street Store' }
{ email: 'user@techcorp.io', company_domain: 'techcorp.io' }
{ email: 'ceo@megacorp.com', company: 'MegaCorp Inc', company_domain: 'megacorp.com' }

11.3 DataLayer with Company (pp event)

Tests company detection in dataLayer push events.

dataLayer.push({ event: 'lead', email: 'dl-lead@acmecorp.io', company: 'DataLayer Corp' })
dataLayer.push({ event: 'signup', email: 'dl-org@nonprof.org', organization: 'Nonprofit Org' })
dataLayer.push({ event: 'identify', user: { email: 'nested@biz.io', company: 'Nested Corp' } })
dataLayer.push({ event: 'b2b_signup', email: 'sales@enterprise.io', company_domain: 'enterprise.io' })
dataLayer.push({ event: 'enterprise_lead', email: 'vp@globaltech.com', company: 'GlobalTech', company_domain: 'globaltech.com' })

11.4 Custom Events with Company (pp event)

Tests company detection in _ll.track() custom events.

_ll.track('lead_captured', { email: 'lead@enterprise.io', company: 'Enterprise Solutions', lead_score: 85 })
_ll.track('demo_requested', { email: 'demo@gov.org', organization: 'Government Agency', demo_type: 'enterprise' })
_ll.track('saas_signup', { email: 'user@cloudapp.io', company_domain: 'cloudapp.io', plan: 'enterprise' })
_ll.track('enterprise_inquiry', { email: 'cto@fintech.co', company: 'FinTech Solutions', company_domain: 'fintech.co' })

11.5 eCommerce with Company (id/pp events)

Tests company detection in eCommerce person/user_data objects.

dataLayer.push({ event: 'purchase', person: { email: 'buyer@biz.io', company: 'Business Buyer LLC' }, ecommerce: { ... } })
dataLayer.push({ event: 'begin_checkout', user_data: { email: 'checkout@corp.io', company: 'Corp Inc' }, ecommerce: { ... } })
dataLayer.push({ event: 'begin_checkout', user_data: { email: 'edu@university.edu', organization: 'State University' }, ecommerce: { ... } })
dataLayer.push({ event: 'purchase', user_data: { email: 'buyer@saasco.io', company_domain: 'saasco.io' }, ecommerce: { ... } })
dataLayer.push({ event: 'purchase', user_data: { email: 'exec@bigcorp.com', company: 'BigCorp Industries', company_domain: 'bigcorp.com' }, ecommerce: { ... } })

11.6 Form Submission with Company (pp event)

Tests company field detection in form submissions.

B2B Lead Form with Company Field

Expected: pp event with company: 'Enterprise Solutions Inc'

Nonprofit Form with Organization Field

Expected: pp event with company: 'Community Nonprofit' (via organization field)
Note: Company data appears in the company field of pp events regardless of which pattern was used to detect it (company, organization, org, business, etc.). For id events via identifyPerson(), use the structured format: { domain: 'acme.io', name: 'Acme Corp' }.

Blocking Tests

blocked

Test that blocked events do NOT emit pp events. The SDK should skip identity detection for configured blocked events and skip keys.

See also: Form Blocking Tests for form-specific blocking (by ID, hash, or path).

Blocked dataLayer Events

These dataLayer event names are blocked from identity detection: internal_login, admin_action, debug_event, test_conversion

Expected: NO pp event (blocked)
Expected: NO pp event (blocked)
Expected: pp event SHOULD fire

Blocked Custom Events

These custom event names are blocked: test_event, debug_tracking, internal_action, staging_test

Expected: NO pp event (blocked)
Expected: NO pp event (blocked)
Expected: pp event SHOULD fire

Skip Keys (Field Name Blocking)

Fields containing these words are always ignored: internal_id, session_token, tracking_code (custom), plus built-in skip keys like password, token, secret, etc.

Expected: NO pp for internal_id field
Expected: NO pp for session_token field
Expected: NO pp for password field
Expected: pp event SHOULD fire

Event Log

Identity events fired on this page will appear below.

[--:--:--] Waiting for identity events...