Embed security

Two controls keep your public widget from being abused: an HMAC signature that proves the embed configuration came from your server, and a browser origin allowlist that decides which sites may load the widget at all. This page is for the web developer doing the install.

The embed signing secret

Open Channels & embed → Credentials. The page, titled Embed signing secret, holds one secret per organization. It signs widget configuration on your server so visitors cannot tamper with identity or routing. Anyone with this value can mint valid signatures for your organization, so store it like a password.

If no secret exists yet, select Generate embed secret. Once one exists you see a masked preview; Show full secret reveals it and Copy puts it on your clipboard. Keep it server-side on properties you control. Never embed it in public JavaScript.

How signing works

Harriet verifies an HMAC-SHA256 signature over a canonical string: sorted key=value pairs joined with &, built from every security-relevant field you send.

Your backend computes signature = HMAC_SHA256_hex(widget_secret, canonical_string), and the page passes the same fields plus timestamp and signature to HarrietChat.init. The console's Embed documentation page carries this Node.js example (Python and Salesforce Apex versions live there too):

const crypto = require("crypto");

function harrietWidgetSignature(secret, fields) {
  const ts = Math.floor(Date.now() / 1000);
  const merged = { ...fields, timestamp: String(ts) };
  const canonical = Object.keys(merged).sort().map((k) => `${k}=${merged[k]}`).join("&");
  const sig = crypto.createHmac("sha256", secret).update(canonical).digest("hex");
  return { signature: sig, timestamp: ts };
}

Two rules follow from the canonical string. First, only include userEmail and userContactId in HarrietChat.init if your server also put them into the string it signed; otherwise the signature will not match. Second, browserContext is not part of the signed field set. Signing covers identity and access claims; page context rides on the X-Browser-Context header and is protected by the origin allowlist instead.

For end users, always sign on your own server so the secret never ships to the browser. Authenticated staff calling the Harriet signature API can omit identity fields.

The browser origin allowlist

Open Channels & embed → Browser origins (page title: Widget browser origins). List the hostnames allowed as the browser Origin for your embedded widget, comma-separated. Use exact hosts such as www.example.com, or the wildcard *.example.com to allow any subdomain. Apex domains must be listed explicitly; *.example.com does not include example.com.

Pages on hosts outside this list cannot use the widget, which stops other sites from calling your tenant with a copied embed. Re-review the list when you sunset a site or sell a brand so old embeds cannot keep calling in.

What a bad request gets

RequestResult
widgetId sent without a signatureRejected. If you send widgetId, you must sign it.
Signed field set differs from the fields sentSignature verification fails. Check sort order and a missing widgetId first.
Unknown widgetId, or one belonging to another customerThe chat API returns 400. There is no silent fallback to a default widget.
Page host not on the origin allowlistThe origin is not permitted for the widget API, so the widget does not work on that page.

Rotating the secret

On the Credentials page, Regenerate secret replaces the current value. Every server that signs embeds must switch to the new secret; signatures produced with the old one stop verifying. Plan the rotation like any credential change: update your signing service first, deploy, then regenerate if you suspect exposure, or regenerate first when you know the secret leaked and accept the brief outage on signed embeds.

⚠️

Widgets that send only customerId without widgetId can run unsigned, which means anyone can start a chat from an allowlisted page. The origin allowlist is then your only gate, so keep it tight, and prefer signed widgetId embeds for anything beyond a basic public Q&A panel.

For what the widget itself can know and do, see Website widget. For the area as a whole, see the Channels & embed overview.