Skip to main content
This reference describes outgoing FirstTouch webhooks. For destination setup, see Configure Webhooks.

Event Catalogue

Each destination subscribes to one or more exact event names.

Event Scope

  • A queued, draft, or unconfirmed send is not a successful sent event.
  • Received events concern FirstTouch-tracked conversations, not every message in a connected inbox. LinkedIn reply attribution requires an identifiable single external contact and FirstTouch conversation context.
  • Initial LinkedIn history imports do not produce live reply events. Configuring a destination does not backfill historical activity.
  • InMail and regular messages have distinct subscriptions. A manual FirstTouch reply uses the regular-message send category, even in an InMail-originated conversation.
  • Signal events describe detection, not proof that an associated Flow Enrollment started or completed. Company-only website signals may not identify an individual contact.

Connection Outcomes

For linkedin.connection.request_sent, inspect data.connection.request_submitted and data.connection.outcome. The event name alone does not establish that a new outbound invitation was submitted. Acceptance events use the same connection object, but execution outcome fields can be null when unavailable.

Request Format

FirstTouch sends an HTTP POST with Content-Type: application/json. Field names use snake_case. Each request body represents one event, not a batch.

Common Envelope

Payloads are recorded when the delivery is created. Subsequent contact changes do not update the recorded payload. Receivers should tolerate additional fields and handle unavailable values rather than assuming every field is populated.

Business Data

audience_contact_id is supplied only when a unique contact in the Team was matched. HubSpot record IDs are strings, are distinct from FirstTouch IDs, and may be null. A HubSpot record URL requires access to its portal; the presence of the webhook does not establish that CRM sync completed. message.tone may be positive, negative, neutral, or meeting, or null when unavailable. A bounce category or message ID may be unavailable; handle bounce_type: "unknown" without inferring a hard or soft bounce.

Content And Optional Fields

Destinations created or saved through the current settings form include available sensitive data. There is no separate content toggle in that form. The payload format also supports destinations with sensitive data disabled; their content fields are omitted rather than guaranteed to appear as null. When included and available:
  • contact adds phone, role, location, and image_url.
  • company adds industry, size, location, site_url, and estimated_revenue.
  • message adds subject, text, content_type, recipient arrays (cc, bcc, additional_recipients), and attachments.
  • connection adds note.
  • signal adds context, engagement_type, reaction_type, post_url, post_text, post_author_linkedin_url, visited_url, referrer, captured_domain, and tags.
Message bodies contain the complete available content, not a fixed-length preview. Content can be plain text or HTML; content_type may be unavailable. Treat all content as untrusted data and do not render HTML without sanitisation. Not every message source supplies subjects, recipient lists, content formats, or attachments. Inbound additional_recipients are not necessarily CC recipients. Attachments contain metadata only: name, mime_type, size in bytes, and url when available. Binaries are not embedded. Attachment URLs may be null or expire. Unavailable selected fields can be null; optional arrays can be absent. Missing workflow or contact information is not by itself a delivery failure. The destination’s Testing > Example data > Fields view provides searchable descriptions, while JSON shows examples for that destination’s settings.

Example: Received Email

All values below are synthetic. Real fields depend on the recorded event and the destination’s content settings.

Example: Connection Step Without A New Invitation

This is the data.connection fragment of a linkedin.connection.request_sent payload. It is not a complete request body.

Example: Website Signal

This is the data.signal fragment of a signal.website.detected payload with detailed content included. The complete event also uses the common envelope and contact/company objects.

Signature Verification

The signature is HMAC-SHA256, using the complete signing secret (including whsec_) as a UTF-8 key. Do not Base64-decode the secret. The signed bytes are the timestamp header, a literal ., and the unaltered raw UTF-8 request body.
Verify before trusting or processing the payload. Parsing and reserialising JSON, changing whitespace, or normalising line endings changes the signed bytes. The Node.js example below accepts any matching signature for a trusted active secret. Its five-minute timestamp tolerance is a receiver-side recommendation, not a FirstTouch delivery expiry. Keep the receiver’s clock synchronised. Each retry or resend is signed with its current attempt timestamp.
Pass the raw request buffer, the two signature-related headers, and secrets loaded from secure server-side configuration. During rotation, accept the old and new secret until the receiver update is complete and the old secret expires. For operational steps, see Change A Signing Secret.

Duplicate Handling

Delivery can be repeated; exactly-once processing is not guaranteed. A receiver may have accepted an event even if FirstTouch did not receive its response.
  • Deduplicate by idempotency_key or X-FT-Webhook-Id, not by attempt ID or the delivery history row ID of a resend.
  • Record the deduplication identity durably alongside your processing or queue operation.
  • Return 2xx for an already-accepted duplicate rather than repeating its side effects.
  • Acknowledge promptly after durable acceptance; perform slow business processing asynchronously.
  • Do not assume business events arrive in occurrence order. A retry can arrive after a newer event.

Retries And Resends

Any HTTP status from 200 through 299 completes a delivery successfully. Other statuses, including 3xx, 4xx, 429, and 5xx, and attempts without a response are retried within the attempt budget. Redirects are not followed. The retry schedule is not adjusted from the receiver’s Retry-After header. There are eight total attempts: the initial attempt and up to seven retries. The nominal gaps after each unsuccessful attempt, not cumulative times from creation, are: Each gap varies by ±5%. Without other delays, the last attempt is approximately 24 hours after the first. Scheduling, backlog, and timeout protection can delay delivery further; this is not a delivery-time guarantee. After eight unsuccessful attempts, the delivery becomes failed. Five consecutive timeouts without an HTTP response pause sending to that destination for one hour. Receiving an HTTP response resets the timeout counter. The pause does not turn off the destination. Resend creates a fresh delivery execution with its own attempt history and retry budget. It preserves the original payload bytes, event_id, is_test, and idempotency_key, and links to the original delivery. It uses the destination’s current URL and active signing secrets. A receiver that has already processed the identity should acknowledge the resend without duplicating work. A delivery can be resent only within the 30-day history window, after it is no longer pending or processing, and while its destination exists, is enabled, and has an active signing secret. Resend is available for eligible completed deliveries, not only failed ones.

Statuses And Limits

There are no destination controls for custom headers, per-destination rate limits, or segment filters in the current settings form. If your receiving app requires additional authentication headers, use a receiver or adapter that can validate FirstTouch signatures and perform the authenticated downstream request.