Skip to Content

Websites API

Create and manage websites for visitor identification and tracking.

Required Headers

HeaderDescription
x-access-keyYour Warm AI API key
x-user-idYour Warm AI user ID
x-idempotency-keyUnique key to prevent duplicate requests
Content-Typeapplication/json

For better prospect matching and visitor scoring, link your Ideal Customer Profiles (ICPs) to your website when creating it. This ensures visitors are automatically scored against your target profiles.

{ "action": "create_website", "domain": "example.com", "name": "My Website", "icp_ids": ["icp_abc123", "icp_def456"] }

Installing the Tracking Script

After creating a website, add the following script to the <head> tag of your site:

<script src="https://assets.warmai.uk/warm.js" data-id="YOUR_TRACKING_ID" async></script>

Replace YOUR_TRACKING_ID with the tracking_id returned when you create a website.

For details on what warm.js captures and how to configure it, see Tracking. The public compliance disclosure (lawful basis, sub-processors, visitor opt-out) is at getwarmai.com/tracker  — share with your legal/security team or auditors.

Using WordPress or a site builder? Rich-text editors, optimization plugins, and consent managers can silently break the snippet. See the troubleshooting guide below if you don’t see sessions landing within a few minutes of a page view.


Troubleshooting install issues

If tracking isn’t working after you’ve installed the snippet, one of the following is usually the cause.

Smart quotes (WordPress, Elementor, Gutenberg rich-text)

Rich-text editors in WordPress, Elementor, and the Gutenberg visual editor convert straight quotes (" ") into curly “smart quotes” ( ) when you paste code. Browsers can’t parse curly quotes as attribute delimiters, so the script silently fails to load.

Fix: Paste the snippet into a plain-text block — for example:

  • Gutenberg’s Custom HTML block
  • A header/footer injection plugin like WPCode or Insert Headers and Footers
  • Directly in your theme’s header.php

After saving, view the page source (view-source:yoursite.com) and confirm the src and data-id attributes use straight quotes.

WP Rocket (script lazy-loading)

WP Rocket wraps third-party scripts in type="text/rocketlazyloadscript", deferring execution until the first user interaction (scroll, click, hover, etc.).

For visitor identification, this is usually acceptable — engaged visitors naturally trigger an interaction, while pure bounces go uncaptured. If that trade-off is fine for your use case, no action needed.

If you want every page load tracked regardless, in WP Rocket → File Optimization → JavaScript → Excluded JavaScript Files, add:

assets.warmai.uk/warm.js

Save and clear cache.

Autoptimize, WP-Optimize, and similar minifiers

JS combine/minify plugins can break async loading. Exclude assets.warmai.uk/warm.js from JavaScript optimization in the plugin’s settings.

Cloudflare Rocket Loader

Rocket Loader automatically defers third-party scripts via async injection. Either:

  • Add data-cfasync="false" to the script tag, or
  • In the Cloudflare dashboard → Speed → Optimization → Rocket Loader, add assets.warmai.uk to the ignored URLs.

Consent managers block scripts categorised as analytics or tracking until a visitor accepts cookies — and warm.js falls into that category. warm.js sets a first-party warm_device cookie (a 1-year UUID used for cross-session visitor identification) and captures form-engagement signals, so it must be treated as an analytics / statistics script, not as a necessary or functional one.

To keep tracking working after consent is granted, add assets.warmai.uk/warm.js to your consent manager as an analytics script and ensure it fires only after the visitor accepts. Most platforms (Cookiebot, OneTrust, Termly) handle this automatically once the script is tagged correctly.

See the Cookies & Consent guide for a full walkthrough including a sample Cookiebot configuration.

Content Security Policy (CSP)

If your site serves a Content-Security-Policy header, allow:

  • script-src: https://assets.warmai.uk
  • connect-src: https://track.getwarmai.com and https://muagykdazutcjpapkcer.supabase.co

Single-page apps (React, Next.js, Vue)

warm.js hooks into the History API (pushState, replaceState, popstate), so client-side navigation is tracked automatically. No extra work needed. If you’re loading the script inside a component instead of in <head>, make sure it runs exactly once per page load (not on every mount).


Verifying your install

  1. Open your site in a new tab with DevTools → Network open.
  2. Filter for warm.js — the request should return 200 OK. A 404 means the src URL is wrong (it’s warm.js, not tracker.js).
  3. Interact with the page (scroll, click a link).
  4. Filter for /api/track or tracking-event — you should see POST 200 responses.
  5. Check your Warm AI dashboard — sessions appear within ~30 seconds.

If the Network tab shows no warm.js request at all, the snippet isn’t executing (check smart quotes, WP Rocket, or Rocket Loader). If warm.js loads but no /api/track POST fires, check CSP or a consent manager blocking the connect-src.


Endpoints

Create Website

POST https://api.warmai.uk/api-website-action

Creates a new website for visitor identification.

Request Body

FieldTypeRequiredDescription
actionstringYesMust be "create_website"
domainstringYesThe domain of your website (e.g. example.com)
namestringNoA display name for the website
icp_idsstring[]NoArray of ICP IDs to link for visitor scoring

Example Request

{ "action": "create_website", "domain": "example.com", "name": "My Marketing Site", "icp_ids": ["icp_abc123"] }

Response Fields

FieldTypeDescription
idstringUnique website ID
domainstringThe website domain
namestringDisplay name
tracking_idstringID used in the tracking script
statusstringWebsite status
created_atstringISO 8601 timestamp
linked_icp_idsstring[]ICP IDs linked to this website

