Skip to main content
Use browser events for live elapsed-time and accepted-email signals. Use the existing session.completed webhook for final buying intent, including when analysis finishes after the visitor closes the page. Hobbes emits the signals; your GTM tags or webhook receiver submit conversions to your ad account.

Browser contract

The existing window.dataLayer.push and hobbes:event CustomEvent carry: All subsequent session events also include hobbes_email_domain_type and hobbes_session_recording_id when available. hobbes_session_id remains the voice call ID for compatibility. Join to webhook session.id using hobbes_session_recording_id, not the call ID. The organization, widget session, surface, client timestamp, and existing first-touch attribution fields remain. If a recording ID is absent, use a single delivery route for that conversion signal. Cross-route deduplication requires the recording ID. Categories are personal, corporate, disposable, and unknown. Corporate means a company provider/domain type, not a qualified buyer. Rejected emails do not emit a classification event. The current call-start policy rejects disposable email; consumers should nevertheless accept that category in the contract. Missing email, classification timeout, malformed/unknown result, and older sessions are unknown. There is no additional classifier request or lookup. The new browser fields contain no email address or email domain.
Duration begins with hobbes_session_started: the accepted demo is ready and its startup/research overlay has closed. Page load, launcher opening, lead submission, and company research do not run this clock. Listening, mute, quiet gaps, and time in a background tab count. A disconnected transport pauses emission; reconnection with the same call ID catches up crossed thresholds using the original clock. Explicit end, idle/system timeout, or terminal connection closure ends the clock. Timer throttling may delay a push; elapsed seconds can exceed its threshold. Re-renders and surface changes do not restart it. A new call starts a new set. Unmount/page closure ends browser delivery; no intent polling or return visit is required. The existing webhook session.duration_seconds remains recording wall time (end_time - start_time). It can include backend startup and earlier chat in a recording reused for a demo, so it is not numerically interchangeable with the ready-demo browser clock. Choose and name a duration definition for each conversion action; use one route consistently. Do not interpret either as active speaking time or verified attention.

GTM setup

  1. Load your GTM container on the same document as the widget. For a hosted standalone demo, use its configured container. A browser data layer is document-local; an unrelated parent page does not automatically receive it.
  2. Add Data Layer Variables for hobbes_duration_threshold_seconds, hobbes_email_domain_type, hobbes_session_recording_id, and hobbes_organization_id.
  3. Add a Custom Event trigger named exactly hobbes_session_duration_threshold. Create one trigger per desired threshold, with the numeric variable equal to 60, 300, or 600. Optionally require email type to equal corporate.
  4. For company-email conversions, use Custom Event hobbes_email_classified with hobbes_email_domain_type equal to corporate.
  5. Attach the relevant conversion tag and configure consent through the existing CMP/GTM consent setup. A data-layer event is not consent to send advertising data. Preview in Tag Assistant before publishing your container.
Example conversion identity: organization-uuid:recording-uuid:demo_ready_300s. Use a distinct definition for corporate-only milestones, such as corporate_demo_ready_300s. Supply the same identity to your receiver if both sources are involved. Do not make separate browser and offline conversion actions for the same logical milestone and assume Google will deduplicate across them. See Google’s data-layer guide.

Completion webhook contract

Configure an enabled organization webhook with both notify_on_active_session and notify_on_inactive_session enabled. Those cover eligible analyzed sessions regardless of booking or qualification; enabling only booked/qualified filters would omit the mid-funnel sessions this integration needs. Existing internal, staging-preview, and unclaimed website-chat exclusions still apply. The existing payload retains transcript, sales summary, contact/consent, and tracking fields. This abbreviated example shows the conversion fields:
buying_intent is this session’s persisted analysis, not the person’s aggregate. Low, medium, and high are delivered; the receiver chooses its target levels. Incomplete/failed analysis does not send session.completed. A missing intent is not synthesized as low; skip it for scored conversions. Historical email category and analysis timestamp may be unknown/null. webhook.test is a connectivity test, not a conversion. Use session.end_time for a conversion defined as “completed high-intent demo”. Use analysis_completed_at only if the chosen definition is “analysis became available”. Never use the receiver clock or top-level emission timestamp as a replacement. A recording-duration threshold can use start_time + threshold when start/end and duration support that threshold. Do not use that timestamp for a ready-demo threshold with a different clock origin. The logical event key is also sent in Idempotency-Key. Retries preserve it, the body, and signature; rebuilding delivery for the same recording retains the logical key. Existing bounded retries and delivery diagnostics are unchanged.

Receiver and Google mapping

Verify X-Webhook-Signature: sha256=<hex> over the raw request body using HMAC-SHA256 and the configured webhook secret, with a timing-safe comparison. Validate the expected organization. Ignore events other than session.completed. For example, select sales_summary.buying_intent === 'high', optionally require session.email_domain_type === 'corporate', and choose a named definition such as completed_high_intent_v1. Claim a durable unique key (organization_id, session.id, conversion_definition) in the same database transaction that queues the outbound conversion. Return 2xx only after that transaction commits. An outbox worker can retry Google delivery without losing the conversion if the process stops. A duplicate webhook should acknowledge the existing item; do not mark an item delivered before Google accepts it. An in-memory set alone is insufficient in a deployed receiver. A minimal transformation after signature, organization, consent, and dedup checks:
For Data Manager, wrap that event in an ingestion request with a Google Ads operating account and your offline conversion action as productDestinationId. Add only the conversion value/currency you have defined. Authentication, consent fields, account permissions, ingestion errors, and diagnostics belong to your existing Google integration. Follow Google’s event ingestion guide for the current request schema and destination setup. Missing click IDs must remain missing and be reported as unmatched/skipped for this click-based recipe. UTMs, a corporate email category, or a raw email address are not a GCLID. This work does not introduce enhanced-conversion identifiers or new Google consent collection. Existing Hobbes consent-aware first-touch storage and verified cross-domain handoff are retained.