> ## Documentation Index
> Fetch the complete documentation index at: https://docs.ringg.ai/llms.txt
> Use this file to discover all available pages before exploring further.

> ## Agent Instructions
> To search or read these docs, connect the Ringg docs MCP server at https://docs.ringg.ai/mcp (no key needed; tools search_ringg_ai and query_docs_filesystem_ringg_ai) instead of browsing pages one by one. Setup for each client: https://docs.ringg.ai/resources/mcp-server/docs-mcp-server/mcp-for-ai-assistants.md
> The dashboard labels agents "Assistants"; these docs say "agent". Both mean the same object, identified by agent_id.
> API base URL: https://prod-api.ringg.ai/ca/api/v0. Send the workspace API key in the X-API-KEY header, from server-side code only.
> Placing a call dials a real phone number and spends credits. Confirm the number with the user before calling.
> To act on a Ringg workspace (place calls, edit agents, read transcripts), use the separate Ringg workspace MCP server at https://mcp.ringg.ai/mcp: https://docs.ringg.ai/resources/mcp-server/workspace-mcp-server/overview.md

# Lesson: Evals and alerts

> Write an Evals test case that checks Ananya records a promise to pay, run it as a simulated call, then create an alert on the call failure rate.

export const Quiz = ({question, answers, correctFeedback, incorrectFeedback}) => {
  const [selected, setSelected] = React.useState(null);
  const [checked, setChecked] = React.useState(false);
  const quizId = React.useId();
  const isCorrect = checked && answers[selected]?.correct;
  const reset = () => {
    setSelected(null);
    setChecked(false);
  };
  const optionStyle = (answer, i) => {
    const base = {
      display: "flex",
      alignItems: "flex-start",
      gap: "0.6rem",
      padding: "0.6rem 0.8rem",
      marginTop: "0.5rem",
      borderRadius: "0.6rem",
      border: "1px solid rgba(128, 128, 128, 0.35)",
      cursor: checked ? "default" : "pointer",
      lineHeight: 1.45
    };
    if (checked && answer.correct) {
      return {
        ...base,
        borderColor: "rgba(22, 163, 74, 0.8)",
        background: "rgba(22, 163, 74, 0.12)"
      };
    }
    if (checked && selected === i && !answer.correct) {
      return {
        ...base,
        borderColor: "rgba(220, 38, 38, 0.8)",
        background: "rgba(220, 38, 38, 0.1)"
      };
    }
    if (!checked && selected === i) {
      return {
        ...base,
        borderColor: "currentColor",
        background: "rgba(128, 128, 128, 0.12)"
      };
    }
    return base;
  };
  const buttonStyle = disabled => ({
    marginTop: "0.9rem",
    padding: "0.45rem 0.9rem",
    borderRadius: "0.5rem",
    border: "1px solid rgba(128, 128, 128, 0.5)",
    background: "rgba(128, 128, 128, 0.1)",
    color: "inherit",
    font: "inherit",
    fontSize: "0.9em",
    cursor: disabled ? "not-allowed" : "pointer",
    opacity: disabled ? 0.5 : 1
  });
  return <div style={{
    marginTop: "1.5rem",
    padding: "1rem 1.1rem",
    borderRadius: "0.8rem",
    border: "1px solid rgba(128, 128, 128, 0.35)"
  }}>
      <Badge color="green">Quiz</Badge>
      <p id={quizId + "-question"} style={{
    fontWeight: 600,
    margin: "0.6rem 0 0.2rem"
  }}>
        {question}
      </p>
      <div role="radiogroup" aria-labelledby={quizId + "-question"} aria-disabled={checked}>
        {answers.map((answer, i) => <label key={i} style={optionStyle(answer, i)}>
            <input type="radio" name={"quiz-" + quizId} checked={selected === i} onChange={() => !checked && setSelected(i)} disabled={checked} style={{
    marginTop: "0.3rem",
    flexShrink: 0
  }} />
            <span>{answer.text}</span>
          </label>)}
      </div>
      {checked && <div role="status" aria-live="polite" aria-atomic="true" style={{
    marginTop: "0.9rem",
    padding: "0.7rem 0.9rem",
    borderRadius: "0.6rem",
    lineHeight: 1.5,
    background: isCorrect ? "rgba(22, 163, 74, 0.12)" : "rgba(220, 38, 38, 0.1)"
  }}>
          <strong style={{
    marginRight: "0.4rem"
  }}>{isCorrect ? "✓" : "✗"}</strong>
          {isCorrect ? correctFeedback : incorrectFeedback}
        </div>}
      {!checked ? <button type="button" onClick={() => selected !== null && setChecked(true)} disabled={selected === null} style={buttonStyle(selected === null)}>
          Check answer
        </button> : <button type="button" onClick={reset} style={buttonStyle(false)}>
          Try again
        </button>}
    </div>;
};

export const RelatedTopics = ({items}) => <div className="not-prose mt-12">
    <div className="text-sm font-medium text-gray-500 dark:text-zinc-400 mb-3">Related topics</div>
    <div className="flex flex-col gap-2.5">
      {items.map(([title, href]) => <a key={href} href={href} className="flex items-center gap-2.5 text-sm text-gray-700 hover:text-gray-950 dark:text-zinc-300 dark:hover:text-white">
          <Icon icon="file-text" size={16} color="currentColor" />
          {title}
        </a>)}
    </div>
  </div>;

**Part 6: Measuring and improving your agent · Lesson 3 of 4 · 20 minutes**

## Objective

Make the promise-to-pay path a test Ananya has to pass, and get an email when her calls start failing.

## The scenario

