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

# Quickstart

> Build an end-to-end RAG agent in 5 minutes, from document upload to a streaming conversation.

In this guide you will upload a document, create a knowledge base, index the document into it, spin up an agent backed by that knowledge base, and run a streaming conversation, all through the REST API. By the end you will have a RAG agent that answers questions grounded in your own content.

<Note>
  **Prerequisites:**

  * A Powabase project — grab your Project URL and Service Role (Secret) Key from the Connect modal in the Studio (click the Connect button in your project header, or append ?showConnect=true to any project URL). See the Auth & Connection guide for the full walkthrough.
</Note>

<Steps>
  <Step title="Authenticate">
    Set up your base URL and authentication headers. Copy Project URL and Service Role (Secret) Key from the Studio's Connect modal. Every /api/\* request needs the service role key in both the apikey and Authorization headers.

    **Endpoint:** `Headers: apikey + Authorization`

    <CodeGroup>
      ```python Python theme={null}
      import requests

      BASE_URL = "{BASE_URL}"   # Connect modal -> Project URL
      API_KEY = "{API_KEY}"     # Connect modal -> Service Role (Secret) Key

      headers = {
          "apikey": API_KEY,
          "Authorization": f"Bearer {API_KEY}",
          "Content-Type": "application/json",
      }
      ```

      ```typescript TypeScript theme={null}
      const BASE_URL = "{BASE_URL}";  // Connect modal -> Project URL
      const API_KEY = "{API_KEY}";    // Connect modal -> Service Role (Secret) Key

      const headers = {
        apikey: API_KEY,
        Authorization: `Bearer ${API_KEY}`,
        "Content-Type": "application/json",
      };
      ```

      ```bash cURL theme={null}
      # Set these variables for the rest of the guide.
      # Both values come from the Studio's Connect modal:
      #   BASE_URL = Project URL
      #   API_KEY  = Service Role (Secret) Key
      BASE_URL="{BASE_URL}"
      API_KEY="{API_KEY}"
      ```
    </CodeGroup>
  </Step>

  <Step title="Upload a document">
    Upload a file to create a Source. The platform automatically extracts its text content for indexing.

    **Endpoint:** `POST /api/sources/upload`

    <CodeGroup>
      ```python Python theme={null}
      with open("product-docs.pdf", "rb") as f:
          response = requests.post(
              f"{BASE_URL}/api/sources/upload",
              headers={"apikey": API_KEY, "Authorization": f"Bearer {API_KEY}"},
              files={"file": ("product-docs.pdf", f, "application/pdf")},
          )
      source = response.json()
      source_id = source["id"]
      print(f"Source created: {source_id}, status: {source['extraction_status']}")

      # Poll until extraction completes
      import time
      TERMINAL = {"extracted", "attention_required", "failed", "cancelled"}
      while True:
          res = requests.get(f"{BASE_URL}/api/sources/{source_id}", headers=headers)
          status = res.json()["extraction_status"]
          if status in TERMINAL:
              print(f"Extraction ended with status: {status}")
              break
          time.sleep(2)
      ```

      ```typescript TypeScript theme={null}
      const formData = new FormData();
      formData.append("file", fileBlob, "product-docs.pdf");

      const uploadRes = await fetch(`${BASE_URL}/api/sources/upload`, {
        method: "POST",
        headers: { apikey: API_KEY, Authorization: `Bearer ${API_KEY}` },
        body: formData,
      });
      const source = await uploadRes.json();
      const sourceId = source.id;
      console.log("Source created:", sourceId);

      // Poll until extraction reaches a terminal state
      const TERMINAL = new Set(["extracted", "attention_required", "failed", "cancelled"]);
      let status = "pending";
      while (!TERMINAL.has(status)) {
        await new Promise((r) => setTimeout(r, 2000));
        const res = await fetch(`${BASE_URL}/api/sources/${sourceId}`, { headers });
        status = (await res.json()).extraction_status;
      }
      console.log("Extraction ended with status:", status);
      ```

      ```bash cURL theme={null}
      # Upload the document
      curl -X POST '{BASE_URL}/api/sources/upload' \
        -H "apikey: {API_KEY}" \
        -H "Authorization: Bearer {API_KEY}" \
        -F "file=@product-docs.pdf"

      # Poll extraction status (replace {source_id})
      curl '{BASE_URL}/api/sources/{source_id}' \
        -H "apikey: {API_KEY}" \
        -H "Authorization: Bearer {API_KEY}"
      ```
    </CodeGroup>

    **Response:**

    ```json theme={null}
    {
      "id": "source-uuid",
      "name": "product-docs.pdf",
      "file_type": "application/pdf",
      "storage_path": "sources-{org}-{project}/{source_id}/product-docs.pdf",
      "extraction_status": "pending",
      "task_id": "celery-task-uuid"
    }
    ```
  </Step>

  <Step title="Create a knowledge base and index the document">
    Create a knowledge base, then add the source to it. Adding a source triggers chunking and vector indexing automatically.

    **Endpoint:** `POST /api/knowledge-bases`

    <CodeGroup>
      ```python Python theme={null}
      # Create the knowledge base
      response = requests.post(
          f"{BASE_URL}/api/knowledge-bases",
          headers=headers,
          json={
              "name": "Product Docs",
              "description": "Product documentation knowledge base",
          },
      )
      kb = response.json()
      kb_id = kb["id"]
      print(f"Knowledge base created: {kb_id}")

      # Add the source to trigger indexing
      response = requests.post(
          f"{BASE_URL}/api/knowledge-bases/{kb_id}/sources",
          headers=headers,
          json={"source_id": source_id},
      )
      print(f"Source added, indexing started: {response.json()}")
      ```

      ```typescript TypeScript theme={null}
      // Create the knowledge base
      const kbRes = await fetch(`${BASE_URL}/api/knowledge-bases`, {
        method: "POST",
        headers,
        body: JSON.stringify({
          name: "Product Docs",
          description: "Product documentation knowledge base",
        }),
      });
      const kb = await kbRes.json();
      const kbId = kb.id;
      console.log("Knowledge base created:", kbId);

      // Add the source to trigger indexing
      await fetch(`${BASE_URL}/api/knowledge-bases/${kbId}/sources`, {
        method: "POST",
        headers,
        body: JSON.stringify({ source_id: sourceId }),
      });
      console.log("Source added, indexing started");
      ```

      ```bash cURL theme={null}
      # Create the knowledge base
      curl -X POST '{BASE_URL}/api/knowledge-bases' \
        -H "apikey: {API_KEY}" \
        -H "Authorization: Bearer {API_KEY}" \
        -H "Content-Type: application/json" \
        -d '{
          "name": "Product Docs",
          "description": "Product documentation knowledge base"
        }'

      # Add source to trigger indexing (replace {kb_id})
      curl -X POST '{BASE_URL}/api/knowledge-bases/{kb_id}/sources' \
        -H "apikey: {API_KEY}" \
        -H "Authorization: Bearer {API_KEY}" \
        -H "Content-Type: application/json" \
        -d '{"source_id": "{source_id}"}'
      ```
    </CodeGroup>
  </Step>

  <Step title="Create an agent with the knowledge base">
    Create an agent and link the knowledge base to it. The agent automatically gets a search tool for each linked knowledge base.

    **Endpoint:** `POST /api/agents`

    <CodeGroup>
      ```python Python theme={null}
      # Create the agent
      response = requests.post(
          f"{BASE_URL}/api/agents",
          headers=headers,
          json={
              "name": "Docs Assistant",
              "model": "gpt-4o",
              "system_prompt": "You are a helpful assistant. Use the knowledge base to answer questions about our product documentation.",
              "settings": {"temperature": 0.7},
          },
      )
      agent = response.json()
      agent_id = agent["id"]
      print(f"Agent created: {agent_id}")

      # Link the knowledge base
      response = requests.post(
          f"{BASE_URL}/api/agents/{agent_id}/knowledge-bases",
          headers=headers,
          json={"knowledge_base_id": kb_id},
      )
      print(f"Knowledge base linked: {response.json()}")
      ```

      ```typescript TypeScript theme={null}
      // Create the agent
      const agentRes = await fetch(`${BASE_URL}/api/agents`, {
        method: "POST",
        headers,
        body: JSON.stringify({
          name: "Docs Assistant",
          model: "gpt-4o",
          system_prompt: "You are a helpful assistant. Use the knowledge base to answer questions about our product documentation.",
          settings: { temperature: 0.7 },
        }),
      });
      const agent = await agentRes.json();
      const agentId = agent.id;
      console.log("Agent created:", agentId);

      // Link the knowledge base
      await fetch(`${BASE_URL}/api/agents/${agentId}/knowledge-bases`, {
        method: "POST",
        headers,
        body: JSON.stringify({ knowledge_base_id: kbId }),
      });
      console.log("Knowledge base linked");
      ```

      ```bash cURL theme={null}
      # Create the agent
      curl -X POST '{BASE_URL}/api/agents' \
        -H "apikey: {API_KEY}" \
        -H "Authorization: Bearer {API_KEY}" \
        -H "Content-Type: application/json" \
        -d '{
          "name": "Docs Assistant",
          "model": "gpt-4o",
          "system_prompt": "You are a helpful assistant.",
          "settings": {"temperature": 0.7}
        }'

      # Link knowledge base (replace {agent_id})
      curl -X POST '{BASE_URL}/api/agents/{agent_id}/knowledge-bases' \
        -H "apikey: {API_KEY}" \
        -H "Authorization: Bearer {API_KEY}" \
        -H "Content-Type: application/json" \
        -d '{"knowledge_base_id": "{kb_id}"}'
      ```
    </CodeGroup>
  </Step>

  <Step title="Chat with your agent (streaming)">
    Send a message and consume the SSE stream. The agent will search the knowledge base, reason about the results, and stream back an answer.

    **Endpoint:** `POST /api/agents/{id}/run/stream`

    <Note>
      The agent will emit tool\_call and tool\_result events as it searches the knowledge base, followed by chunk events containing the streamed answer.
    </Note>

    <CodeGroup>
      ```python Python theme={null}
      response = requests.post(
          f"{BASE_URL}/api/agents/{agent_id}/run/stream",
          headers=headers,
          json={"message": "How do I get started with the product?"},
          stream=True,
      )

      import json

      session_id = None
      for line in response.iter_lines():
          if not line:
              continue
          text = line.decode("utf-8")
          if text.startswith("data: "):
              event = json.loads(text[6:])
              if event["event"] == "start":
                  session_id = event["session_id"]
              elif event["event"] == "chunk":
                  print(event["content"], end="")
              elif event["event"] == "tool_call":
                  print(f"\n[Searching: {event['tool_name']}]")
              elif event["event"] == "tool_result":
                  print(f"[Results received]")
              elif event["event"] == "complete":
                  print(f"\n\nDone! Session: {session_id}")
      ```

      ```typescript TypeScript theme={null}
      const response = await fetch(`${BASE_URL}/api/agents/${agentId}/run/stream`, {
        method: "POST",
        headers,
        body: JSON.stringify({
          message: "How do I get started with the product?",
        }),
      });

      const reader = response.body!.getReader();
      const decoder = new TextDecoder();
      let sessionId: string | null = null;

      while (true) {
        const { done, value } = await reader.read();
        if (done) break;
        const text = decoder.decode(value);
        for (const line of text.split("\n")) {
          if (line.startsWith("data: ")) {
            const event = JSON.parse(line.slice(6));
            if (event.event === "start") sessionId = event.session_id;
            if (event.event === "chunk") process.stdout.write(event.content);
            if (event.event === "tool_call") console.log(`\n[Searching: ${event.tool_name}]`);
            if (event.event === "tool_result") console.log("[Results received]");
            if (event.event === "complete") console.log(`\nDone! Session: ${sessionId}`);
          }
        }
      }
      ```

      ```bash cURL theme={null}
      curl -N -X POST '{BASE_URL}/api/agents/{agent_id}/run/stream' \
        -H "apikey: {API_KEY}" \
        -H "Authorization: Bearer {API_KEY}" \
        -H "Content-Type: application/json" \
        -d '{"message": "How do I get started with the product?"}'
      ```
    </CodeGroup>
  </Step>
</Steps>

## What's Next

<CardGroup cols={2}>
  <Card title="Agents & Tools" href="/concepts/agents-tools">
    Understand the ReAct loop, tool types, and how agents reason.
  </Card>

  <Card title="Streaming Responses" href="/guides/streaming-guide">
    Deep dive into SSE event handling and multi-turn sessions.
  </Card>

  <Card title="Agents API Reference" href="/api-reference/agents">
    Full endpoint documentation for agents.
  </Card>
</CardGroup>
