> ## 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: Plan the system

> List what Acme Lending's EMI reminder system must do, map each part to a checkpoint you can verify in the dashboard, and tick what parts 2 to 6 left in place.

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 7: Capstone: EMI reminders end to end · Lesson 1 of 4 · 10 minutes**

## Objective

Say what the finished system does, name its six checkpoints, and know which already exist in your workspace.

## The scenario

Acme Lending wants the reminder system live for the 15 October EMI run. Ananya has to call each customer in English or Hindi, answer questions about late fees, know the loan's dues before she dials, record whether the customer promised to pay and when, hand that to the CRM, respect a limit on how often one person is called, and leave the collections lead a report. Parts 2 to 6 built those pieces on the same agent. The capstone checks each on screen, adds the CRM webhook and the call limit, then runs the campaign.

## Hands-on

<Steps>
  <Step title="Know the checkpoints">
    A checkpoint is done when its proof is on screen.

    | # | Checkpoint | You are done when | Lesson |
    | - | - | - | - |
    | 1 | Agent with knowledge | **Knowledge Base** on `EMI reminder - Hindi` lists the EMI policy knowledge base, and the prompt mentions it | 2 |
    | 2 | CRM lookup | `crm_lookup` is on the **Pre-call** tab, **Test API** returns the CRM's reply, and the first message reads the dues from it | 2 |
    | 3 | A number to call from | A test call from one of **Your numbers** reaches your phone and appears in Logs | 2 |
    | 4 | Webhook to the CRM | A subscription card shows your URL and `all_processing_completed`, and your server logs one request per finished call | 3 |
    | 5 | Call limit | **Limit how often one person is called** is on and saved | 4 |
    | 6 | Campaign and report | The campaign is **Completed**, Analytics shows **Goal achieved** for it, and the report is exported | 4 |
  </Step>

  <Step title="Gather what you need">
    From earlier parts: the agent with its variables, the EMI policy knowledge base, the `crm_lookup` tool and `payment_promised` marked as the goal (part 3); a number and the 3-row calling list of your own numbers (part 4).

    New here: an HTTPS endpoint on your server that can receive a POST (lesson 3 says what it must do), a phone you can answer, and balance for a short campaign.
  </Step>

  <Step title="Tick what exists">
    Open `EMI reminder - Hindi` on the **Agents** page, your number on the Numbers page and the part 4 campaign on the **Campaigns** page, and write "exists" or "to do" against each checkpoint. Checkpoints 4 to 6 are "to do" for everyone; 1 to 3 depend on how far you took parts 3 and 4.
  </Step>
</Steps>

## Checkpoint

<Check>
  The six checkpoints are written down with "exists" or "to do" against each, and you can open the agent, the number and the old campaign from memory.
</Check>

## Exercise

Write down what Acme's CRM needs per call to record a promise to pay. Expected result: `loan_account`, the three Advanced Analysis fields (`payment_promised`, `promised_date`, `objection`) and a reference to the call. Lesson 3 shows where each sits in the webhook request.

## What you learned

* The system is six checkpoints across one agent, one number, one subscription, one limit and one campaign; the capstone adds wiring, not features.
* A checkpoint counts only when its proof is on screen: the attached list, the tool's response, the Logs entry, the subscription card.
* Checkpoints 4 to 6 are new for everyone; 1 to 3 depend on how far you took parts 3 and 4.

<Quiz
  question="Which checkpoints are new work for everyone in the capstone, whatever they did in parts 3 and 4?"
  answers={[
{ text: "1 to 3: the agent, the CRM lookup and the number", correct: false },
{ text: "Only 6: the campaign and the report", correct: false },
{ text: "4 to 6: the CRM webhook, the call limit, and the campaign with its report", correct: true },
{ text: "None; the capstone only verifies earlier work", correct: false },
]}
  correctFeedback="Right. Parts 2 to 6 built the agent, its knowledge, the CRM tool and the number. The capstone adds the Event Subscription into the CRM and the call limit, then runs the real campaign and reads the report."
  incorrectFeedback="Not quite. Checkpoints 1 to 3 may already exist from parts 3 and 4. The webhook, the call limit and the production campaign with its report are what the capstone adds for everyone."
/>

<RelatedTopics items={[["All lessons", "/resources/learn/all-lessons"], ["Making your agent smarter", "/resources/learn/making-your-agent-smarter/variables"]]} />


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