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

# Common rules

> The address, headers, responses, errors, and pagination that apply to every endpoint.

## Base URL

```text theme={null}
https://api.bottari.ai
```

Every endpoint starts with `/v1/`.

## Headers

| Header | Value | Required |
| - | - | - |
| `Authorization` | `Bearer <token>` | Yes |
| `Bottari-Version` | `2026-03-11` | No. Without it, the current version is used. |
| `Content-Type` | `application/json` | For requests with a body |

## Responses

Successful responses carry a `request_id` at the top level, different for each request. Include it when you contact support. Response examples in these docs use example values.

## Errors

Errors use this format. `additional_data` appears only when there's something to add.

```json theme={null}
{
  "object": "error",
  "status": 404,
  "code": "object_not_found",
  "message": "Could not find page with ID: exampleRow01. Make sure the relevant pages and databases are shared with your connection \"Order sync\".",
  "request_id": "33333333-3333-4333-8333-333333333333"
}
```

| Status | `code` | When |
| - | - | - |
| 400 | `invalid_request_url` | The path doesn't exist or the method doesn't match. Rejected before the token is read. |
| 400 | `invalid_json` | The body isn't JSON. |
| 400 | `validation_error` | The version isn't `2026-03-11`, or a body or query value is wrong. A request body over 500 KB also returns this code. |
| 400 | `invalid_request` | Another request that can't be processed. |
| 401 | `unauthorized` | The token is missing or wrong. |
| 403 | `restricted_resource` | A capability is off, or the target can't be written. |
| 404 | `object_not_found` | It doesn't exist, or the integration can't see it. |
| 429 | `rate_limited` | You went over a rate limit. See [Rate limits](https://developers.bottari.ai/limits/rate-limits.md). |
| 500 | `internal_server_error` | A server error. |
| 503 | `service_unavailable` | Temporarily unavailable. Send the request again. |

The server checks the path and method, then JSON, authentication, version, and body values.

## Pagination

List responses use this format.

```json theme={null}
{
  "object": "list",
  "results": [],
  "next_cursor": null,
  "has_more": false,
  "type": "page_or_data_source",
  "page_or_data_source": {},
  "request_id": "33333333-3333-4333-8333-333333333333"
}
```

* `page_size` is 1 to 100, and the default is 100.
* If `has_more` is `true`, send `next_cursor` as `start_cursor` in the next request.
* A cursor only works for its own query. Sending it to a query with a different filter or sort returns 400.

## IDs

| Object | Format |
| - | - |
| Database, data source, row, subpage, property, option, team, integration | 12-character string |
| Person, integration bot | UUID |
| Title property | `title` |
| A document read as a block | The document ID |
| Subpage block, inline database block | The ID of that subpage or database |
| Other blocks | `<document id>.<block address>`. Don't parse this. Use the value you received. |

A database and its data source have the same ID. Property IDs and option IDs are in the [Retrieve a data source](https://developers.bottari.ai/api-reference/data-sources/retrieve.md) response.

IDs in paths and relations aren't format-checked, so a malformed ID returns the same 404 as an ID that doesn't exist.

## Timestamps

`created_time` and `last_edited_time` are in UTC and truncated to the minute. `created_time` and `last_edited_time` filters compare against these values. Formula `now()` and date filters such as today and this week also use UTC.

## Reserved user IDs

`created_by` and `last_edited_by` can hold these values.

| ID | Meaning |
| - | - |
| `00000000-0000-0000-0000-000000000000` | Unknown author. The system created it, or the author's account was deleted. |
| `00000000-0000-0000-0000-000000000003` | An automation wrote it. `GET /v1/users/{id}` returns 404, and it can't be written to a people property. |

## Select properties

`POST /v1/data_sources/{id}/query`, `GET /v1/pages/{id}`, and `POST /v1/search` accept a `filter_properties` query string. Repeat it with property names or IDs to include only those properties. `filter_properties[]` also works.

```text theme={null}
?filter_properties=title&filter_properties=exampleFld01
```

An unknown property returns 400 `Could not find property with name or id: ...` for queries and page reads. Search skips it. Subpages only have the title.

## File URLs

URLs for files properties, file blocks, and uploaded icons and covers are signed and expire after 1 hour. Check `expiry_time` and read again for a new URL.

## Unavailable endpoints

These endpoints don't exist and return 400 `invalid_request_url`.

| Group | Endpoints |
| - | - |
| Writing blocks | `PATCH /v1/blocks/{id}`, `PATCH /v1/blocks/{id}/children`, `DELETE /v1/blocks/{id}` |
| Writing Markdown | `PATCH /v1/pages/{id}/markdown` |
| Writing comments | `POST /v1/comments`, `PATCH /v1/comments/{id}`, `DELETE /v1/comments/{id}` |
| File uploads | `/v1/file_uploads` |
| Writing databases | `POST /v1/databases`, `PATCH /v1/databases/{id}`, `POST /v1/data_sources`, `PATCH /v1/data_sources/{id}` |
| Querying through a database | `POST /v1/databases/{id}/query`. Use `POST /v1/data_sources/{id}/query`. |
| Other | Views (`/v1/views`), templates, custom emoji lists, OAuth |

Passing `file_upload` to a file, icon, or cover returns 404. `max_file_upload_size_in_bytes` in `GET /v1/users/me` is 0.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.