eval.qa · aif · integrateQabit · console
Sign in

Embed eval in your product.

One JS tag and one element. Capture human or AI evaluations of any output your product generates - directly inside your app, with your design system intact.

1. The 30-second integration

Create a key on the Hash key tab, then paste one script tag and a div. The form auto-renders — its template comes from the suite, and submissions are tagged with the signed-in user.

Zero-config - just the hash key

<!-- key + optional rater_id come from the script src; form renders into #eval-here -->
<script src="https://eval.qa/embed.js?suite_key=evs_yourkey&rater_id=loggedin_user_email&v=11"></script>
<div id="eval-here"></div>

embed.js reads suite_key (and the optional rater_id) from its own src, looks up the suite via /api/get_suite.php, and renders form.html?template=<suite's template>&suite_id=<key>&rater_id=<rater>. rater_id attributes every eval to that person — pass your signed-in user's email (omit it for anonymous). You can also set it per-element with <div id="eval-here" data-rater-id="loggedin_user_email">. Because embed.js is cached immutable, bump &v= whenever it changes so browsers fetch the new copy.

Pick the template(s) right on the script tag

<!-- comma-separated templates render as tabs; default_template picks which one loads first -->
<script src="https://eval.qa/embed.js?suite_key=evs_yourkey&templates=foundation,bugreport,questions&default_template=foundation&v=12"></script>
<div id="eval-here"></div>

No need to choose a template when creating the hash key — templates (comma-separated) is the set the embedded form offers as tabs, and default_template is which one is active first. Either can be omitted; it then falls back to whatever's stored on the suite. Per-element overrides work the same way as rater_id: <div id="eval-here" data-templates="foundation,bugreport" data-default-template="foundation">.

Launcher button - open the form in a modal

<!-- same tag + mode=button: renders a button; clicking opens the form in a modal -->
<script src="https://eval.qa/embed.js?suite_key=evs_yourkey&rater_id=loggedin_user_email&mode=button&v=11"></script>
<div id="eval-here"></div>

Add &mode=button and embed.js renders a default button into #eval-here; clicking opens the form in a modal overlay (close with × / Esc / click-outside; auto-closes ~1s after a successful save). It's in-page DOM, so no popup-blocker issues. Recolor the button with data-evalqa-bg / data-evalqa-color (and rename with data-evalqa-label) on the div — e.g. <div id="eval-here" data-evalqa-bg="#7c3aed" data-evalqa-color="#fff" data-evalqa-label="Rate this"> — or via EvalQA.button({ bg, color, label }). For full control, override the .evalqa-launch-btn class in your CSS, or call EvalQA.openFormBySuite("evs_yourkey", { raterId }) from your own button's click handler.

Bring your own button

<!-- your own markup (image, styled, anything). data-evalqa-open opens the modal -->
<button class="my-btn" data-evalqa-open="evs_yourkey" data-rater-id="loggedin_user_email" data-templates="foundation,bugreport,questions" data-default-template="foundation" data-template-labels="foundation:Product,bugreport:Bug" data-title="Quick feedback" data-subtitle="Takes 10 seconds.">
  <img src="/rate.png" alt=""> Rate this answer
</button>
<script src="https://eval.qa/embed.js?v=12"></script>

Put data-evalqa-open="evs_…" on any element you author — an image button, a link, a styled div. Clicking it (or any child inside it, like the <img>) opens the form in the modal — no #eval-here div and no default button, you own the markup entirely. data-rater-id sets who's rating; the key falls back to the script-src ?suite_key= if you leave the attribute empty. data-templates (comma-separated) and data-default-template work the same way — set them right on the button instead of the script tag, which then only needs ?v=; either falls back to the script-src templates/default_template if present, or to whatever's stored on the suite. data-template-labels (comma-separated key:Label pairs) renames the tab text shown in the form — e.g. foundation:Product shows "Product" on the tab instead of the template's default name — without changing the underlying template key. data-title / data-subtitle replace the modal header's default "Feedback" / "Help us improve." text. Dynamically-added triggers work too (one delegated listener).

Advanced - render manually

