# Knowledge

> Keep what your agents answer from in step with your own documents.

A knowledge base is what an agent answers from when a question is about your business: your files,
the pages of your website, and topics written in the console. Bases are made and attached to agents
in the console; through the API you keep their files up to date and have their websites read again.

Reading needs `knowledge:read`. Changing needs `knowledge:write` and a **live key**: agents answer
real callers from these files. A knowledge base belongs to its whole project, so a key limited to
some agents cannot reach one.

## Find the base

**curl**

```sh
#!/bin/sh
# List knowledge bases
curl -sS --fail-with-body -X GET "https://api.aigently.ai/v1/knowledge-bases?project_id=0b1c2d3e-4f5a-6b7c-8d9e-0f1a2b3c4d5e" \
  -H "Authorization: Bearer $AIGENTLY_API_KEY"
```

**JavaScript**

```js
// List knowledge bases
const response = await fetch("https://api.aigently.ai/v1/knowledge-bases?project_id=0b1c2d3e-4f5a-6b7c-8d9e-0f1a2b3c4d5e", {
  method: "GET",
  headers: {
    Authorization: `Bearer ${process.env.AIGENTLY_API_KEY}`,
  },
});
const answer = await response.json();
if (!response.ok) throw new Error(`${answer.code}: ${answer.detail}`);
console.log(answer);
```

**Python**

```python
# List knowledge bases
# pip install httpx
import os

import httpx

response = httpx.get(
    "https://api.aigently.ai/v1/knowledge-bases",
    headers={
        "Authorization": f"Bearer {os.environ['AIGENTLY_API_KEY']}",
    },
    params={
        "project_id": "0b1c2d3e-4f5a-6b7c-8d9e-0f1a2b3c4d5e",
    },
)
if response.is_error:
    raise SystemExit(response.text)
print(response.json())
```

**Go**

```go
// List knowledge bases
package main

import (
	"fmt"
	"io"
	"net/http"
	"os"
)

func main() {
	request, err := http.NewRequest("GET", "https://api.aigently.ai/v1/knowledge-bases?project_id=0b1c2d3e-4f5a-6b7c-8d9e-0f1a2b3c4d5e", nil)
	if err != nil {
		panic(err)
	}
	request.Header.Set("Authorization", "Bearer "+os.Getenv("AIGENTLY_API_KEY"))
	response, err := http.DefaultClient.Do(request)
	if err != nil {
		panic(err)
	}
	defer response.Body.Close()
	body, _ := io.ReadAll(response.Body)
	if response.StatusCode >= 400 {
		fmt.Fprintln(os.Stderr, string(body))
		os.Exit(1)
	}
	fmt.Println(string(body))
}
```

**Java**

```java
// List knowledge bases
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

public class Main {
    public static void main(String[] args) throws Exception {
        HttpRequest request = HttpRequest.newBuilder(URI.create("https://api.aigently.ai/v1/knowledge-bases?project_id=0b1c2d3e-4f5a-6b7c-8d9e-0f1a2b3c4d5e"))
            .header("Authorization", "Bearer " + System.getenv("AIGENTLY_API_KEY"))
            .method("GET", HttpRequest.BodyPublishers.noBody())
            .build();
        // HTTP/1.1: on a plain-http address Java's default asks to upgrade, which not every
        // server allows.
        HttpClient client = HttpClient.newBuilder().version(HttpClient.Version.HTTP_1_1).build();
        HttpResponse<String> response =
            client.send(request, HttpResponse.BodyHandlers.ofString());
        if (response.statusCode() >= 400) {
            System.err.println(response.body());
            System.exit(1);
        }
        System.out.println(response.body());
    }
}
```

**C#**

```csharp
// List knowledge bases
using System.Net.Http.Headers;
using System.Text;

using var client = new HttpClient();
client.DefaultRequestHeaders.Authorization =
    new AuthenticationHeaderValue("Bearer", Environment.GetEnvironmentVariable("AIGENTLY_API_KEY"));
var request = new HttpRequestMessage(HttpMethod.Get, "https://api.aigently.ai/v1/knowledge-bases?project_id=0b1c2d3e-4f5a-6b7c-8d9e-0f1a2b3c4d5e")
{
};
var response = await client.SendAsync(request);
var body = await response.Content.ReadAsStringAsync();
if (!response.IsSuccessStatusCode)
{
    Console.Error.WriteLine(body);
    return 1;
}
Console.WriteLine(body);
return 0;
```

**PHP**

```php
<?php
// List knowledge bases
$curl = curl_init("https://api.aigently.ai/v1/knowledge-bases?project_id=0b1c2d3e-4f5a-6b7c-8d9e-0f1a2b3c4d5e");
curl_setopt_array($curl, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        "Authorization: Bearer " . getenv("AIGENTLY_API_KEY"),
    ],
]);
$body = curl_exec($curl);
if (curl_getinfo($curl, CURLINFO_RESPONSE_CODE) >= 400) {
    fwrite(STDERR, $body . "\n");
    exit(1);
}
echo $body, "\n";
```

## Add a file

**curl**

```sh
#!/bin/sh
# Add a file to a knowledge base. With a live key. It is indexed in the background: read it until it says indexed.
curl -sS --fail-with-body -X POST "https://api.aigently.ai/v1/knowledge-bases/6e8a0c2d-4f6b-4d8e-a0c2-4e6a8c0e2a4c/documents" \
  -H "Authorization: Bearer $AIGENTLY_API_KEY" \
  -F "file=@opening-hours.md;type=text/markdown"
```

**JavaScript**

