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

# Create a knowledge source to ingest

> Connect a documentation website, an XML sitemap, a GitHub repository or uploaded files to a collection. Creating a source stores it; it reads nothing yet.

Creating a source validates and stores its configuration. It does not read anything. To ingest the source, call [Sync a source](/api-reference/knowledge/sync-source).

`configuration` is a flat object. Every value is a string, and lists are comma-separated inside one string, for example `"docs/,README.md"`. Numbers are accepted and converted to strings. Booleans, arrays and nested objects are refused. The configuration is checked for its `source_type` when you create the source, so a malformed URL or repository name returns `400` here, not later during ingestion. The response returns the stored form: keys the source type does not read are dropped, and optional keys you left out are filled in with their defaults.

No credential can be passed in `configuration`. Everything in it is readable by every member of your organization.

Creating a source requires a role that can manage knowledge. A read-only member receives `403`. A `collection_id` that is not in your organization returns `404`. See [Knowledge sources](/knowledge/sources) for guidance on choosing a source type.

<ParamField body="collection_id" type="string" required>
  ID (a UUID) of the collection to ingest into. A malformed ID returns `400`. Syncing a source whose collection is archived is accepted, and the job then ends `failed`.
</ParamField>

<ParamField body="source_type" type="string" required>
  One of `website`, `sitemap`, `github` or `upload`. Any other value returns `400`.
</ParamField>

<ParamField body="name" type="string" required>
  Display name, 1 to 255 characters after trimming.
</ParamField>

<ParamField body="configuration" type="object">
  Settings for the source type. At most 50 keys, and each value at most 8,192 characters. The keys each type reads are listed below. The field itself may be omitted, but every source type has a required setting, so in practice a source created without `configuration` is refused with `400`.

  <Expandable title="website">
    Crawls a documentation site from a seed URL, following links on the same origin (scheme, host and port) only. Redirects are not followed, so a page that redirects is treated as unreadable. URLs ending in image, media, archive, font, stylesheet or script extensions are skipped.

    <ParamField body="seed_url" type="string" required>
      Absolute `http` or `https` URL the crawl starts from, at most 2,048 characters. It must not contain a username or password.
    </ParamField>

    <ParamField body="path_prefix" type="string" default="">
      Crawl only paths at or below this prefix, for example `/docs`. It must start with `/` and must not contain `..`. Matching is by path segment, so `/docs` does not match `/docs-internal`.
    </ParamField>

    <ParamField body="max_depth" type="string" default="3">
      Number of links followed from the seed. An integer from `1` to `10`.
    </ParamField>

    <ParamField body="max_pages" type="string" default="500">
      Maximum pages collected in one run. An integer from `1` to `5000`.
    </ParamField>

    <ParamField body="include_paths" type="string" default="">
      Comma-separated globs matched against the URL path. When set, only matching pages are ingested. At most 100 globs of up to 512 characters each.
    </ParamField>

    <ParamField body="exclude_paths" type="string" default="">
      Comma-separated globs matched against the URL path. Matching pages are skipped, even when they also match `include_paths`. At most 100 globs of up to 512 characters each.
    </ParamField>
  </Expandable>

  <Expandable title="sitemap">
    Reads the URLs listed in a `sitemap.xml` without following links. It takes the same keys as `website`. Listed URLs must still be on the sitemap's origin and pass `path_prefix`, `include_paths` and `exclude_paths`. `max_pages` caps the pages read. `max_depth` is validated but not used. A sitemap index is followed to at most 100 child sitemaps.

    <ParamField body="seed_url" type="string" required>
      Absolute `http` or `https` URL of the sitemap, at most 2,048 characters.
    </ParamField>
  </Expandable>

  <Expandable title="github">
    Reads a GitHub repository at a branch, tag or commit. Nothing secret is stored on the source. The repository is read with the access your deployment is configured with.

    <ParamField body="owner" type="string" required>
      Account or organization name. Letters, digits, `.`, `_` and `-`, starting with a letter or digit, not ending in `.`, at most 100 characters.
    </ParamField>

    <ParamField body="repository" type="string" required>
      Repository name, with the same rules as `owner`.
    </ParamField>

    <ParamField body="ref" type="string" default="">
      Branch, tag or commit SHA: letters, digits, `.`, `_`, `/` and `-`, starting with a letter or digit, at most 255 characters. It must not contain `..` or `//`. Leave it empty to follow the repository's default branch.
    </ParamField>

    <ParamField body="include_paths" type="string" default="">
      Comma-separated globs matched against repository paths. When set, only matching files are ingested. At most 100 globs of up to 512 characters each.
    </ParamField>

    <ParamField body="exclude_paths" type="string" default=".git/,node_modules/,vendor/,dist/,build/,target/,.venv/,**/__pycache__/,**/*.min.js,**/*.lock">
      Comma-separated globs for files to skip. An exclusion wins over an include. A value you send replaces the default list rather than adding to it. At most 100 globs of up to 512 characters each.
    </ParamField>
  </Expandable>

  <Expandable title="upload">
    Ingests files you stored with [Upload files](/api-reference/knowledge/upload-files). Post that response's `configuration` object back unchanged.

    <ParamField body="files" type="string" required>
      The manifest: one `storage_key|filename` entry per file, entries separated by newlines. At most 1,000 files, with no storage key listed twice. Every storage key must be one issued to your organization.
    </ParamField>
  </Expandable>
