> ## 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: How a call flows

> Follow one EMI reminder call from its trigger through the conversation, analysis and events to Logs, and read the call statuses it passes through on the way.

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 1: Getting to know Ringg AI · Lesson 2 of 3 · 10 minutes**

## Objective

Trace a single call from the moment it is placed to the analysis that lands in Logs, and name the status it has at each point.

## The scenario

Rahul Verma's EMI of ₹4,500 is due on 15 October. Three days earlier, Ananya calls him. Everything that happens to that call, from the row that triggers it to the "payment promised" field your team reads afterwards, follows the same eight stages.

## Hands-on

<Steps>
  <Step title="Trigger">
    Something asks Ringg to start a conversation: a test in the dashboard, an API request, a campaign row, a workflow step, a call to an attached number, a visitor in the web widget, or a WhatsApp message. For Rahul, it is a campaign row. In Logs the call reads **Registered**: queued and not placed yet.
  </Step>

  <Step title="Agent and number">
    Ringg loads Ananya. The call goes out from a number in your workspace, which Rahul's phone shows as the caller ID.
  </Step>

  <Step title="Pre-call tools">
    Pre-call tools run before the call connects, so a CRM lookup is ready for the agent's first sentence. Ananya gets one in part 3.
  </Step>

  <Step title="Conversation">
    Rahul picks up and the status becomes **Ongoing**. Ananya follows her prompt, searches her knowledge base when she decides a question needs it, and uses on-call tools when the prompt says to.
  </Step>

  <Step title="Call ends">
    Ringg stores the transcript and, for a voice call, the recording. The status becomes **Completed**: the call connected and ended.
  </Step>

  <Step title="Analysis">
    Ringg's platform analysis extracts a summary, key points, action items and a classification of the call. Your own Advanced Analysis fields are extracted too; for Acme, whether Rahul promised to pay and on which date.
  </Step>

  <Step title="Post-call tools and events">
    Post-call tools push results to your systems. Event Subscription sends each event to your server as it happens, from `call_started` to `all_processing_completed`; the capstone wires this into Acme's CRM.
  </Step>

  <Step title="Logs and Analytics">
    The call shows in **Monitor → Logs** with its transcript and analysis, and rolls up into **Monitor → Analytics**.
  </Step>
</Steps>

Not every call takes that path. When an attempt does not connect and another is scheduled, the status is **Retry**. **Failed** means no conversation took place and nothing is left to try, for example when the retries ran out; **Error** means a system-side failure stopped it; **Cancelled** means it was stopped before it completed. **Waiting for limit** holds a call because the callee reached a call limit. The full list is in [Call statuses](/documentation/monitor/logs#call-statuses).

## Checkpoint

<Check>
  For Rahul's call you can say which stage produces the transcript (call ends), which fills "payment promised" (analysis), and which status Logs shows while Ananya is talking (**Ongoing**).
</Check>

## Exercise

Trace the call again, but this time Rahul does not pick up and the campaign allows another attempt. Expected result: the call stops before the Conversation stage and its status in Logs reads **Retry**; hovering it shows the next attempt time, and the retry count comes from the campaign.

## What you learned

* A call passes through eight stages, from trigger to Logs and Analytics.
* Pre-call tools run before the call connects; analysis, post-call tools and events run after it ends.
* The status in Logs tells you where a call is: **Registered**, **Ongoing**, then **Completed**, or **Retry** when it did not connect.

<Quiz
  question="Ananya is mid-conversation with Rahul and the collections lead opens Logs. Which status does the call show, and when will the payment_promised field be filled?"
  answers={[
{ text: "Registered; the field fills as soon as the call connects", correct: false },
{ text: "Ongoing; the field fills after the call ends, when analysis runs", correct: true },
{ text: "Completed; the field fills during the call as Ananya hears it", correct: false },
{ text: "Retry; the field fills once the next attempt connects", correct: false },
]}
  correctFeedback="Right. A connected call is Ongoing. The transcript is stored when the call ends, and only then does analysis extract the summary and your Advanced Analysis fields, so the promise appears after the call, not during it."
  incorrectFeedback="Not quite. Registered is a queued call, Completed is a finished one, and Retry is an attempt that did not connect. While Ananya talks the call is Ongoing, and Advanced Analysis fields are filled after it ends."
/>

<RelatedTopics items={[["How Ringg works", "/get-started/how-ringg-works"], ["Call statuses", "/documentation/monitor/logs#call-statuses"]]} />


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