```js
import { readFile } from "node:fs/promises";

// Add a file to a knowledge base. With a live key. It is indexed in the background: read it until it says indexed.
const form = new FormData();
form.append("file", new Blob([await readFile("opening-hours.md")], { type: "text/markdown" }), "opening-hours.md");

const response = await fetch("https://api.aigently.ai/v1/knowledge-bases/6e8a0c2d-4f6b-4d8e-a0c2-4e6a8c0e2a4c/documents", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.AIGENTLY_API_KEY}`,
  },
  body: form,
});
const answer = await response.json();
if (!response.ok) throw new Error(`${answer.code}: ${answer.detail}`);
console.log(answer);
```

**Python**

```python
# Add a file to a knowledge base. With a live key. It is indexed in the background: read it until it says indexed.
# pip install httpx
import os

import httpx

with open("opening-hours.md", "rb") as upload:
    response = httpx.post(
        "https://api.aigently.ai/v1/knowledge-bases/6e8a0c2d-4f6b-4d8e-a0c2-4e6a8c0e2a4c/documents",
        headers={
            "Authorization": f"Bearer {os.environ['AIGENTLY_API_KEY']}",
        },
        files={"file": ("opening-hours.md", upload, "text/markdown")},
        timeout=60,
    )
if response.is_error:
    raise SystemExit(response.text)
print(response.json())
```

**Go**

```go
// Add a file to a knowledge base. With a live key. It is indexed in the background: read it until it says indexed.
package main

import (
	"bytes"
	"fmt"
	"io"
	"mime/multipart"
	"net/http"
	"os"
)

func main() {
	var form bytes.Buffer
	writer := multipart.NewWriter(&form)
	part, _ := writer.CreateFormFile("file", "opening-hours.md")
	contents, err := os.ReadFile("opening-hours.md")
	if err != nil {
		panic(err)
	}
	part.Write(contents)
	writer.Close()
	request, err := http.NewRequest("POST", "https://api.aigently.ai/v1/knowledge-bases/6e8a0c2d-4f6b-4d8e-a0c2-4e6a8c0e2a4c/documents", &form)
	if err != nil {
		panic(err)
	}
	request.Header.Set("Authorization", "Bearer "+os.Getenv("AIGENTLY_API_KEY"))
	request.Header.Set("Content-Type", writer.FormDataContentType())
	response, err := http.DefaultClient.Do(request)
	if err != nil {
		panic(err)
	}
	defer response.Body.Close()
	body, _ := io.ReadAll(response.Body)
	if response.StatusCode >= 400 {
		fmt.Fprintln(os.Stderr, string(body))
		os.Exit(1)
	}
	fmt.Println(string(body))
}
```

**Java**

```java
// Add a file to a knowledge base. With a live key. It is indexed in the background: read it until it says indexed.
import java.io.ByteArrayOutputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

public class Main {
    public static void main(String[] args) throws Exception {
        String boundary = Long.toHexString(System.nanoTime());
        ByteArrayOutputStream form = new ByteArrayOutputStream();
        form.write(("--" + boundary
            + "\r\nContent-Disposition: form-data; name=\"file\"; filename=\"opening-hours.md\"\r\n"
            + "Content-Type: text/markdown\r\n\r\n").getBytes());
        form.write(Files.readAllBytes(Path.of("opening-hours.md")));
        form.write(("\r\n--" + boundary + "--\r\n").getBytes());
        HttpRequest request = HttpRequest.newBuilder(URI.create("https://api.aigently.ai/v1/knowledge-bases/6e8a0c2d-4f6b-4d8e-a0c2-4e6a8c0e2a4c/documents"))
            .header("Authorization", "Bearer " + System.getenv("AIGENTLY_API_KEY"))
            .header("Content-Type", "multipart/form-data; boundary=" + boundary)
            .method("POST", HttpRequest.BodyPublishers.ofByteArray(form.toByteArray()))
            .build();
        // HTTP/1.1: on a plain-http address Java's default asks to upgrade, which not every
        // server allows.
        HttpClient client = HttpClient.newBuilder().version(HttpClient.Version.HTTP_1_1).build();
        HttpResponse<String> response =
            client.send(request, HttpResponse.BodyHandlers.ofString());
        if (response.statusCode() >= 400) {
            System.err.println(response.body());
            System.exit(1);
        }
        System.out.println(response.body());
    }
}
```

**C#**

```csharp
// Add a file to a knowledge base. With a live key. It is indexed in the background: read it until it says indexed.
using System.Net.Http.Headers;
using System.Text;

var form = new MultipartFormDataContent();
var upload = new ByteArrayContent(File.ReadAllBytes("opening-hours.md"));
upload.Headers.ContentType = new MediaTypeHeaderValue("text/markdown");
form.Add(upload, "file", "opening-hours.md");

using var client = new HttpClient();
client.DefaultRequestHeaders.Authorization =
    new AuthenticationHeaderValue("Bearer", Environment.GetEnvironmentVariable("AIGENTLY_API_KEY"));
