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 existingwindow.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.
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
- 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.
- Add Data Layer Variables for
hobbes_duration_threshold_seconds,hobbes_email_domain_type,hobbes_session_recording_id, andhobbes_organization_id. - 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 equalcorporate. - For company-email conversions, use Custom Event
hobbes_email_classifiedwithhobbes_email_domain_typeequal tocorporate. - 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.
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 bothnotify_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
VerifyX-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:
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.