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

# Pipeline & opportunities

> Learn how to use the pipeline and opportunities system in the HoopAI Platform to track leads, manage your sales process, and close more deals.

export const CourseTracker = ({courseId, lessonId, videoId}) => {
  const [progress, setProgress] = useState({});
  const [expanded, setExpanded] = useState(false);
  useEffect(() => {
    setProgress(loadProgress());
    document.body.setAttribute("data-academy", "true");
    return () => document.body.removeAttribute("data-academy");
  }, []);
  const course = COURSES[courseId];
  if (!course) return null;
  const lessons = course.lessons;
  const currentIndex = lessons.findIndex(l => l.id === lessonId);
  const completedCount = lessons.filter(l => !!progress[courseId + "/" + l.id]).length;
  const percent = lessons.length > 0 ? Math.round(completedCount / lessons.length * 100) : 0;
  const isCurrentDone = !!progress[courseId + "/" + lessonId];
  const prev = currentIndex > 0 ? lessons[currentIndex - 1] : null;
  const next = currentIndex < lessons.length - 1 ? lessons[currentIndex + 1] : null;
  const totalDuration = formatTotalDuration(lessons);
  const handleToggle = () => {
    const key = courseId + "/" + lessonId;
    const updated = {
      ...progress
    };
    if (updated[key]) {
      delete updated[key];
    } else {
      updated[key] = Date.now();
    }
    setProgress(updated);
    persistProgress(updated);
  };
  const lessonPath = lesson => "/academy/" + course.dir + "/" + lesson.id;
  const videoEmbed = videoId ? <div style={{
    position: "relative",
    width: "100%",
    paddingBottom: "56.25%",
    borderRadius: "var(--ds-radius-lg)",
    overflow: "hidden",
    marginBottom: "24px",
    background: "#000"
  }}>
      <iframe src={"https://www.youtube-nocookie.com/embed/" + videoId + "?rel=0"} title="Lesson video" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowFullScreen style={{
    position: "absolute",
    top: 0,
    left: 0,
    width: "100%",
    height: "100%",
    border: "none"
  }} />
    </div> : null;
  const lessonList = lessons.map(lesson => {
    const isActive = lesson.id === lessonId;
    const isDone = !!progress[courseId + "/" + lesson.id];
    const itemStyle = {
      display: "flex",
      alignItems: "center",
      gap: "8px",
      padding: "10px 16px",
      borderTop: "1px solid var(--academy-border-subtle, #f3f4f6)",
      background: isActive ? "var(--academy-active-bg, #f9fafb)" : "transparent",
      textDecoration: "none",
      cursor: isActive ? "default" : "pointer",
      color: "inherit",
      transition: "background 0.15s"
    };
    const titleStyle = {
      flex: 1,
      fontSize: "13px",
      fontWeight: isActive ? 600 : 400,
      color: isActive ? "var(--academy-text, #111827)" : "var(--academy-text-secondary, #6b7280)",
      overflow: "hidden",
      textOverflow: "ellipsis",
      whiteSpace: "nowrap",
      lineHeight: "1.4"
    };
    const durationStyle = {
      fontSize: "12px",
      color: "var(--academy-text-muted, #9ca3af)",
      whiteSpace: "nowrap"
    };
    const inner = <>
        {isDone ? <CheckCircle /> : <VideoIcon />}
        <span style={titleStyle}>{lesson.title}</span>
        <span style={durationStyle}>{lesson.duration}</span>
      </>;
    if (isActive) {
      return <div key={lesson.id} style={itemStyle}>
          {inner}
        </div>;
    }
    return <a key={lesson.id} href={lessonPath(lesson)} style={itemStyle}>
        {inner}
      </a>;
  });
  return <div className="not-prose" id="academy-course-tracker">
      {videoEmbed}
      <div className="academy-tracker" style={{
    fontFamily: "Inter, -apple-system, BlinkMacSystemFont, sans-serif",
    borderRadius: "var(--ds-radius-lg)",
    border: "1px solid var(--academy-border, #e5e7eb)",
    background: "var(--academy-bg, #ffffff)",
    overflow: "hidden",
    marginBottom: "24px"
  }}>
        {}
        <div style={{
    padding: "16px 16px 0"
  }}>
          <h3 style={{
    margin: 0,
    fontSize: "16px",
    fontWeight: 600,
    lineHeight: "1.4",
    color: "var(--academy-text, #111827)"
  }}>
            {course.title}
          </h3>
          <div style={{
    display: "flex",
    gap: "12px",
    marginTop: "8px"
  }}>
            <span style={{
    display: "flex",
    alignItems: "center",
    gap: "5px",
    fontSize: "12px",
    color: "var(--academy-text-secondary, #6b7280)",
    padding: "3px 8px 3px 6px",
    borderRadius: "var(--ds-radius-sm)",
    border: "1px solid var(--academy-border, #e5e7eb)"
  }}>
              <BookIcon /> {lessons.length} lessons
            </span>
            <span style={{
    display: "flex",
    alignItems: "center",
    gap: "5px",
    fontSize: "12px",
    color: "var(--academy-text-secondary, #6b7280)",
    padding: "3px 8px 3px 6px",
    borderRadius: "var(--ds-radius-sm)",
    border: "1px solid var(--academy-border, #e5e7eb)"
  }}>
              <ClockIcon /> Approx. {totalDuration}
            </span>
          </div>
        </div>

        {}
        <div style={{
    padding: "14px 16px 16px"
  }}>
          <div style={{
    display: "flex",
    justifyContent: "space-between",
    fontSize: "12px",
    color: "var(--academy-text-secondary, #6b7280)"
  }}>
            <span>Your progress</span>
            <span style={{
    fontWeight: 500,
    color: "var(--academy-text, #111827)"
  }}>
              {percent}%
            </span>
          </div>
          <div style={{
    height: "6px",
    borderRadius: "var(--ds-radius-full)",
    background: "var(--academy-track, #f3f4f6)",
    marginTop: "8px",
    overflow: "hidden"
  }}>
            <div style={{
    height: "100%",
    borderRadius: "var(--ds-radius-full)",
    background: "var(--academy-progress, #3b82f6)",
    transition: "width 0.5s cubic-bezier(0.4, 0, 0.2, 1)",
    width: percent + "%"
  }} />
          </div>
        </div>

        {}
        <div className="academy-lessons-desktop">{lessonList}</div>

        {}
        <div className="academy-lessons-mobile">
          <button onClick={() => setExpanded(!expanded)} aria-expanded={expanded} style={{
    display: "flex",
    alignItems: "center",
    justifyContent: "space-between",
    padding: "12px 16px",
    cursor: "pointer",
    borderTop: "1px solid var(--academy-border-subtle, #f3f4f6)",
    background: "transparent",
    border: "none",
    borderTopStyle: "solid",
    borderTopWidth: "1px",
    borderTopColor: "var(--academy-border-subtle, #f3f4f6)",
    width: "100%",
    textAlign: "left",
    color: "var(--academy-text-secondary, #6b7280)",
    fontSize: "13px",
    fontWeight: 500,
    fontFamily: "inherit"
  }}>
            <span>
              {expanded ? "Hide lessons" : "Show all " + lessons.length + " lessons"}
            </span>
            <ChevronDown open={expanded} />
          </button>
          {expanded && lessonList}
        </div>

        {}
        <div style={{
    display: "flex",
    alignItems: "center",
    justifyContent: "space-between",
    borderTop: "1px solid var(--academy-border, #e5e7eb)",
    padding: "10px 16px"
  }}>
          {prev ? <a href={lessonPath(prev)} style={{
    fontSize: "13px",
    color: "var(--academy-text-secondary, #6b7280)",
    textDecoration: "none",
    display: "flex",
    alignItems: "center",
    gap: "4px"
  }}>
              ‹ Previous
            </a> : <div />}
          <button onClick={handleToggle} style={{
    fontSize: "13px",
    fontWeight: 500,
    cursor: "pointer",
    padding: "6px 14px",
    borderRadius: "var(--ds-radius-md)",
    border: isCurrentDone ? "1px solid var(--academy-border, #e5e7eb)" : "none",
    background: isCurrentDone ? "var(--academy-bg, #ffffff)" : "var(--academy-btn-bg, #111827)",
    color: isCurrentDone ? "var(--academy-complete, #10b981)" : "var(--academy-btn-text, #ffffff)",
    display: "flex",
    alignItems: "center",
    gap: "6px",
    transition: "all 0.2s",
    lineHeight: "1.4",
    fontFamily: "inherit"
  }}>
            {isCurrentDone ? <>
                <CheckCircle /> Completed
              </> : "Mark as completed"}
          </button>
          {next ? <a href={lessonPath(next)} style={{
    fontSize: "13px",
    color: "var(--academy-text-secondary, #6b7280)",
    textDecoration: "none",
    display: "flex",
    alignItems: "center",
    gap: "4px"
  }}>
              Next ›
            </a> : <div />}
        </div>
      </div>
    </div>;
};