var request = new HttpRequestMessage(HttpMethod.Post, "https://api.aigently.ai/v1/knowledge-bases/6e8a0c2d-4f6b-4d8e-a0c2-4e6a8c0e2a4c/documents")
{
    Content = form,
};
var response = await client.SendAsync(request);
var body = await response.Content.ReadAsStringAsync();
if (!response.IsSuccessStatusCode)
{
    Console.Error.WriteLine(body);
    return 1;
}
Console.WriteLine(body);
return 0;
```

**PHP**

```php
<?php
// Add a file to a knowledge base. With a live key. It is indexed in the background: read it until it says indexed.
$curl = curl_init("https://api.aigently.ai/v1/knowledge-bases/6e8a0c2d-4f6b-4d8e-a0c2-4e6a8c0e2a4c/documents");
curl_setopt_array($curl, [
    CURLOPT_POST => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        "Authorization: Bearer " . getenv("AIGENTLY_API_KEY"),
    ],
    CURLOPT_POSTFIELDS => [
        "file" => new CURLFile("opening-hours.md", "text/markdown"),
    ],
]);
$body = curl_exec($curl);
if (curl_getinfo($curl, CURLINFO_RESPONSE_CODE) >= 400) {
    fwrite(STDERR, $body . "\n");
    exit(1);
}
echo $body, "\n";
```

Send one file as `multipart/form-data`, in a field called `file`: text, Markdown, CSV, JSON, PDF or
Word, with its extension in the name. The answer is `202` with the source, `pending`. It is indexed in
the background, and agents answer from it once it reads `indexed`:

**curl**

```sh
#!/bin/sh
# Read a source's indexing status
curl -sS --fail-with-body -X GET "https://api.aigently.ai/v1/knowledge-bases/6e8a0c2d-4f6b-4d8e-a0c2-4e6a8c0e2a4c/documents/7f9b1d3e-5a7c-4e9f-b1d3-5f7b9d1f3b5d" \
  -H "Authorization: Bearer $AIGENTLY_API_KEY"
```

**JavaScript**

```js
// Read a source's indexing status
const response = await fetch("https://api.aigently.ai/v1/knowledge-bases/6e8a0c2d-4f6b-4d8e-a0c2-4e6a8c0e2a4c/documents/7f9b1d3e-5a7c-4e9f-b1d3-5f7b9d1f3b5d", {
  method: "GET",
  headers: {
    Authorization: `Bearer ${process.env.AIGENTLY_API_KEY}`,
  },
});
const answer = await response.json();
if (!response.ok) throw new Error(`${answer.code}: ${answer.detail}`);
console.log(answer);
```

**Python**

```python
# Read a source's indexing status
# pip install httpx
import os

import httpx

response = httpx.get(
    "https://api.aigently.ai/v1/knowledge-bases/6e8a0c2d-4f6b-4d8e-a0c2-4e6a8c0e2a4c/documents/7f9b1d3e-5a7c-4e9f-b1d3-5f7b9d1f3b5d",
    headers={
        "Authorization": f"Bearer {os.environ['AIGENTLY_API_KEY']}",
    },
)
if response.is_error:
    raise SystemExit(response.text)
print(response.json())
```

**Go**

```go
// Read a source's indexing status
package main

import (
	"fmt"
	"io"
	"net/http"
	"os"
)

func main() {
	request, err := http.NewRequest("GET", "https://api.aigently.ai/v1/knowledge-bases/6e8a0c2d-4f6b-4d8e-a0c2-4e6a8c0e2a4c/documents/7f9b1d3e-5a7c-4e9f-b1d3-5f7b9d1f3b5d", nil)
	if err != nil {
		panic(err)
	}
	request.Header.Set("Authorization", "Bearer "+os.Getenv("AIGENTLY_API_KEY"))
	response, err := http.DefaultClient.Do(request)
	if err != nil {
		panic(err)
	}
	defer response.Body.Close()
	body, _ := io.ReadAll(response.Body)
	if response.StatusCode >= 400 {
		fmt.Fprintln(os.Stderr, string(body))
		os.Exit(1)
	}
	fmt.Println(string(body))
}
```

**Java**

```java
// Read a source's indexing status
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

public class Main {
    public static void main(String[] args) throws Exception {
        HttpRequest request = HttpRequest.newBuilder(URI.create("https://api.aigently.ai/v1/knowledge-bases/6e8a0c2d-4f6b-4d8e-a0c2-4e6a8c0e2a4c/documents/7f9b1d3e-5a7c-4e9f-b1d3-5f7b9d1f3b5d"))
            .header("Authorization", "Bearer " + System.getenv("AIGENTLY_API_KEY"))
            .method("GET", HttpRequest.BodyPublishers.noBody())
            .build();
        // HTTP/1.1: on a plain-http address Java's default asks to upgrade, which not every
        // server allows.
        HttpClient client = HttpClient.newBuilder().version(HttpClient.Version.HTTP_1_1).build();
        HttpResponse<String> response =
            client.send(request, HttpResponse.BodyHandlers.ofString());
        if (response.statusCode() >= 400) {
            System.err.println(response.body());
            System.exit(1);
        }
        System.out.println(response.body());
    }
}
```

**C#**

```csharp
// Read a source's indexing status
using System.Net.Http.Headers;
using System.Text;

using var client = new HttpClient();
client.DefaultRequestHeaders.Authorization =
    new AuthenticationHeaderValue("Bearer", Environment.GetEnvironmentVariable("AIGENTLY_API_KEY"));
var request = new HttpRequestMessage(HttpMethod.Get, "https://api.aigently.ai/v1/knowledge-bases/6e8a0c2d-4f6b-4d8e-a0c2-4e6a8c0e2a4c/documents/7f9b1d3e-5a7c-4e9f-b1d3-5f7b9d1f3b5d")
{
};
var response = await client.SendAsync(request);
var body = await response.Content.ReadAsStringAsync();
if (!response.IsSuccessStatusCode)
{
    Console.Error.WriteLine(body);
    return 1;
}
Console.WriteLine(body);
return 0;
```

**PHP**

```php
<?php
// Read a source's indexing status
$curl = curl_init("https://api.aigently.ai/v1/knowledge-bases/6e8a0c2d-4f6b-4d8e-a0c2-4e6a8c0e2a4c/documents/7f9b1d3e-5a7c-4e9f-b1d3-5f7b9d1f3b5d");
curl_setopt_array($curl, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        "Authorization: Bearer " . getenv("AIGENTLY_API_KEY"),
    ],
]);
$body = curl_exec($curl);
if (curl_getinfo($curl, CURLINFO_RESPONSE_CODE) >= 400) {
    fwrite(STDERR, $body . "\n");
    exit(1);
}
echo $body, "\n";
```

| `status` | What it means |
|---|---|
| `pending` | Waiting for the indexer. |
| `processing` | Being read now. |
| `indexed` | Agents answer from it. |
| `failed` | It could not be indexed, and `error` says why. |
| `held` | A website page waiting for review in the console. |

The same bytes twice in one base are refused with `duplicate_source`. A base holds a set number of
sources; past it, a file is refused with `source_limit_reached`.

## Replace a file

**curl**

```sh
#!/bin/sh
# Replace a file with a new version. It keeps its id; agents answer from the old version until the new one is indexed.
curl -sS --fail-with-body -X PUT "https://api.aigently.ai/v1/knowledge-bases/6e8a0c2d-4f6b-4d8e-a0c2-4e6a8c0e2a4c/documents/7f9b1d3e-5a7c-4e9f-b1d3-5f7b9d1f3b5d" \
  -H "Authorization: Bearer $AIGENTLY_API_KEY" \
  -F "file=@opening-hours.md;type=text/markdown"
