JavaScript API

The script tag creates one client and exposes it as window.signal. Calls never throw and never block your page: when something fails, a method returns null or false and the client emits an error event.

// Wait until Signal has a session (resolves within a few seconds, or carries on without).
await window.signal?.ready;
console.log(window.signal.visitorId);

Tracking events

window.signal.track("cta_clicked", { label: "Book a demo" });
window.signal.track("pricing_viewed");
  • type: letters, digits and _ . : -, up to 64 characters.
  • data: any small JSON object.
  • Events are batched and sent every 2 seconds (page_view, cta_clicked, pricing_viewed, resource_requested and contact_started go immediately), and on page hide.
  • Signal records page_view, engagement (time on the page) and, on Shopify, product_view by itself.
  • Event types listed under classifyOn in your configuration make Signal re-estimate the visitor straight away.

Identifying a visitor

When a visitor tells you who they are on your own forms or login, pass it on:

await window.signal.identify({
  name: "Robin Baker",
  email: "robin@example.com",
  company: "Bakery Robin",
  externalId: "user_123",          // your own id
  traits: { plan: "pro", seats: 12 },
});

All fields are optional. Only identify people who gave you these details themselves.

Reading what Signal knows

const profile = await window.signal.getProfile();
// {
//   visitorId: "vis_…",
//   identity: { name?: "Robin", company?: "…", emailKnown: true },
//   signals: { leadTemperature: { value: "hot", confidence: 0.86, label: "Lead temperature" } },
//   session: { pageViews: 4, engagedSeconds: 130, returningVisitor: true }
// }

Events

const off = window.signal.on("signal_changed", ({ key, value, confidence, previous }) => {
  if (key === "leadTemperature" && value === "hot") showSalesBanner();
});
off(); // unsubscribe
Event Payload When
ready { visitorId, sessionId, config } A session opened (or the ids changed).
profile_changed profile The visitor's profile changed.
signal_changed { key, value, confidence, previous? } An estimate changed.
question InteractionEvent Signal wants to ask a question.
offer InteractionEvent Signal wants to show an offer.
action InteractionEvent A configured browser action (message, link, …).
chat InteractionEvent The team is chatting with this visitor.
outcome { interactionId, routeId?, step } The visitor chose a route, gave details or declined.
form { formId, phase, runId? } A smart form started, moved a step, was submitted or ended.
next next interaction Every time Signal's next step changes.
identified profile After identify().
error Error A call failed (network, validation, rate limit).

Drawing your own UI

question, offer, action and chat events carry { interaction, preventDefault() }. Call preventDefault() to keep it out of the built-in popup and show it your way:

window.signal.on("question", (event) => {
  event.preventDefault();
  const { interactionId, question } = event.interaction;
  // question.input.type is "choice", "text" or "email"
  renderMyQuestion(question, (optionId) =>
    window.signal.answer({ questionId: question.id, optionId, interactionId }));
});

window.signal.on("offer", async (event) => {
  event.preventDefault();
  const { interactionId, offer } = event.interaction;
  // offer.routes: [{ id, type, label, effort, needs: [{ key, label, type }] }]
  const step = await window.signal.chooseRoute(interactionId, offer.routes[0].id);
  // step.step is "collect" (ask for step.fields), "open" (step.url), "done", "chat" or "products"
});

To turn the popup off entirely, add data-signal-popup="false" to the script tag.

Method Returns Use
answer({ questionId, optionId?, text?, interactionId? }) { next, profile } or null Answer a question.
dismiss(interactionId) boolean The visitor closed it.
chooseRoute(interactionId, routeId) step or null The visitor picked a route of an offer.
submitContact(interactionId, { routeId, fields, consent }) step or null Send the fields a collect step asked for.
declineOffer(interactionId) boolean "No thanks".
getNext() next interaction Ask for the current next step.

Smart forms from code

await window.signal.mountForm("#contact", "projectLead"); // element or selector, form key
window.signal.setConsent({ storage: "persistent", analytics: true, personalisation: true });

See Consent and storage.

Other methods

Method What it does
reset() Forget this visitor and start a new anonymous one (e.g. on log-out).
lastErrorCode() The error code of the last failed call, e.g. "too_many_submissions".
destroy() Stop Signal on this page and remove the popup and forms.

DOM events

  • window receives signal:event (event.detail = { event, data }) for configured emit_event actions, when the built-in popup is on.
  • document receives signal:cart-added (event.detail = { variantId, handle }) when a visitor adds a recommended product to the cart from the popup.