Integration

Hand the packet to your IT team. Five steps; the email below is pre-written and waiting for one click.

STEP A

Send to your IT team

Fill the four fields. We generate a self-contained email — your IT person never needs to read this dashboard.

IT contact email
Your company name (optional)
Widget key
No active widget keys yet. Issue one →
Environment
Default workload template

Used as the default in the snippet. Your IT team / customers can override per-call.

Generate email packet
STEP B

Allowed origins

One per line. Wildcards allowed (https://*.yourapp.com). The browser only fetches a recommendation when the embedding page's origin is on this list.

STEP C

Check connection

Paste any URL to verify it'll be accepted. Then click 'Open preview' to see the actual widget render.

Test origin
Live widget preview

Paste your widget key in Step A to enable preview.

STEP D

Code snippets (if you prefer to copy directly)

Same code that lands inside the email packet. Replace QLRO_KEY env reference with your widget key, or use the React component as-is.

React
import { QlroRecommend } from "@qlro/embed/react";

export function YourFeature() {
  return (
    <QlroRecommend
      partnerKey={process.env.NEXT_PUBLIC_QLRO_KEY!}
      workload={{ template: "chemistry.lih_ground_state" }}
      theme="auto"
      lang="en"
      onRecommendation={(rec) => {
        // hand off to your CRM / lead-routing
      }}
    />
  );
}
Plain HTML / ESM
<div id="qlro"></div>
<script type="module">
  import { attach } from "https://esm.sh/@qlro/embed@0.1.0";
  attach({
    target: document.getElementById("qlro"),
    partnerKey: "qlro-pkw_...",
    workload: { template: "chemistry.lih_ground_state" },
    theme: "auto",
  });
</script>
iframe (quickest test)
<iframe
  src="https://partner.qlro.io/embed/recommend?partner_key=qlro-pkw_...&template=chemistry.lih_ground_state&theme=auto"
  style="width:100%;border:0;min-height:280px"
  title="Qlro recommendation"
></iframe>

Test from https://yourapp.com to confirm the allowlist + key combination is live.

STEP E

Webhook signature verification

If you wire webhooks (Step F on /partner/webhooks), verify every payload with HMAC-SHA256 before trusting it. Snippets your IT team can paste verbatim.

Header: X-Qlro-Signature · Algorithm: HMAC-SHA256 over the raw request body, using the project's webhook_secret. Use a constant-time comparison.

Python
import hmac, hashlib

def verify_qlro_signature(secret: str, body: bytes, signature: str) -> bool:
    """Constant-time HMAC-SHA256 verification.

    Pass the raw request body bytes (do NOT json.loads first) — the
    signature is computed over the exact bytes Qlro sent.
    """
    expected = hmac.new(
        secret.encode("utf-8"),
        body,
        hashlib.sha256,
    ).hexdigest()
    return hmac.compare_digest(expected, signature)
Node.js / Express
const crypto = require("crypto");

function verifyQlroSignature(secret, body, signature) {
  // body must be the raw bytes, not a JSON-parsed object.
  // In Express: app.use(express.raw({ type: "application/json" }));
  const expected = crypto
    .createHmac("sha256", secret)
    .update(body)
    .digest("hex");
  return crypto.timingSafeEqual(
    Buffer.from(expected),
    Buffer.from(signature),
  );
}
Go
import (
    "crypto/hmac"
    "crypto/sha256"
    "encoding/hex"
)

func VerifyQlroSignature(secret string, body []byte, sigHex string) bool {
    mac := hmac.New(sha256.New, []byte(secret))
    mac.Write(body)
    expected := hex.EncodeToString(mac.Sum(nil))
    return hmac.Equal([]byte(expected), []byte(sigHex))
}
Independence guarantee: the iframe is hosted on qlro.io. The recommendation, partner-name byline, and snapshot DOI footer are all server-rendered and cannot be CSS-hidden. read the clause →