```

**JavaScript**

```js
import { readFile } from "node:fs/promises";

// Replace a file with a new version. It keeps its id; agents answer from the old version until the new one is indexed.
const form = new FormData();
form.append("file", new Blob([await readFile("opening-hours.md")], { type: "text/markdown" }), "opening-hours.md");

const response = await fetch("https://api.aigently.ai/v1/knowledge-bases/6e8a0c2d-4f6b-4d8e-a0c2-4e6a8c0e2a4c/documents/7f9b1d3e-5a7c-4e9f-b1d3-5f7b9d1f3b5d", {
  method: "PUT",
  headers: {
    Authorization: `Bearer ${process.env.AIGENTLY_API_KEY}`,
  },
  body: form,
});
const answer = await response.json();
if (!response.ok) throw new Error(`${answer.code}: ${answer.detail}`);
console.log(answer);
```

**Python**

```python
# Replace a file with a new version. It keeps its id; agents answer from the old version until the new one is indexed.
# pip install httpx
import os

import httpx

with open("opening-hours.md", "rb") as upload:
    response = httpx.put(
        "https://api.aigently.ai/v1/knowledge-bases/6e8a0c2d-4f6b-4d8e-a0c2-4e6a8c0e2a4c/documents/7f9b1d3e-5a7c-4e9f-b1d3-5f7b9d1f3b5d",
        headers={
            "Authorization": f"Bearer {os.environ['AIGENTLY_API_KEY']}",
        },
        files={"file": ("opening-hours.md", upload, "text/markdown")},
        timeout=60,
    )
if response.is_error:
    raise SystemExit(response.text)
print(response.json())
```

**Go**

```go
// Replace a file with a new version. It keeps its id; agents answer from the old version until the new one is indexed.
package main

import (
	"bytes"
	"fmt"
	"io"
	"mime/multipart"
	"net/http"
	"os"
)

func main() {
	var form bytes.Buffer
	writer := multipart.NewWriter(&form)
	part, _ := writer.CreateFormFile("file", "opening-hours.md")
	contents, err := os.ReadFile("opening-hours.md")
	if err != nil {
		panic(err)
	}
	part.Write(contents)
	writer.Close()
	request, err := http.NewRequest("PUT", "https://api.aigently.ai/v1/knowledge-bases/6e8a0c2d-4f6b-4d8e-a0c2-4e6a8c0e2a4c/documents/7f9b1d3e-5a7c-4e9f-b1d3-5f7b9d1f3b5d", &form)
	if err != nil {
		panic(err)
	}
	request.Header.Set("Authorization", "Bearer "+os.Getenv("AIGENTLY_API_KEY"))
	request.Header.Set("Content-Type", writer.FormDataContentType())
	response, err := http.DefaultClient.Do(request)
	if err != nil {
		panic(err)
	}
	defer response.Body.Close()
	body, _ := io.ReadAll(response.Body)
	if response.StatusCode >= 400 {
		fmt.Fprintln(os.Stderr, string(body))
		os.Exit(1)
	}
	fmt.Println(string(body))
}
```

**Java**

```java
// Replace a file with a new version. It keeps its id; agents answer from the old version until the new one is indexed.
import java.io.ByteArrayOutputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

public class Main {
    public static void main(String[] args) throws Exception {
        String boundary = Long.toHexString(System.nanoTime());
        ByteArrayOutputStream form = new ByteArrayOutputStream();
        form.write(("--" + boundary
            + "\r\nContent-Disposition: form-data; name=\"file\"; filename=\"opening-hours.md\"\r\n"
            + "Content-Type: text/markdown\r\n\r\n").getBytes());
        form.write(Files.readAllBytes(Path.of("opening-hours.md")));
        form.write(("\r\n--" + boundary + "--\r\n").getBytes());
        HttpRequest request = HttpRequest.newBuilder(URI.create("https://api.aigently.ai/v1/knowledge-bases/6e8a0c2d-4f6b-4d8e-a0c2-4e6a8c0e2a4c/documents/7f9b1d3e-5a7c-4e9f-b1d3-5f7b9d1f3b5d"))
            .header("Authorization", "Bearer " + System.getenv("AIGENTLY_API_KEY"))
            .header("Content-Type", "multipart/form-data; boundary=" + boundary)
            .method("PUT", HttpRequest.BodyPublishers.ofByteArray(form.toByteArray()))
            .build();
        // HTTP/1.1: on a plain-http address Java's default asks to upgrade, which not every
        // server allows.
        HttpClient client = HttpClient.newBuilder().version(HttpClient.Version.HTTP_1_1).build();
        HttpResponse<String> response =
            client.send(request, HttpResponse.BodyHandlers.ofString());
        if (response.statusCode() >= 400) {
            System.err.println(response.body());
            System.exit(1);
        }
        System.out.println(response.body());
    }
}
```

**C#**

```csharp
// Replace a file with a new version. It keeps its id; agents answer from the old version until the new one is indexed.
using System.Net.Http.Headers;
using System.Text;