<CourseTracker courseId="quick-start" lessonId="pipeline-opportunities" />

The pipeline is your visual sales board — a Kanban-style view where every active lead sits in a stage that reflects where they are in your sales process. When you move a contact from "New Lead" to "Proposal Sent" to "Closed Won," you always know exactly where your revenue is coming from and what action each deal needs next.

<Frame caption="The Pipeline and Opportunities lesson in the HoopAI Academy Quick Start">
  <img src="https://mintcdn.com/hoopai-84ec0cdc/gyU98wGhFXcTdqFf/images/academy-quick-start-conversations.png?fit=max&auto=format&n=gyU98wGhFXcTdqFf&q=85&s=b35ece2a2ead2ac8c92820a62c044b70" alt="Pipeline and opportunities overview" width="1536" height="826" data-path="images/academy-quick-start-conversations.png" />
</Frame>

## Why this matters

Most businesses lose leads not because they were a bad fit — but because nothing happened next. A well-maintained pipeline eliminates this problem. Every lead has a stage, every stage has a next action, and your pipeline view shows you — at a glance — exactly which deals need attention right now. The pipeline is the bridge between your lead generation and your revenue.

<Note>
  Before building your pipeline, define your sales stages by mapping out the exact steps a lead takes from first contact to closed deal. Having 5–7 clear stages makes your pipeline actionable and easy to maintain.