Three transcripts you can read; three thousand you can't. Evals scores every completed call and replays one conversation path as a simulated call; Alerts watch a metric on a schedule. Ringg turns Evals on per agent: if the page says "No agents have evals enabled.", write to [admin@ringg.ai](mailto:admin@ringg.ai) with the agent ID and do the alert first.

## Hands-on

<Frame caption="Reviewing an agent's evals">
  <video alt="Reviewing an agent's evals" aria-label="Reviewing an agent's evals" controls muted playsInline className="w-full rounded-xl" style={{ aspectRatio: "16 / 10" }} preload="metadata" controlsList="nodownload noremoteplayback" poster="/media/monitor-evals/poster.webp">
    <source src="https://mintcdn.com/ringgai-0da73d20/XUOcNie5hfK3q--Y/media/monitor-evals/video.webm?fit=max&auto=format&n=XUOcNie5hfK3q--Y&q=85&s=3be56729a0c5c57d1ebde751564dc125" type="video/webm; codecs=&#x22;av01.0.09M.08&#x22;" data-path="media/monitor-evals/video.webm" />
  </video>
</Frame>

<Steps>
  <Step title="Open Ananya's evals">
    Open **Monitor → Evals** and pick **EMI reminder - Hindi**. **Metrics** shows an average score per metric for the last 30 days, for the metrics enabled on the agent, such as **Factual accuracy**, **Instruction following**, **Knowledge coverage** and **Tool accuracy**. **Issues** groups the failures, each with **Add as test case**.
  </Step>

  <Step title="Write the test case">
    Click **Create a test case**, choose **Voice**, and on **Write a prompt** fill in:

    * **Name**: `Promise to pay on 15 October`
    * **Conversation path**: `The customer is Rahul Verma. He confirms it is him, listens to the amount, says he will pay on 15 October, then asks whether there is a late fee if he misses that date.`
    * **Success criteria**: `The agent states the due amount and due date, confirms 15 October as the payment date, answers the late-fee question from the EMI policy, and ends the call politely.`

    Click **Add test case**. It appears with the status **Never run**.
  </Step>

  <Step title="Run it">
    Click **Run tests**. The case moves from **Queued** to **Running**, then to **Passed**, **Failed** or **Not evaluated**. Open it for the verdict, a reason per criterion, which version ran, and the simulated conversation. **Not evaluated** means the simulation never reached the situation; make the **Conversation path** more specific and **Run again**.
  </Step>

  <Step title="Create the alert">
    <Frame caption="Creating an alert with email and webhook notifications">
      <video alt="Creating an alert with email and webhook notifications" aria-label="Creating an alert with email and webhook notifications" controls muted playsInline className="w-full rounded-xl" style={{ aspectRatio: "16 / 10" }} preload="metadata" controlsList="nodownload noremoteplayback" poster="/media/monitor-alerts/poster.webp">
        <source src="https://mintcdn.com/ringgai-0da73d20/XUOcNie5hfK3q--Y/media/monitor-alerts/video.webm?fit=max&auto=format&n=XUOcNie5hfK3q--Y&q=85&s=af5f4f2ef8d4a91a9f3715956bc0778c" type="video/webm; codecs=&#x22;av01.0.09M.08&#x22;" data-path="media/monitor-alerts/video.webm" />
      </video>
    </Frame>

    Open **Monitor → Alerts** and click **Create alert**. Set **Alert name** to `EMI reminder call failures` and **Scope** to **Agent**, then pick **EMI reminder - Hindi**. Build the **Trigger**: check every **15 min** for the last **1 hour**, when **Call failure rate** **is above** `20` (rate thresholds are percentages). Under **Notify via**, expand **Email**, type your address and click **Add**. Click **Save**.
  </Step>
</Steps>

## Checkpoint

<Check>
  "Alert created", and on the **Agents** tab of Alerts the rule sits under **EMI reminder - Hindi** with its condition, scope and channel, switch on. In Evals, the case shows **Passed**, or **Failed** with the criteria it missed.
</Check>

## Exercise

On the **Workspace** tab, under **Recommended**, click the pencil icon on **Disconnection rate spike** rather than **Enable**, add your email under **Notify via** and save. Expected result: the alert is on with your address as its recipient, firing when the disconnection rate is above 25% over the last 30 minutes. **Enable** alone adds no recipients.

## What you learned

* Evals scores every completed call; a test case is one conversation path plus success criteria, simulated against the published version.
* **Not evaluated** is neither a pass nor a failure: the simulation never reached the situation under test.
* An alert is a sentence: check every *interval* for the last *window*, when *metric* *condition* *threshold*. It needs at least one email, Slack or webhook target.

<Quiz
  question="You run the Promise to pay test case and it comes back Not evaluated. What does that mean, and what do you do?"
  answers={[
{ text: "It failed; rewrite the prompt", correct: false },
{ text: "The simulated customer never reached the situation under test; make the Conversation path more specific and run again", correct: true },
{ text: "It passed with warnings; nothing to do", correct: false },
{ text: "Evals is not enabled for the agent; write to support", correct: false },
]}
  correctFeedback="Right. Not evaluated is neither a pass nor a failure: the simulation never reached the situation the success criteria describe. A more specific Conversation path steers it there; then Run again."
  incorrectFeedback="Not quite. A failed case shows Failed with the criteria it missed, and a disabled Evals page says so up front. Not evaluated means the simulated conversation never reached the situation under test, which the Conversation path controls."
/>

<RelatedTopics items={[["Evals", "/documentation/monitor/evals"], ["Alerts", "/documentation/monitor/alerts"]]} />


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.