</ParamField>

In `include_paths` and `exclude_paths`, `*` matches within one path segment, `**` matches across segments, `?` matches one character, and a trailing `/` matches everything below a directory.

## Response

Returns `201` with the new source.

<ResponseField name="id" type="string" required>
  Source ID (a UUID).
</ResponseField>

<ResponseField name="collection_id" type="string" required>
  Collection the source ingests into.
</ResponseField>

<ResponseField name="source_type" type="string" required>
  `website`, `sitemap`, `github` or `upload`.
</ResponseField>

<ResponseField name="name" type="string" required>
  The trimmed name.
</ResponseField>

<ResponseField name="configuration" type="object" required>
  The stored configuration. Every key the source type reads is present, with defaults filled in, and
  every value is a string.
</ResponseField>

<ResponseField name="status" type="string" required>
  `active` for a new source.
</ResponseField>

<ResponseField name="last_source_version" type="string | null" required>
  `null` until the source has been ingested.
</ResponseField>

<ResponseField name="last_ingested_at" type="string | null" required>
  `null` until the source has been ingested.
</ResponseField>

<ResponseField name="created_at" type="string" required>
  When the source was created, ISO 8601 in UTC.
</ResponseField>

<ResponseField name="updated_at" type="string" required>
  When the source was last changed, ISO 8601 in UTC.
</ResponseField>

<RequestExample>
  ```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}}
  curl --request POST "$MITHUNAI_URL/arukz/api/v1/knowledge/sources" \
    --header "Authorization: Bearer $MITHUNAI_API_KEY" \
    --header "Content-Type: application/json" \
    --data '{
      "collection_id": "0b6f2c14-8a3d-4e91-9c77-2f5b1d0a4e88",
      "source_type": "website",
      "name": "Product documentation site",
      "configuration": {
        "seed_url": "https://docs.example.com/",
        "path_prefix": "/guides",
        "max_pages": "1000",
        "exclude_paths": "/guides/archive/"
      }
    }'
  ```

  ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}}
  import os, requests

  response = requests.post(
      f"{os.environ['MITHUNAI_URL']}/arukz/api/v1/knowledge/sources",
      headers={"Authorization": f"Bearer {os.environ['MITHUNAI_API_KEY']}"},
      json={
          "collection_id": "0b6f2c14-8a3d-4e91-9c77-2f5b1d0a4e88",
          "source_type": "website",
          "name": "Product documentation site",
          "configuration": {
              "seed_url": "https://docs.example.com/",
              "path_prefix": "/guides",
              "max_pages": "1000",
              "exclude_paths": "/guides/archive/",
          },
      },
      timeout=60,
  )
  response.raise_for_status()
  print(response.json())
  ```

  ```javascript JavaScript theme={"theme":{"light":"github-light","dark":"github-dark"}}
  const response = await fetch(`${process.env.MITHUNAI_URL}/arukz/api/v1/knowledge/sources`, {
    method: 'POST',
    headers: {
      Authorization: `Bearer ${process.env.MITHUNAI_API_KEY}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      collection_id: '0b6f2c14-8a3d-4e91-9c77-2f5b1d0a4e88',
      source_type: 'website',
      name: 'Product documentation site',
      configuration: {
        seed_url: 'https://docs.example.com/',
        path_prefix: '/guides',
        max_pages: '1000',
        exclude_paths: '/guides/archive/',
      },
    }),
  })
  console.log(await response.json())
  ```
</RequestExample>

<ResponseExample>
  ```json 201 theme={"theme":{"light":"github-light","dark":"github-dark"}}
  {
    "id": "5d1e7a90-3c4b-4f2a-8e61-7b9c0d2f4a13",
    "collection_id": "0b6f2c14-8a3d-4e91-9c77-2f5b1d0a4e88",
    "source_type": "website",
    "name": "Product documentation site",
    "configuration": {
      "seed_url": "https://docs.example.com/",
      "path_prefix": "/guides",
      "max_depth": "3",
      "max_pages": "1000",
      "include_paths": "",
      "exclude_paths": "/guides/archive/"
    },
    "status": "active",
    "last_source_version": null,
    "last_ingested_at": null,
    "created_at": "2026-09-24T10:15:02.418331",
    "updated_at": "2026-09-24T10:15:02.418331"
  }
  ```

  ```json 400 theme={"theme":{"light":"github-light","dark":"github-dark"}}
  { "code": "validation_error", "message": "The source URL is not valid." }
  ```

  ```json 403 theme={"theme":{"light":"github-light","dark":"github-dark"}}
  { "code": "authorization_error", "message": "You do not have permission to perform this action." }
  ```

  ```json 404 theme={"theme":{"light":"github-light","dark":"github-dark"}}
  { "code": "not_found", "message": "The knowledge collection was not found." }
  ```
</ResponseExample>