Example Response

{ "id": "ws_a1b2c3d4", "domain": "example.com", "name": "My Marketing Site", "tracking_id": "trk_x9y8z7", "status": "active", "created_at": "2026-03-19T12:00:00Z", "linked_icp_ids": ["icp_abc123"] }

List Websites

POST https://api.warmai.uk/api-website-action

Retrieve all websites associated with your account.

Request Body

FieldTypeRequiredDescription
actionstringYesMust be "list_websites"
user_idstringNoFilter websites by a specific user ID

Example Request

{ "action": "list_websites" }

Response Fields

FieldTypeDescription
websitesarrayList of website objects
websites[].idstringUnique website ID
websites[].domainstringThe website domain
websites[].namestringDisplay name
websites[].tracking_script_idstringID used in the tracking script
websites[].statusstringWebsite status (active, paused, archived)
websites[].created_atstringISO 8601 timestamp
websites[].linked_icp_idsstring[]ICP IDs linked to this website

Example Response

{ "websites": [ { "id": "ws_a1b2c3d4", "domain": "example.com", "name": "My Marketing Site", "tracking_script_id": "trk_x9y8z7", "status": "active", "created_at": "2026-03-19T12:00:00Z", "linked_icp_ids": ["icp_abc123"] }, { "id": "ws_e5f6g7h8", "domain": "blog.example.com", "name": "Company Blog", "tracking_script_id": "trk_w6v5u4", "status": "active", "created_at": "2026-03-18T09:30:00Z", "linked_icp_ids": [] } ] }

How Tracking Works

When a visitor browses your website:

  1. warm.js captures page views, scroll depth, and session duration
  2. On session end (tab close or 30 min inactivity), identification runs automatically
  3. The visitor’s IP is checked against our databases and multiple paid providers
  4. VPN, hosting, and ISP traffic is filtered at zero cost
  5. Business matches are POSTed to your webhook URL as a signed payload

warm.js sets a first-party warm_device cookie for cross-session identification. UK and EU sites must obtain consent before the script runs — see Cookies & Consent.


Webhook Delivery

Configure your webhook URL in your API key settings. Every visitor session triggers a webhook to your endpoint.

Headers

HeaderDescription
X-Warm-SignatureHMAC-SHA256 signature for payload verification
X-Warm-TimestampUnix timestamp of the event
X-Warm-Eventvisitor_identified or visitor_not_identified
Content-Typeapplication/json

Visitor Identified

Sent when a business visitor is successfully identified.

{ "identified": true, "ip_address": "144.9.12.81", "session_id": "uuid", "identification_id": "uuid", "tracking_domain": "yoursite.com", "tracking_website_id": "uuid", "session": { "started_at": "2026-04-14T10:00:00Z", "ended_at": "2026-04-14T10:05:30Z", "duration_seconds": 330, "page_count": 4, "referrer": "https://google.com", "pages": [ { "url": "https://yoursite.com/", "path": "/", "title": "Home", "scroll_depth": 80, "duration_seconds": 45 }, { "url": "https://yoursite.com/pricing", "path": "/pricing", "title": "Pricing", "scroll_depth": 100, "duration_seconds": 120 }, { "url": "https://yoursite.com/features", "path": "/features", "title": "Features", "scroll_depth": 60, "duration_seconds": 90 }, { "url": "https://yoursite.com/contact", "path": "/contact", "title": "Contact", "scroll_depth": 40, "duration_seconds": 75 } ] }, "identification_type": "individual", "identified_at": "2026-04-14T10:05:30Z", "company": "Goldman Sachs", "domain": "goldmansachs.com", "traffic_type": "business", "confidence": 0.91, "confidence_level": "confirmed", "individual": { "linkedin_name": "Sarah Chen", "linkedin_title": "Managing Director", "linkedin_company": "Goldman Sachs", "linkedin_url": "https://linkedin.com/in/sarachen" }, "company_data": { "name": "Goldman Sachs", "domain": "goldmansachs.com", "industry": "Financial Services", "employee_count": "10001+", "location": "New York, US", "linkedin_url": "https://linkedin.com/company/goldman-sachs" }, "decision_makers": [ { "name": "Jane Doe", "title": "CTO", "linkedin_url": "https://linkedin.com/in/janedoe", "seniority": "c_suite" } ] }

Visitor Not Identified

Sent when a visitor could not be matched to a business (residential ISP, VPN, etc.).

{ "identified": false, "ip_address": "81.246.16.178", "session_id": "uuid", "tracking_domain": "yoursite.com", "tracking_website_id": "uuid", "reason": "Residential ISP", "session": { "started_at": "2026-04-14T09:45:00Z", "ended_at": "2026-04-14T09:46:20Z", "duration_seconds": 80, "page_count": 2, "referrer": "https://linkedin.com", "pages": [ { "url": "https://yoursite.com/", "path": "/", "title": "Home", "scroll_depth": 40, "duration_seconds": 50 }, { "url": "https://yoursite.com/about", "path": "/about", "title": "About", "scroll_depth": 20, "duration_seconds": 30 } ] } }

Only business identifications are charged credits. VPN, hosting, and ISP traffic is detected and never billed.

Verifying Webhook Signatures

Verify the HMAC-SHA256 signature to ensure the webhook is from Warm AI:

const crypto = require('crypto'); function verifyWebhook(payload, signature, timestamp, secret) { const expected = crypto .createHmac('sha256', secret) .update(`${timestamp}.${JSON.stringify(payload)}`) .digest('hex'); return signature === expected; }
Last updated on