var form = new MultipartFormDataContent();
var upload = new ByteArrayContent(File.ReadAllBytes("opening-hours.md"));
upload.Headers.ContentType = new MediaTypeHeaderValue("text/markdown");
form.Add(upload, "file", "opening-hours.md");

using var client = new HttpClient();
client.DefaultRequestHeaders.Authorization =
    new AuthenticationHeaderValue("Bearer", Environment.GetEnvironmentVariable("AIGENTLY_API_KEY"));
var request = new HttpRequestMessage(HttpMethod.Put, "https://api.aigently.ai/v1/knowledge-bases/6e8a0c2d-4f6b-4d8e-a0c2-4e6a8c0e2a4c/documents/7f9b1d3e-5a7c-4e9f-b1d3-5f7b9d1f3b5d")
{
    Content = form,
};
var response = await client.SendAsync(request);
var body = await response.Content.ReadAsStringAsync();
if (!response.IsSuccessStatusCode)
{
    Console.Error.WriteLine(body);
    return 1;
}
Console.WriteLine(body);
return 0;
```

**PHP**

```php
<?php
// Replace a file with a new version. It keeps its id; agents answer from the old version until the new one is indexed.
$curl = curl_init("https://api.aigently.ai/v1/knowledge-bases/6e8a0c2d-4f6b-4d8e-a0c2-4e6a8c0e2a4c/documents/7f9b1d3e-5a7c-4e9f-b1d3-5f7b9d1f3b5d");
curl_setopt_array($curl, [
    CURLOPT_CUSTOMREQUEST => "PUT",
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        "Authorization: Bearer " . getenv("AIGENTLY_API_KEY"),
    ],
    CURLOPT_POSTFIELDS => [
        "file" => new CURLFile("opening-hours.md", "text/markdown"),
    ],
]);
$body = curl_exec($curl);
if (curl_getinfo($curl, CURLINFO_RESPONSE_CODE) >= 400) {
    fwrite(STDERR, $body . "\n");
    exit(1);
}
echo $body, "\n";
```

A new version keeps its id and goes back to `pending`. Agents keep answering from the old version
until the new one is indexed. If the new version fails, the old one stays, and `error` says what went
wrong. Only a file can be replaced; a website page changes when the website is read again.

## Delete a source

**curl**

```sh
#!/bin/sh
# Delete a source
curl -sS --fail-with-body -X DELETE "https://api.aigently.ai/v1/knowledge-bases/6e8a0c2d-4f6b-4d8e-a0c2-4e6a8c0e2a4c/documents/7f9b1d3e-5a7c-4e9f-b1d3-5f7b9d1f3b5d" \
  -H "Authorization: Bearer $AIGENTLY_API_KEY"
```

**JavaScript**

```js
// Delete a source
const response = await fetch("https://api.aigently.ai/v1/knowledge-bases/6e8a0c2d-4f6b-4d8e-a0c2-4e6a8c0e2a4c/documents/7f9b1d3e-5a7c-4e9f-b1d3-5f7b9d1f3b5d", {
  method: "DELETE",
  headers: {
    Authorization: `Bearer ${process.env.AIGENTLY_API_KEY}`,
  },
});
const answer = await response.json();
if (!response.ok) throw new Error(`${answer.code}: ${answer.detail}`);
console.log(answer);
```

**Python**

```python
# Delete a source
# pip install httpx
import os

import httpx

response = httpx.delete(
    "https://api.aigently.ai/v1/knowledge-bases/6e8a0c2d-4f6b-4d8e-a0c2-4e6a8c0e2a4c/documents/7f9b1d3e-5a7c-4e9f-b1d3-5f7b9d1f3b5d",
    headers={
        "Authorization": f"Bearer {os.environ['AIGENTLY_API_KEY']}",
    },
)
if response.is_error:
    raise SystemExit(response.text)
print(response.json())
```

**Go**

```go
// Delete a source
package main

import (
	"fmt"
	"io"
	"net/http"
	"os"
)

func main() {
	request, err := http.NewRequest("DELETE", "https://api.aigently.ai/v1/knowledge-bases/6e8a0c2d-4f6b-4d8e-a0c2-4e6a8c0e2a4c/documents/7f9b1d3e-5a7c-4e9f-b1d3-5f7b9d1f3b5d", nil)
	if err != nil {
		panic(err)
	}
	request.Header.Set("Authorization", "Bearer "+os.Getenv("AIGENTLY_API_KEY"))
	response, err := http.DefaultClient.Do(request)
	if err != nil {
		panic(err)
	}
	defer response.Body.Close()
	body, _ := io.ReadAll(response.Body)
	if response.StatusCode >= 400 {
		fmt.Fprintln(os.Stderr, string(body))
		os.Exit(1)
	}
	fmt.Println(string(body))
}
```

**Java**

```java
// Delete a source
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