</Note>

## How to set up and use your pipeline

<Steps>
  <Step title="Navigate to Opportunities">
    Go to **CRM** > **Opportunities** (or **Pipelines**) in the left sidebar. This is your pipeline board — a visual view of all active deals organized by stage.

    <Frame caption="The Opportunities pipeline board in the HoopAI Platform">
      <img src="https://mintcdn.com/hoopai-84ec0cdc/gyU98wGhFXcTdqFf/images/academy-quick-start-conversations.png?fit=max&auto=format&n=gyU98wGhFXcTdqFf&q=85&s=b35ece2a2ead2ac8c92820a62c044b70" alt="Opportunities pipeline board" width="1536" height="826" data-path="images/academy-quick-start-conversations.png" />
    </Frame>
  </Step>

  <Step title="Create or configure your pipeline">
    Click **Settings** > **Pipelines** (or the gear icon on the pipeline board) to manage your pipeline stages.

    * **Create a new pipeline** if this is your first time — name it based on the business process it represents (e.g., "Sales Pipeline" or "Client Onboarding")
    * **Add stages** by clicking **Add Stage** — give each stage a clear name that represents where the lead is in your process
    * **Set stage colors** to differentiate stages visually at a glance

    Common sales pipeline stages:

    * New Lead
    * Contacted
    * Qualified
    * Proposal Sent
    * Negotiation
    * Closed Won
    * Closed Lost

    <Tip>
      Keep your pipeline stages to 5–7 maximum. Too many stages create friction and make it hard to keep the pipeline updated. Each stage should represent a clear, distinct milestone in your process.
    </Tip>
  </Step>

  <Step title="Add an opportunity">
    From the pipeline board, click **Add Opportunity** (the + button) in any stage. Fill in the opportunity details:

    * **Contact:** Select the existing contact this opportunity is for — or create a new one
    * **Pipeline:** Choose which pipeline this opportunity belongs to
    * **Stage:** Select the current stage
    * **Opportunity name:** A short description of the deal (e.g., "Website Build — Smith Co.")
    * **Value:** The estimated or actual dollar value of the deal
    * **Close date:** Your target close date for this opportunity
    * **Assigned to:** The team member responsible for this deal

    <Frame caption="Adding a new opportunity to your pipeline with contact, stage, value, and close date">
      <img src="https://mintcdn.com/hoopai-84ec0cdc/gyU98wGhFXcTdqFf/images/academy-quick-start-conversations.png?fit=max&auto=format&n=gyU98wGhFXcTdqFf&q=85&s=b35ece2a2ead2ac8c92820a62c044b70" alt="Add opportunity form" width="1536" height="826" data-path="images/academy-quick-start-conversations.png" />
    </Frame>
  </Step>

  <Step title="Move opportunities through stages">
    Drag and drop opportunity cards between columns to move them through stages as deals progress. Alternatively, open an opportunity and change the stage from within the record.

    Every time you move an opportunity, the platform records the stage change — giving you a complete history of how the deal progressed over time.
  </Step>

  <Step title="Update opportunity status">
    Mark opportunities with a status when they reach a final outcome:

    * **Won:** The deal closed and the client converted
    * **Lost:** The deal did not close — record a lost reason for analysis
    * **Abandoned:** The contact became unresponsive

    Marking outcomes accurately gives you reliable data on your close rate, average deal value, and pipeline velocity.

    <Frame caption="Updating an opportunity status to Won, Lost, or Abandoned in the pipeline">
      <img src="https://mintcdn.com/hoopai-84ec0cdc/gyU98wGhFXcTdqFf/images/academy-quick-start-conversations.png?fit=max&auto=format&n=gyU98wGhFXcTdqFf&q=85&s=b35ece2a2ead2ac8c92820a62c044b70" alt="Opportunity status update" width="1536" height="826" data-path="images/academy-quick-start-conversations.png" />
    </Frame>
  </Step>

  <Step title="Use automations to move pipeline stages">
    Connect your pipeline to workflows for automatic stage updates. For example:

    * When a form is submitted, create a new opportunity and place it in the "New Lead" stage
    * When an appointment is booked, move the opportunity to "Qualified"
    * When a payment is received, move the opportunity to "Closed Won" automatically

    Go to **Automation** > **Workflows** and use the **Create Opportunity** or **Update Opportunity Status** action to build this automation.

    <Tip>
      Automating pipeline updates keeps your pipeline accurate without relying on manual entry. The more your pipeline updates automatically, the more useful it becomes as a real-time picture of your business.
    </Tip>
  </Step>

  <Step title="View pipeline analytics">
    From the pipeline view, switch to the **Statistics** or **Analytics** tab to see:

    * Total pipeline value by stage
    * Number of opportunities per stage
    * Average time in each stage
    * Win rate and close rate over a selected date range

    Use this data to identify your pipeline's bottlenecks — the stages where deals stall the longest — and focus your process improvement there.

    <Frame caption="Pipeline analytics showing total value, stage counts, and win rate metrics">
      <img src="https://mintcdn.com/hoopai-84ec0cdc/gyU98wGhFXcTdqFf/images/academy-quick-start-conversations.png?fit=max&auto=format&n=gyU98wGhFXcTdqFf&q=85&s=b35ece2a2ead2ac8c92820a62c044b70" alt="Pipeline analytics dashboard" width="1536" height="826" data-path="images/academy-quick-start-conversations.png" />
    </Frame>
  </Step>