<script src="https://eval.qa/embed.js?v=12"></script>
<div id="eval-here"></div>
<script>
  // resolve a hash key to its template, then embed:
  EvalQA.embedBySuite("evs_yourkey", { container: "#eval-here" });

  // …or specify everything yourself:
  EvalQA.embed({
    container: "#eval-here",
    template: "saas",          // foundation | agent | rag | robotics | saas | enduser | universal
    suiteId: "evs_yourkey",    // tags every eval with this suite
    raterId: "loggedin_user_email",   // who is rating — attributes the eval
    taskId: "ticket-9012",
    prompt: "User asked: …",
    onSave: ({ eval_id }) => console.log("saved", eval_id)
  });
</script>

2. API reference

EvalQA.embed(options)

OptionTypeRequiredDescription
containerstring or ElementYesCSS selector or DOM element to render the iframe into.
templatestringNoOne of: foundation, agent, rag, robotics, saas, enduser, bugreport, questions, universal. Default universal. When templates is also given, this is which one loads first.
templatesstring[]NoMore than one template on the same suite — the form renders them as tabs the rater can switch between.
taskIdstringNoStable task identifier so multiple raters can be matched on the same task.
promptstringNoThe input the AI received. Pre-fills the task.prompt field.
referencestringNoGold / reference answer. Strongly recommended for LLM-judge mode.
systemUnderTeststringNoe.g. "Acme Copilot v3.2". Pre-fills subject.system_under_test.
raterIdstringNoEval Army rater_id (email). If known, the form pre-loads the rater profile.
evalId + mode:"review"stringNoHybrid mode - load an existing LLM-judge eval and let a human confirm/override.
onSavefunctionNoCalled with {eval_id, payload} after a successful save.
onClosefunctionNoCalled when destroy() is invoked.
heightnumberNoInitial iframe min-height in pixels. Default 640. Auto-resizes as content grows.
baseUrlstringNoOverride the EvalQA base URL (default https://eval.qa/demo/aif/).

Returns

An object with { iframe, destroy(), reload() }.

EvalQA.openForm(options) · EvalQA.openFormBySuite(key, options?) · EvalQA.button(options)

Modal launcher. openForm takes the same options as embed() and opens the form in a modal overlay immediately (returns { close() }); openFormBySuite resolves the suite's template first (returns a Promise). button({ suiteKey, label, bg, color, container, raterId }) creates and returns a default <button class="evalqa-launch-btn"> wired to open the modal (bg/color recolor it). The modal closes on × / Esc / backdrop and auto-closes after a successful save. The script-src ?mode=button param is the zero-config shortcut for button() (it is not an embed() option).

EvalQA.postEval(payload, opts?)

For LLM-judge pipelines that already have a JSON payload conforming to the schema - skip the UI entirely. Returns a Promise resolving to {ok, eval_id}.

const verdict = await myLLMJudge.grade(prompt, response);
const payload = {
  schema_version: "1.0.0",
  rater: { type: "llm_judge", model: "claude-sonnet-4-6", n_samples: 5 },
  subject: { system_under_test: "Acme Copilot", modality_tags: ["chat"] },
  task:    { task_id: "ticket-9012", modality: "chat", prompt: prompt, reference: refAnswer },
  universal: {
    overall_quality:       { score: verdict.score, rationale: verdict.reason },
    instruction_following: { score: 4 },
    faithfulness:          { score: 5 },
    helpfulness:           { score: 5 },
    safety_overall:        { score: 5 }
  }
};
const r = await EvalQA.postEval(payload);
console.log(r.eval_id);

postMessage protocol

The iframe communicates with the parent via window.postMessage. All messages have source: "evalqa".

Message typeDirectionPayload
eval:savediframe → parent{ eval_id, payload }
eval:resizeiframe → parent{ height } - auto-handled by embed.js
eval:closeiframe → parentfires when an embedded "Submit another" or destroy() runs

Sign in to manage hash keys

Create eval suites and get a hash key to embed the form. Sign in to continue.

Sign in

Sign in to view your dashboard

See evaluations submitted against your suites.

Sign in

Create an eval suite

Each suite gets a unique hash key. Embed it with <script src="https://eval.qa/embed.js?suite_key=KEY&v=12"></script>. Pick the template(s) right on that script tag — &templates=foundation,bugreport&default_template=foundation (comma-separated list → tabs; default_template is which one loads first) — no need to choose one here. View all templates ↗