public class Main {
    public static void main(String[] args) throws Exception {
        HttpRequest request = HttpRequest.newBuilder(URI.create("https://api.aigently.ai/v1/knowledge-bases/6e8a0c2d-4f6b-4d8e-a0c2-4e6a8c0e2a4c/documents/7f9b1d3e-5a7c-4e9f-b1d3-5f7b9d1f3b5d"))
            .header("Authorization", "Bearer " + System.getenv("AIGENTLY_API_KEY"))
            .method("DELETE", HttpRequest.BodyPublishers.noBody())
            .build();
        // HTTP/1.1: on a plain-http address Java's default asks to upgrade, which not every
        // server allows.
        HttpClient client = HttpClient.newBuilder().version(HttpClient.Version.HTTP_1_1).build();
        HttpResponse<String> response =
            client.send(request, HttpResponse.BodyHandlers.ofString());
        if (response.statusCode() >= 400) {
            System.err.println(response.body());
            System.exit(1);
        }
        System.out.println(response.body());
    }
}
```

**C#**

```csharp
// Delete a source
using System.Net.Http.Headers;
using System.Text;

using var client = new HttpClient();
client.DefaultRequestHeaders.Authorization =
    new AuthenticationHeaderValue("Bearer", Environment.GetEnvironmentVariable("AIGENTLY_API_KEY"));
var request = new HttpRequestMessage(HttpMethod.Delete, "https://api.aigently.ai/v1/knowledge-bases/6e8a0c2d-4f6b-4d8e-a0c2-4e6a8c0e2a4c/documents/7f9b1d3e-5a7c-4e9f-b1d3-5f7b9d1f3b5d")
{
};
var response = await client.SendAsync(request);
var body = await response.Content.ReadAsStringAsync();
if (!response.IsSuccessStatusCode)
{
    Console.Error.WriteLine(body);
    return 1;
}
Console.WriteLine(body);
return 0;
```

**PHP**

```php
<?php
// Delete a source
$curl = curl_init("https://api.aigently.ai/v1/knowledge-bases/6e8a0c2d-4f6b-4d8e-a0c2-4e6a8c0e2a4c/documents/7f9b1d3e-5a7c-4e9f-b1d3-5f7b9d1f3b5d");
curl_setopt_array($curl, [
    CURLOPT_CUSTOMREQUEST => "DELETE",
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        "Authorization: Bearer " . getenv("AIGENTLY_API_KEY"),
    ],
]);
$body = curl_exec($curl);
if (curl_getinfo($curl, CURLINFO_RESPONSE_CODE) >= 400) {
    fwrite(STDERR, $body . "\n");
    exit(1);
}
echo $body, "\n";
```

Agents stop answering from it on their next turn.

## Everything in a base

**curl**

```sh
#!/bin/sh
# List what a knowledge base holds
curl -sS --fail-with-body -X GET "https://api.aigently.ai/v1/knowledge-bases/6e8a0c2d-4f6b-4d8e-a0c2-4e6a8c0e2a4c/documents?status=failed" \
  -H "Authorization: Bearer $AIGENTLY_API_KEY"
```

**JavaScript**

```js
// List what a knowledge base holds
const response = await fetch("https://api.aigently.ai/v1/knowledge-bases/6e8a0c2d-4f6b-4d8e-a0c2-4e6a8c0e2a4c/documents?status=failed", {
  method: "GET",
  headers: {
    Authorization: `Bearer ${process.env.AIGENTLY_API_KEY}`,
  },
});
const answer = await response.json();
if (!response.ok) throw new Error(`${answer.code}: ${answer.detail}`);
console.log(answer);
```

**Python**

```python
# List what a knowledge base holds
# pip install httpx
import os

import httpx

response = httpx.get(
    "https://api.aigently.ai/v1/knowledge-bases/6e8a0c2d-4f6b-4d8e-a0c2-4e6a8c0e2a4c/documents",
    headers={
        "Authorization": f"Bearer {os.environ['AIGENTLY_API_KEY']}",
    },
    params={
        "status": "failed",
    },
)
if response.is_error:
    raise SystemExit(response.text)
print(response.json())
```

**Go**

```go
// List what a knowledge base holds
package main

import (
	"fmt"
	"io"
	"net/http"
	"os"
)

func main() {
	request, err := http.NewRequest("GET", "https://api.aigently.ai/v1/knowledge-bases/6e8a0c2d-4f6b-4d8e-a0c2-4e6a8c0e2a4c/documents?status=failed", nil)
	if err != nil {
		panic(err)
	}
	request.Header.Set("Authorization", "Bearer "+os.Getenv("AIGENTLY_API_KEY"))
	response, err := http.DefaultClient.Do(request)
	if err != nil {
		panic(err)
	}
	defer response.Body.Close()
	body, _ := io.ReadAll(response.Body)
	if response.StatusCode >= 400 {
		fmt.Fprintln(os.Stderr, string(body))
		os.Exit(1)
	}
	fmt.Println(string(body))
}
```

**Java**

```java
// List what a knowledge base holds
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

public class Main {
    public static void main(String[] args) throws Exception {
        HttpRequest request = HttpRequest.newBuilder(URI.create("https://api.aigently.ai/v1/knowledge-bases/6e8a0c2d-4f6b-4d8e-a0c2-4e6a8c0e2a4c/documents?status=failed"))
            .header("Authorization", "Bearer " + System.getenv("AIGENTLY_API_KEY"))
            .method("GET", HttpRequest.BodyPublishers.noBody())
            .build();
        // HTTP/1.1: on a plain-http address Java's default asks to upgrade, which not every
        // server allows.
        HttpClient client = HttpClient.newBuilder().version(HttpClient.Version.HTTP_1_1).build();
        HttpResponse<String> response =
            client.send(request, HttpResponse.BodyHandlers.ofString());
        if (response.statusCode() >= 400) {
            System.err.println(response.body());
            System.exit(1);
        }
        System.out.println(response.body());
    }
}
```

**C#**

```csharp
// List what a knowledge base holds
using System.Net.Http.Headers;
using System.Text;

using var client = new HttpClient();
client.DefaultRequestHeaders.Authorization =
    new AuthenticationHeaderValue("Bearer", Environment.GetEnvironmentVariable("AIGENTLY_API_KEY"));
