IntegrationsWebhooks

Webhooks

Real-time event-driven notifications for any system. Bullseye sends an HTTP POST to your endpoint when visitors are identified, so you can integrate with internal tools, custom CRMs, or any system that accepts webhooks.

Plan Requirement

Webhooks are available on Scale plans and above. If you’re on Starter, Growth, or Free Trial, upgrade to Scale to enable webhooks.

Overview

  • HTTP POST to your endpoint when visitors are identified
  • HMAC-SHA256 signature verification for security
  • Full visitor, company, and session data in the payload
  • Configurable per organization

Setup

  1. Log in to the Bullseye dashboard and navigate to Settings > Integrations > Webhooks
  2. Click Add Webhook
  3. Enter your endpoint URL (must be HTTPS)
  4. Optionally configure a secret for HMAC-SHA256 signature verification
  5. Select the events you want to receive (e.g., visitor.identified, visitor.phone_revealed)
  6. Save the webhook

Your endpoint must respond with a 2xx status code within 30 seconds. Non-2xx responses or timeouts trigger retries (see Retry Behavior below).

Payload Structure

Each webhook delivery includes a JSON payload. Fields are included in every delivery but may be empty strings, null, or omitted when data is unavailable.

Event Type

The event_type field indicates the type of event. For visitor identification, the value is visitor.identified. When a phone is later unlocked via phone enrichment, Bullseye emits visitor.phone_revealed with the same payload shape and visitor.phone filled.

Top-Level Fields

FieldDescription
event_typeEvent type (e.g., visitor.identified)
timestampISO 8601 timestamp when the event occurred
organization_idYour Bullseye organization ID (UUID)
session_idUnique identifier for the visitor session (UUID)
workspaceOrganization name
visitorIdentified visitor profile data
companyCompany data associated with the visitor
sessionSession metadata (attribution, device, geo, engagement)
page_viewsArray of { url, visited_at } for pages visited in this session

Beyond the basics (name, email, job title), the visitor object can include phone, social URLs, full address, demographics, ICP/persona match status, and timestamps. The company object includes domain, size ranges, revenue range, LinkedIn/website URLs, logo, and address. The session object includes full UTM/campaign params, geo, device/browser signals, IP intelligence (isp, asn, connection_type, is_hosting, is_anonymous), engagement metrics, and ad tracking params (gclid, fbclid, etc.).

The same session IP fields are returned on the visitor profile API as SessionProfile (camelCase: isp, asn, connectionType, isHosting, isAnonymous). See API reference - Webhooks for the full payload and IP intelligence availability matrix.

Example Payload

Minimal example; see the API reference for every field.

{
  "event_type": "visitor.identified",
  "timestamp": "2025-01-15T10:30:00Z",
  "organization_id": "550e8400-e29b-41d4-a716-446655440000",
  "session_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
  "workspace": "Acme Corp",
  "visitor": {
    "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "email": "jane.smith@acme.com",
    "first_name": "Jane",
    "last_name": "Smith",
    "job_title": "VP of Marketing",
    "icp": true,
    "persona_match": true
  },
  "company": {
    "name": "Acme Corp",
    "domain": "acme.com"
  },
  "session": {
    "landing_page": "https://acme.com/pricing",
    "country": "US",
    "region": "Florida",
    "city": "Boynton Beach",
    "isp": "AT&T Enterprises, LLC",
    "asn": "AS7018",
    "connection_type": "isp",
    "is_hosting": false,
    "is_anonymous": false,
    "tracking_params": { "gclid": "abc123" }
  },
  "page_views": [
    { "url": "https://acme.com/pricing", "visited_at": "2025-01-15T10:25:00Z" }
  ]
}

Full payload: api-reference/webhooks.md

HMAC-SHA256 Verification

To verify that webhook deliveries come from Bullseye, use the X-Bullseye-Signature header. The header contains an HMAC-SHA256 signature of the raw request body using your webhook secret.

Verification Steps

  1. Read the raw request body (before any JSON parsing)
  2. Compute HMAC-SHA256(raw_body, your_webhook_secret)
  3. Encode the result as hexadecimal
  4. Compare with the value in X-Bullseye-Signature

If the values match, the webhook is authentic. Reject requests where the signature does not match.

Example (Pseudocode)

signature = request.headers["X-Bullseye-Signature"]
expected = hmac_sha256_hex(request.raw_body, webhook_secret)
if signature != expected:
  return 401  # Unauthorized

Retry Behavior

If your endpoint returns a non-2xx status code or does not respond within 30 seconds, Bullseye retries the delivery:

  • Retry schedule - Retries occur at increasing intervals (e.g., 1 minute, 5 minutes, 30 minutes)
  • Maximum retries - Up to 3 retries per delivery
  • Idempotency - The same payload may be delivered more than once; design your endpoint to handle duplicate events (e.g., using session_id as a deduplication key)

Best Practices

  • Use HTTPS - Webhook endpoints must use HTTPS
  • Verify the signature - Always validate X-Bullseye-Signature before processing
  • Respond quickly - Return 200 immediately and process the payload asynchronously if needed
  • Handle duplicates - Use session_id or a combination of fields to deduplicate events
  • Log failures - Monitor for 4xx/5xx responses and fix endpoint issues promptly