# Let the agent link your docs

How a knowledge document gets a public address the agent may link, and how to set one yourself.

A document without an address is still searched and answered from. The agent never links it.

## Where addresses come from

| Source | Address |
| --- | --- |
| Website | The address the page was scraped from. A re-scrape updates it |
| Notion | The page's public URL, only when the page is shared to the web. Median opens it signed out before it counts. Every pull checks again, so a page taken off the web loses its link |
| GitHub repository | Worked out from each file's path once you set **Published at**, then checked against the live site |
| Docs site | Worked out from the framework's config, then checked against the live site |
| API spec endpoints | None |
| Written, uploaded, learned | None until you add one by hand |

## Set where a repository is published

Set **Published at** when you add a repository, or pick **Published docs** from its row's menu on **Sources** later. A docs site always has one, since **Published at** is required when you add it. See [Connect knowledge sources](/articles/connect-knowledge-sources).

The **Published docs** dialog:

| Control | Does |
| --- | --- |
| **Address** | Where the repository's files are published, for example `docs.example.com`. `https://` is added when missing. A query or anchor is dropped |
| **Save** | Saves the address and starts a check |
| **Check again** | Runs the check again. Shown once a check has started |
| **Stop linking** | Clears the address and every link a check set. Hand-set links stay. Plain repositories only |

A check runs when you save an address, when you press **Check again**, and after every sync that added or changed a file. A sync or **Check again** does not start a second check while one is running. Saving a new address mid-check lets the running check finish, drops its answer about the old address, and checks the new one.

### Row states

Under the sync status, the row shows the address host and the check state, for example `docs.example.com, 142 of 146 linked`.

| State | Means |
| --- | --- |
| waiting to check links | An address is set and no check has finished |
| checking links | A check is running |
| 142 of 146 linked | The last check linked 142 of the documents it looked at. Endpoint documents from an API spec and links you set yourself are not counted |
| link check incomplete | The check failed. Press **Check again** |

The total counts the documents from the repository that can have a page of their own. API endpoint documents never get a link, so they are left out, and so are documents whose link was set or removed by hand.

The dialog can show a note under the result:

| Note | Means |
| --- | --- |
| No sitemap found. Pages were checked individually. | The site has no sitemap Median could find |
| Some pages did not hold what their file says: ... | A sampled page did not match its file, so every page was read one by one |
| N were left unlinked to keep the check quick. | More than 120 pages needed reading |
| No matching pages found. Check the site URL and confirm your docs are published. | Nothing passed |
| Could not check these links. Try again. | The check failed |

| Error | Cause |
| --- | --- |
| Enter a valid documentation URL. | The address is not a web address |
| Use a public URL that customers can open. | The address points at a private or local host |

## What the check does

Every link passes three steps. A document that fails any of them stays unlinked.

| Step | What happens |
| --- | --- |
| Route | Median works out each file's address. For a repository, the synced folder and the extension come off the path, `index`, `readme` and `_index` become their folder, and number prefixes like `01-` are dropped. A docs site uses its framework's routes. [Frontmatter](#frontmatter) overrides both |
| Sitemap | Median looks up the site's sitemap and matches each address against it. Files with no exact match are paired with sitemap pages no other file claimed, using AI |
| Read | Median opens each page as a signed-out visitor, and AI confirms the page holds the same document as the file |

For the read step, Median opens 5 exact sitemap matches spread across the list. If all 5 hold, every exact match is linked. If one does not, every page is read on its own.

A page fails the read when it does not answer, returns an error status, has almost no text, or holds a different page, a login wall or a section index.

Median looks for the sitemap in this order and uses the first one that lists pages on the same site:

1. `Sitemap` lines in the site's `robots.txt`
2. `/sitemap.xml`, `/sitemap_index.xml`, `/sitemap-index.xml` and `/sitemap-0.xml`
3. `sitemap.xml` under the address's own path

Matching ignores case, `www.`, a trailing slash and `.html`.

With no sitemap, Median opens and reads every worked-out address.

| Limit | Value |
| --- | --- |
| Documents per check | 400. The rest are not checked |
| Pages read per check | 120 |
| Pages read at once | 6 |
| Sitemap addresses | 5,000, from up to 12 child sitemaps |

The check fetches sitemaps and pages with the user agent `MedianDocsLinker/1.0 (+https://median.sh)`.

The check uses AI and is billed in credits. Without AI on your plan, or with no credits left, no page passes the read step. See [plans](/articles/manage-your-plan-and-credits#plans).

## Frontmatter

A file can name its own address. Median reads these keys in this order and uses the first one it finds:

1. `canonical`
2. `canonicalUrl`
3. `url`
4. `permalink`
5. `slug`

Keys match in any case. Only top-level values in the frontmatter block count.

| Value | Address |
| --- | --- |
| `https://docs.example.com/billing` | Taken whole. A private or local host is ignored and the file's path is used |
| `/guides/billing` | That route under **Published at**. On a docs site the framework's route prefix goes in front, unless **Published at** already ends with it |
| `billing` | Replaces the last part of the file's own route |

```md title="docs/guides/old-name.mdx"
---
title: Billing
slug: billing
---
```

With the folder `docs` and **Published at** `example.com/docs`, this file's address is `https://example.com/docs/guides/billing`. The sitemap and read steps still have to pass.

## Set a link on one document

| Where | How |
| --- | --- |
| Library row menu | **Add public link** or **Change public link** |
| A document's page | **Add** or **Change** beside **Public link**, in the details column |

The menu items open the **Public link** dialog. Its **Address** field reads "Optional. Without a URL, answers will not include a link to this document." **Remove** appears when the document has a link.

A page link keeps its query and anchor. `https://` is added when missing. A bad address returns "Enter a valid URL." or "Use a public URL that customers can open."

### Which link wins

- A link set by hand always wins. Checks, re-scrapes and Notion pulls never change it.
- Removing a link by hand sticks too. The document stays unlinked, even a scraped page.
- **Stop linking** on a repository keeps hand-set links.

## How the agent uses links

It never writes an address a document did not come with.

| Channel | How a page is cited |
| --- | --- |
| Widget | Each page the reply cites shows as a button under it, up to three a reply. A page already shown in the conversation is not shown again |
| Email, Discord and others | One markdown link at the end of the sentence the page supports, and only when the page says more than the answer |