var request = new HttpRequestMessage(HttpMethod.Get, "https://api.aigently.ai/v1/knowledge-bases/6e8a0c2d-4f6b-4d8e-a0c2-4e6a8c0e2a4c/documents?status=failed")
{
};
var response = await client.SendAsync(request);
var body = await response.Content.ReadAsStringAsync();
if (!response.IsSuccessStatusCode)
{
    Console.Error.WriteLine(body);
    return 1;
}
Console.WriteLine(body);
return 0;
```

**PHP**

```php
<?php
// List what a knowledge base holds
$curl = curl_init("https://api.aigently.ai/v1/knowledge-bases/6e8a0c2d-4f6b-4d8e-a0c2-4e6a8c0e2a4c/documents?status=failed");
curl_setopt_array($curl, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        "Authorization: Bearer " . getenv("AIGENTLY_API_KEY"),
    ],
]);
$body = curl_exec($curl);
if (curl_getinfo($curl, CURLINFO_RESPONSE_CODE) >= 400) {
    fwrite(STDERR, $body . "\n");
    exit(1);
}
echo $body, "\n";
```

## Read a website again

Websites are given to a base in the console. Read one again when it has changed:

**curl**

```sh
#!/bin/sh
# List the websites a knowledge base reads
curl -sS --fail-with-body -X GET "https://api.aigently.ai/v1/knowledge-bases/6e8a0c2d-4f6b-4d8e-a0c2-4e6a8c0e2a4c/crawls" \
  -H "Authorization: Bearer $AIGENTLY_API_KEY"
```

**JavaScript**

```js
// List the websites a knowledge base reads
const response = await fetch("https://api.aigently.ai/v1/knowledge-bases/6e8a0c2d-4f6b-4d8e-a0c2-4e6a8c0e2a4c/crawls", {
  method: "GET",
  headers: {
    Authorization: `Bearer ${process.env.AIGENTLY_API_KEY}`,
  },
});
const answer = await response.json();
if (!response.ok) throw new Error(`${answer.code}: ${answer.detail}`);
console.log(answer);
```

**Python**

```python
# List the websites a knowledge base reads
# pip install httpx
import os

import httpx

response = httpx.get(
    "https://api.aigently.ai/v1/knowledge-bases/6e8a0c2d-4f6b-4d8e-a0c2-4e6a8c0e2a4c/crawls",
    headers={
        "Authorization": f"Bearer {os.environ['AIGENTLY_API_KEY']}",
    },
)
if response.is_error:
    raise SystemExit(response.text)
print(response.json())
```

**Go**

```go
// List the websites a knowledge base reads
package main

import (
	"fmt"
	"io"
	"net/http"
	"os"
)

func main() {
	request, err := http.NewRequest("GET", "https://api.aigently.ai/v1/knowledge-bases/6e8a0c2d-4f6b-4d8e-a0c2-4e6a8c0e2a4c/crawls", nil)
	if err != nil {
		panic(err)
	}
	request.Header.Set("Authorization", "Bearer "+os.Getenv("AIGENTLY_API_KEY"))
	response, err := http.DefaultClient.Do(request)
	if err != nil {
		panic(err)
	}
	defer response.Body.Close()
	body, _ := io.ReadAll(response.Body)
	if response.StatusCode >= 400 {
		fmt.Fprintln(os.Stderr, string(body))
		os.Exit(1)
	}
	fmt.Println(string(body))
}
```

**Java**

```java
// List the websites a knowledge base reads
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

public class Main {
    public static void main(String[] args) throws Exception {
        HttpRequest request = HttpRequest.newBuilder(URI.create("https://api.aigently.ai/v1/knowledge-bases/6e8a0c2d-4f6b-4d8e-a0c2-4e6a8c0e2a4c/crawls"))
            .header("Authorization", "Bearer " + System.getenv("AIGENTLY_API_KEY"))
            .method("GET", HttpRequest.BodyPublishers.noBody())
            .build();
        // HTTP/1.1: on a plain-http address Java's default asks to upgrade, which not every
        // server allows.
        HttpClient client = HttpClient.newBuilder().version(HttpClient.Version.HTTP_1_1).build();
        HttpResponse<String> response =
            client.send(request, HttpResponse.BodyHandlers.ofString());
        if (response.statusCode() >= 400) {
            System.err.println(response.body());
            System.exit(1);
        }
        System.out.println(response.body());
    }
}
```

**C#**

```csharp
// List the websites a knowledge base reads
using System.Net.Http.Headers;
using System.Text;

using var client = new HttpClient();
client.DefaultRequestHeaders.Authorization =
    new AuthenticationHeaderValue("Bearer", Environment.GetEnvironmentVariable("AIGENTLY_API_KEY"));
var request = new HttpRequestMessage(HttpMethod.Get, "https://api.aigently.ai/v1/knowledge-bases/6e8a0c2d-4f6b-4d8e-a0c2-4e6a8c0e2a4c/crawls")
{
};
var response = await client.SendAsync(request);
var body = await response.Content.ReadAsStringAsync();
if (!response.IsSuccessStatusCode)
{
    Console.Error.WriteLine(body);
    return 1;
}
Console.WriteLine(body);
return 0;
```

**PHP**

```php
<?php
// List the websites a knowledge base reads
$curl = curl_init("https://api.aigently.ai/v1/knowledge-bases/6e8a0c2d-4f6b-4d8e-a0c2-4e6a8c0e2a4c/crawls");
curl_setopt_array($curl, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        "Authorization: Bearer " . getenv("AIGENTLY_API_KEY"),
    ],
]);
$body = curl_exec($curl);
if (curl_getinfo($curl, CURLINFO_RESPONSE_CODE) >= 400) {
    fwrite(STDERR, $body . "\n");
    exit(1);
}
echo $body, "\n";
```

**curl**

```sh
#!/bin/sh
# Read a website again. Pages that have not changed are skipped.
curl -sS --fail-with-body -X POST "https://api.aigently.ai/v1/knowledge-bases/6e8a0c2d-4f6b-4d8e-a0c2-4e6a8c0e2a4c/crawls/8a0c2e4f-6b8d-4f0a-c2e4-6a8c0e2a4c6e/refresh" \
  -H "Authorization: Bearer $AIGENTLY_API_KEY"