</Steps>

## Key points

<AccordionGroup>
  <Accordion title="Pipeline vs. contacts — what is the difference?">
    A **contact** is a person or business in your CRM. An **opportunity** is a potential deal associated with a contact. One contact can have multiple opportunities — for example, a returning client may have an opportunity for each new project or service they consider. Pipelines organize opportunities (deals), not contacts directly.
  </Accordion>

  <Accordion title="Using multiple pipelines">
    You can create multiple pipelines for different business processes. For example, a sales pipeline for new client acquisition, a separate onboarding pipeline for clients who have already paid, and a referral pipeline for partner leads. Each pipeline has its own set of stages tailored to that specific process.
  </Accordion>

  <Accordion title="Automating opportunity creation">
    Every time a lead submits a form, books a discovery call, or texts a keyword, you can automatically create a new opportunity in your pipeline using a workflow action. This ensures no lead falls through the cracks — every new contact becomes a tracked deal from the moment they enter your system.
  </Accordion>

  <Accordion title="Pipeline value and forecasting">
    The total value of all open opportunities in your pipeline is your pipeline value — a forward-looking estimate of potential revenue. Tracking this number weekly helps you forecast income and identify when you need to generate more leads before revenue drops.
  </Accordion>
</AccordionGroup>

## Key benefits

<AccordionGroup>
  <Accordion title="Complete visibility">
    See every active deal in one view — no leads lost in spreadsheets or missed in your inbox.
  </Accordion>

  <Accordion title="Prioritized follow-up">
    Stages tell you exactly what needs to happen next for each deal — so you always know where to focus.
  </Accordion>

  <Accordion title="Revenue forecasting">
    Pipeline value gives you a data-driven view of expected revenue before it lands in your account.
  </Accordion>

  <Accordion title="Automation integration">
    Workflows move deals through stages automatically, keeping your pipeline accurate without manual updates.
  </Accordion>

  <Accordion title="Performance tracking">
    Win rate, close rate, and stage velocity data help you continuously improve your sales process.
  </Accordion>
</AccordionGroup>
