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.
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.
<!-- 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.
<!-- 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.
<!-- 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).
<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>
EvalQA.embed(options)| Option | Type | Required | Description |
|---|---|---|---|
container | string or Element | Yes | CSS selector or DOM element to render the iframe into. |
template | string | No | One of: foundation, agent, rag, robotics, saas, enduser, universal. Default universal. |
taskId | string | No | Stable task identifier so multiple raters can be matched on the same task. |
prompt | string | No | The input the AI received. Pre-fills the task.prompt field. |
reference | string | No | Gold / reference answer. Strongly recommended for LLM-judge mode. |
systemUnderTest | string | No | e.g. "Acme Copilot v3.2". Pre-fills subject.system_under_test. |
raterId | string | No | Eval Army rater_id (email). If known, the form pre-loads the rater profile. |
evalId + mode:"review" | string | No | Hybrid mode - load an existing LLM-judge eval and let a human confirm/override. |
onSave | function | No | Called with {eval_id, payload} after a successful save. |
onClose | function | No | Called when destroy() is invoked. |
height | number | No | Initial iframe min-height in pixels. Default 640. Auto-resizes as content grows. |
baseUrl | string | No | Override the EvalQA base URL (default https://eval.qa/demo/aif/). |
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);
The iframe communicates with the parent via window.postMessage. All messages have source: "evalqa".
| Message type | Direction | Payload |
|---|---|---|
eval:saved | iframe → parent | { eval_id, payload } |
eval:resize | iframe → parent | { height } - auto-handled by embed.js |
eval:close | iframe → parent | fires when an embedded "Submit another" or destroy() runs |
Create eval suites and get a hash key to embed the form. Sign in to continue.
Sign in