```

**JavaScript**

```js
// Read a website again. Pages that have not changed are skipped.
const response = await fetch("https://api.aigently.ai/v1/knowledge-bases/6e8a0c2d-4f6b-4d8e-a0c2-4e6a8c0e2a4c/crawls/8a0c2e4f-6b8d-4f0a-c2e4-6a8c0e2a4c6e/refresh", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.AIGENTLY_API_KEY}`,
  },
});
const answer = await response.json();
if (!response.ok) throw new Error(`${answer.code}: ${answer.detail}`);
console.log(answer);
```

**Python**

```python
# Read a website again. Pages that have not changed are skipped.
# pip install httpx
import os

import httpx

response = httpx.post(
    "https://api.aigently.ai/v1/knowledge-bases/6e8a0c2d-4f6b-4d8e-a0c2-4e6a8c0e2a4c/crawls/8a0c2e4f-6b8d-4f0a-c2e4-6a8c0e2a4c6e/refresh",
    headers={
        "Authorization": f"Bearer {os.environ['AIGENTLY_API_KEY']}",
    },
    timeout=60,
)
if response.is_error:
    raise SystemExit(response.text)
print(response.json())
```

**Go**

```go
// Read a website again. Pages that have not changed are skipped.
package main

import (
	"fmt"
	"io"
	"net/http"
	"os"
)

func main() {
	request, err := http.NewRequest("POST", "https://api.aigently.ai/v1/knowledge-bases/6e8a0c2d-4f6b-4d8e-a0c2-4e6a8c0e2a4c/crawls/8a0c2e4f-6b8d-4f0a-c2e4-6a8c0e2a4c6e/refresh", nil)
	if err != nil {
		panic(err)
	}
	request.Header.Set("Authorization", "Bearer "+os.Getenv("AIGENTLY_API_KEY"))
	response, err := http.DefaultClient.Do(request)
	if err != nil {
		panic(err)
	}
	defer response.Body.Close()
	body, _ := io.ReadAll(response.Body)
	if response.StatusCode >= 400 {
		fmt.Fprintln(os.Stderr, string(body))
		os.Exit(1)
	}
	fmt.Println(string(body))
}
```

**Java**

```java
// Read a website again. Pages that have not changed are skipped.
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

public class Main {
    public static void main(String[] args) throws Exception {
        HttpRequest request = HttpRequest.newBuilder(URI.create("https://api.aigently.ai/v1/knowledge-bases/6e8a0c2d-4f6b-4d8e-a0c2-4e6a8c0e2a4c/crawls/8a0c2e4f-6b8d-4f0a-c2e4-6a8c0e2a4c6e/refresh"))
            .header("Authorization", "Bearer " + System.getenv("AIGENTLY_API_KEY"))
            .method("POST", HttpRequest.BodyPublishers.noBody())
            .build();
        // HTTP/1.1: on a plain-http address Java's default asks to upgrade, which not every
        // server allows.
        HttpClient client = HttpClient.newBuilder().version(HttpClient.Version.HTTP_1_1).build();
        HttpResponse<String> response =
            client.send(request, HttpResponse.BodyHandlers.ofString());
        if (response.statusCode() >= 400) {
            System.err.println(response.body());
            System.exit(1);
        }
        System.out.println(response.body());
    }
}
```

**C#**

```csharp
// Read a website again. Pages that have not changed are skipped.
using System.Net.Http.Headers;
using System.Text;

using var client = new HttpClient();
client.DefaultRequestHeaders.Authorization =
    new AuthenticationHeaderValue("Bearer", Environment.GetEnvironmentVariable("AIGENTLY_API_KEY"));
var request = new HttpRequestMessage(HttpMethod.Post, "https://api.aigently.ai/v1/knowledge-bases/6e8a0c2d-4f6b-4d8e-a0c2-4e6a8c0e2a4c/crawls/8a0c2e4f-6b8d-4f0a-c2e4-6a8c0e2a4c6e/refresh")
{
};
var response = await client.SendAsync(request);
var body = await response.Content.ReadAsStringAsync();
if (!response.IsSuccessStatusCode)
{
    Console.Error.WriteLine(body);
    return 1;
}
Console.WriteLine(body);
return 0;
```

**PHP**

```php
<?php
// Read a website again. Pages that have not changed are skipped.
$curl = curl_init("https://api.aigently.ai/v1/knowledge-bases/6e8a0c2d-4f6b-4d8e-a0c2-4e6a8c0e2a4c/crawls/8a0c2e4f-6b8d-4f0a-c2e4-6a8c0e2a4c6e/refresh");
curl_setopt_array($curl, [
    CURLOPT_POST => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        "Authorization: Bearer " . getenv("AIGENTLY_API_KEY"),
    ],
    CURLOPT_POSTFIELDS => "",
]);
$body = curl_exec($curl);
if (curl_getinfo($curl, CURLINFO_RESPONSE_CODE) >= 400) {
    fwrite(STDERR, $body . "\n");
    exit(1);
}
echo $body, "\n";
```

Pages that have not changed are skipped, so a refresh costs only what changed. Asking while it is
already being read is `crawl_running`.
