> ## 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.

# Upload Your First Document

> Upload a file (PDF, DOCX, images, etc.) and extract its content. Extracted text becomes available for knowledge base indexing.

Uploading a document creates a Source, the platform's representation of your file. After upload, an asynchronous extraction pipeline converts the file into structured page texts. You'll poll for status and then retrieve the extracted content.

<Note>
  **Prerequisites:**

  * Authentication configured (see Authentication guide)
</Note>

<Steps>
  <Step title="Upload a file">
    Send a multipart form-data request with your file. The server starts extraction automatically and returns the source metadata.

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

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

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

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

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

    **Response:**

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

  <Step title="Check extraction status">
    Poll the source until extraction\_status reaches a terminal state: extracted (success), attention\_required (partial: some pages failed but the source is still indexable), failed, or cancelled. Small documents typically take a few seconds.

    **Endpoint:** `GET /api/sources/{id}`

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

      TERMINAL = {"extracted", "attention_required", "failed", "cancelled"}
      while True:
          response = requests.get(
              f"{BASE_URL}/api/sources/{source_id}",
              headers=headers,
          )
          body = response.json()
          status = body["extraction_status"]
          if status == "extracted":
              print("Extraction complete!")
              break
          elif status == "attention_required":
              print("Extraction partial:", body.get("error_message"))
              break
          elif status in ("failed", "cancelled"):
              print(f"Extraction {status}:", body.get("error_message"))
              break
          time.sleep(2)
      ```

      ```typescript TypeScript theme={null}
      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 });
        const data = await res.json();
        status = data.extraction_status;
      }
      console.log("Extraction ended with status:", status);
      ```

      ```bash cURL theme={null}
      curl '{BASE_URL}/api/sources/{source_id}' \
        -H "apikey: {API_KEY}" \
        -H "Authorization: Bearer {API_KEY}"
      ```
    </CodeGroup>
  </Step>

  <Step title="View extracted content">
    Retrieve the extracted text. `GET /page-texts` returns `{ "page_texts": [string, ...], "count": N }`, where `page_texts` is an array of strings, one per page, in order. To fetch a single page, pass `?page=N`, which returns `{ "text": string, "page": N, "count": N }`.

    **Endpoint:** `GET /api/sources/{id}/page-texts`

    <CodeGroup>
      ```python Python theme={null}
      response = requests.get(
          f"{BASE_URL}/api/sources/{source_id}/page-texts",
          headers=headers,
      )
      body = response.json()
      for i, text in enumerate(body["page_texts"], start=1):
          print(f"Page {i}: {text[:100]}...")
      ```

      ```typescript TypeScript theme={null}
      const res = await fetch(
        `${BASE_URL}/api/sources/${sourceId}/page-texts`,
        { headers },
      );
      const body = await res.json();
      body.page_texts.forEach((text: string, i: number) =>
        console.log(`Page ${i + 1}: ${text.slice(0, 100)}...`),
      );
      ```

      ```bash cURL theme={null}
      curl '{BASE_URL}/api/sources/{source_id}/page-texts' \
        -H "apikey: {API_KEY}" \
        -H "Authorization: Bearer {API_KEY}"
      ```
    </CodeGroup>
  </Step>
</Steps>

## What's Next

<CardGroup cols={2}>
  <Card title="Create a Knowledge Base" href="/guides/create-knowledge-base">
    Index your extracted content for semantic search.
  </Card>

  <Card title="Sources & Extraction" href="/concepts/sources-extraction">
    Understand the extraction pipeline in depth.
  </Card>

  <Card title="Sources API Reference" href="/api-reference/sources">
    Full endpoint documentation.
  </Card>
</CardGroup>
