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

# Build an investor pitch-deck agent

> Research a company website, collect founder context, and generate an investor-ready pitch-deck draft.

This tutorial builds a small command-line agent that combines public company context with founder-provided facts, then asks Presenton Cloud to create a structured investor pitch-deck draft.

## Business outcome

The agent accelerates the first draft without inventing traction, market size, financials, or fundraising terms. A founder still owns the facts and reviews the narrative before sharing it with investors.

```mermaid theme={null}
flowchart LR
  Website[Read public website] --> Questions[Generate diligence questions]
  Questions --> Founder[Collect founder answers]
  Founder --> Brief[Build verified investment brief]
  Brief --> Presenton[Generate pitch-deck draft]
  Presenton --> Review[Founder and finance review]
```

## Before you start

* Create a Presenton Cloud API key.
* Create an OpenAI API key for question generation.
* Confirm you have permission to read and process the target website.

```bash theme={null}
export PRESENTON_API_KEY=YOUR_PRESENTON_API_KEY
export OPENAI_API_KEY=YOUR_OPENAI_API_KEY
python -m pip install beautifulsoup4 openai requests
```

## Build the agent

Save as `pitch_deck_agent.py`:

```python theme={null}
import json
import os
from pathlib import Path
from urllib.parse import urlparse

import requests
from bs4 import BeautifulSoup
from openai import OpenAI

PRESENTON_URL = "https://api.presenton.ai/api/v3/presentation/generate"
PRESENTON_API_KEY = os.environ["PRESENTON_API_KEY"]
openai_client = OpenAI(api_key=os.environ["OPENAI_API_KEY"])


def normalize_url(value: str) -> str:
    return value if value.startswith(("http://", "https://")) else f"https://{value}"


def fetch_public_company_context(url: str) -> dict:
    url = normalize_url(url)
    response = requests.get(
        url,
        headers={"User-Agent": "Presenton pitch-deck research tutorial"},
        timeout=20,
    )
    response.raise_for_status()
    soup = BeautifulSoup(response.text, "html.parser")

    title = soup.title.get_text(" ", strip=True) if soup.title else ""
    description_node = soup.find("meta", attrs={"name": "description"})
    description = description_node.get("content", "") if description_node else ""
    headings = [node.get_text(" ", strip=True) for node in soup.select("h1, h2")[:12]]
    paragraphs = [node.get_text(" ", strip=True) for node in soup.select("p")[:20]]

    return {
        "url": url,
        "domain": urlparse(url).netloc,
        "title": title,
        "description": description,
        "headings": headings,
        "public_copy": "\n".join(paragraphs)[:8000],
    }


def generate_diligence_questions(company: dict) -> list[str]:
    prompt = f"""
You are preparing an investor pitch-deck discovery interview.

Public company context:
{json.dumps(company, indent=2)}

Return JSON with a `questions` array containing exactly six concise questions.
Cover: customer problem, target buyer, evidence of traction, business model,
go-to-market motion, and fundraising objective. Ask for exact figures and time
periods where relevant. Do not assume public marketing claims are verified.
""".strip()

    response = openai_client.chat.completions.create(
        model="gpt-4o-mini",
        messages=[{"role": "user", "content": prompt}],
        response_format={"type": "json_object"},
        temperature=0.2,
    )
    payload = json.loads(response.choices[0].message.content)
    questions = payload.get("questions", [])
    if len(questions) != 6:
        raise ValueError("Question generator did not return exactly six questions")
    return questions


def interview_founder(questions: list[str]) -> list[dict]:
    answers = []
    print("\nAnswer with verified information. Enter 'not available' when unknown.\n")
    for number, question in enumerate(questions, start=1):
        print(f"{number}. {question}")
        answers.append({"question": question, "answer": input("> ").strip()})
    return answers


def build_investment_brief(company: dict, interview: list[dict]) -> str:
    return f"""
Create an investor pitch-deck draft for {company['title'] or company['domain']}.

Public website context — treat as unverified marketing copy:
{json.dumps(company, indent=2)}

Founder interview — treat these as the supplied source facts:
{json.dumps(interview, indent=2)}

Build an 11-slide narrative:
1. Company, category, and one-sentence value proposition
2. Customer problem and why it is urgent now
3. Product and differentiated workflow
4. Target customer and use cases
5. Market opportunity using only supplied market figures
6. Business model and pricing logic
7. Traction with exact periods and denominators
8. Go-to-market strategy and sales motion
9. Competitive alternatives and defensibility
10. Team and execution advantages
11. Fundraising objective, use of funds, and next milestones

Rules:
- Never invent revenue, growth, customers, market size, margins, runway, or deal terms.
- When a required fact is unavailable, write `Founder input required`.
- Distinguish public claims from founder-verified facts.
- Keep the tone confident, specific, and suitable for an investor meeting.
- Prefer evidence and decisions over generic startup language.
""".strip()


def generate_pitch_deck(brief: str, company_name: str) -> dict:
    response = requests.post(
        PRESENTON_URL,
        headers={
            "Authorization": f"Bearer {PRESENTON_API_KEY}",
            "Content-Type": "application/json",
        },
        json={
            "content": brief,
            "instructions": "Use an investor narrative with clear charts only when verified numeric data exists.",
            "n_slides": 11,
            "language": "English",
            "tone": "sales_pitch",
            "verbosity": "concise",
            "standard_template": "neo-modern",
            "theme": "professional-dark",
            "export_as": "pptx",
        },
        timeout=900,
    )
    response.raise_for_status()
    result = response.json()

    output_name = "".join(
        character if character.isalnum() or character in "-_" else "_"
        for character in company_name
    ).strip("_")
    output_path = Path(f"{output_name or 'company'}_pitch_deck.pptx")
    download = requests.get(
        result["path"],
        headers={"Authorization": f"Bearer {PRESENTON_API_KEY}"},
        timeout=120,
    )
    download.raise_for_status()
    output_path.write_bytes(download.content)
    print(f"Saved {output_path}")
    print(f"Review and edit: {result['edit_path']}")
    return result


def main() -> None:
    website = input("Company website: ").strip()
    company = fetch_public_company_context(website)
    questions = generate_diligence_questions(company)
    interview = interview_founder(questions)
    brief = build_investment_brief(company, interview)
    generate_pitch_deck(brief, company["title"] or company["domain"])


if __name__ == "__main__":
    main()
```

Run it with:

```bash theme={null}
python pitch_deck_agent.py
```

## Review the investor narrative

Before sharing the deck, the founder and finance owner should verify:

* Every traction figure, reporting period, and denominator.
* Market-size methodology and cited sources.
* Pricing, margins, runway, and fundraising terms.
* Customer-logo and quote permissions.
* Competitive claims and confidential information.
* Whether all `Founder input required` placeholders are resolved.

<Warning>
  A generated pitch deck is a draft, not financial or legal diligence. Never allow the agent to fabricate missing metrics merely to complete a slide.
</Warning>

<Card title="Original example project" icon="github" href="https://github.com/presenton/examples/tree/main/create-agent-to-create-pitch-decks">
  Explore the earlier pitch-deck agent and adapt its workflow to your approved research and model stack.
</Card>
