eval.qa · aif · integrateeval.qa · qabit · console
try the form →
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=10"></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.

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=10"></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">
  <img src="/rate.png" alt=""> Rate this answer
</button>
<script src="https://eval.qa/embed.js?v=10"></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. Dynamically-added triggers work too (one delegated listener).

Advanced - render manually

<script src="https://eval.qa/embed.js?v=10"></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, universal. Default universal.
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