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

# API reference

> Push content into Cortex from your own code

<Info>
  This is a technical reference for developers. If you just want to connect an AI tool, you do not need this: use a [connection](/connect/how-connections-work) instead.
</Info>

Cortex has a small REST API for pushing content into brains from your own scripts and integrations. It is the same surface the automatic capture and webhook features use underneath.

## Base URL and authentication

All endpoints live under `https://api.oncortex.ai/api/v1`. Authenticate with a [service credential](/connect/service-credentials) as a bearer token:

```
Authorization: Bearer ck_your_key_here
```

A credential is scoped to one brain and one access level. Writing endpoints need a read-and-write credential.

## Push to your default brain

The quickest way to capture something: post to the default inbox, which lands in the credential's brain (or, for a signed-in user, their single writable brain when they have exactly one).

```bash theme={null}
curl https://api.oncortex.ai/api/v1/inbox \
  -H "Authorization: Bearer ck_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{"filename": "note.md", "content": "The text to capture", "externalId": "unique-id-123"}'
```

Set `externalId` to a stable value so that re-sending the same thing does not create a duplicate.

### Say where the document lives

Four optional fields record where a document lives, so the source record shows the link and later versions are linked as revisions:

| Field            | Purpose                                                                                                                                              |
| ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
| `canonicalUrl`   | The human link (an absolute http or https URL). Anything else is refused with a 400.                                                                 |
| `provider`       | Optional. `sharepoint`, `onedrive`, `gdrive`, `confluence`, `notion` or any name of your own; inferred from the link's host when omitted.            |
| `providerItemId` | Optional. The stable id at the provider (a Graph drive item id, a Drive file id, a Confluence page id). Parsed from Google Drive links when omitted. |
| `version`        | Optional. The etag or version at fetch time. Without it, a changed document still counts as a new version, by its content.                           |

When you give a reference and no `externalId`, Cortex derives one as `{provider}:{itemId}@{version}`, so re-sending the same version is a no-op and a new version becomes a new item linked to the earlier one. The response of a GET on the new item carries `revisionOf` (the earlier item, its status and the pages it was filed to) and `revisionDiff`, a line diff of the two extractions.

### Send the file itself

A JSON body can carry the document as base64 instead of text:

```bash theme={null}
curl https://api.oncortex.ai/api/v1/inbox \
  -H "Authorization: Bearer ck_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{"filename": "brief.docx", "contentBase64": "<base64>", "canonicalUrl": "https://sopro.sharepoint.com/sites/cs/brief.docx", "version": "3"}'
```

`contentBase64` and `content` are mutually exclusive. The decoded size is capped at 10 MB on this route; larger files go as multipart form data, up to 50 MB, where the same four fields are accepted as form fields. The same rules apply to your agent's `upload_to_inbox` tool.

Two optional query parameters:

* `?brain={slugOrId}` names a destination brain explicitly (it must be one the credential can write to; a brain-scoped credential can only name its own brain).
* `?path={folder}` suggests where the content should file. It is recorded as a hint for the filing pass, not an order; the content itself can never choose its own destination.
* `?partition={key}` names the client (or site, or matter) in a [partitioned brain](/brains/partitions). An unknown or archived key is refused with a 422 that lists the existing keys. Without it, the attendee emails decide, or the item waits as "needs a client".
* `?skill={name}` (or `skill` in the JSON body or as a form field) pins the filing skill: `ingest`, `ingest-meeting`, `ingest-doc` or `ingest-conversation`. A deliberate upload that names its skill skips the automatic filer's "is this worth filing" check and goes straight to that skill; leave it out for ambient captures (a session hook, say) so the check still runs. A name that is not a skill is refused with a 400 that lists the skills. The same `skill` argument is on your agent's `upload_to_inbox` tool.

## Work with a specific brain's inbox

| Do this                 | Request                                                      |
| ----------------------- | ------------------------------------------------------------ |
| List inbox items        | `GET /api/v1/brains/{brainId}/inbox` (optionally `?status=`) |
| Get one item            | `GET /api/v1/brains/{brainId}/inbox/{itemId}`                |
| Upload an item          | `POST /api/v1/brains/{brainId}/inbox`                        |
| Update an item's status | `PUT /api/v1/brains/{brainId}/inbox/{itemId}/status`         |
| Archive an item         | `POST /api/v1/brains/{brainId}/inbox/{itemId}/archive`       |

Uploads accept either a JSON body (`filename`, `content` or `contentBase64`, optional `externalId`, `targetFolder`, `skill` and the four reference fields above) or multipart form data for binary files, up to 50 MB, with `canonicalUrl`, `provider`, `providerItemId`, `version` and `skill` as form fields. The `?path={folder}` and `?skill={name}` hints work here too. Items carry `skillHint` (the pinned skill, or null), `sourceReference`, `supersedesItemId` and `supersededByItemId`; a GET on one item adds `revisionOf` and `revisionDiff`.

```bash theme={null}
# Upload text to a named brain's inbox
curl https://api.oncortex.ai/api/v1/brains/{brainId}/inbox \
  -H "Authorization: Bearer ck_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{"filename": "report.md", "content": "..."}'
```

## Pages, timelines and the graph

The page endpoints take a brain-scoped credential and the page path as `?path=`. Writes need a read-and-write credential.

| Do this                      | Request                                                                                                                                                                          |
| ---------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| List pages, or read one      | `GET /api/v1/brains/{brainId}/pages` (`?path=` for one page, with its markdown)                                                                                                  |
| Create or replace a page     | `PUT /api/v1/brains/{brainId}/pages` with `{ "path", "markdown", "expectedVersion" }`                                                                                            |
| Add to one part of a page    | `PATCH /api/v1/brains/{brainId}/pages?path=` with `{ "operation", "section", "content", "position" }` (append to or replace a section, append to the page, set frontmatter keys) |
| Read a page's timeline       | `GET /api/v1/brains/{brainId}/pages/timeline?path=&limit=` (the dated lines of its Timeline section, newest first)                                                               |
| Add a timeline line          | `POST /api/v1/brains/{brainId}/pages/timeline?path=` with `{ "text", "date"? }` (today when omitted)                                                                             |
| Move or rename a page        | `POST /api/v1/brains/{brainId}/pages/move` with `{ "fromPath", "toPath" }` (links to the old name are rewritten; refused when the target exists)                                 |
| Delete a page                | `DELETE /api/v1/brains/{brainId}/pages?path=`                                                                                                                                    |
| What changed since last time | `GET /api/v1/brains/{brainId}/changes?since=&limit=` (keep the returned `nextCursor` and pass it back as `since`)                                                                |
| Who links to a page          | `GET /api/v1/brains/{brainId}/graph/backlinks?path=`                                                                                                                             |
| Pages within a few hops      | `GET /api/v1/brains/{brainId}/graph/traverse?path=&depth=&limit=` (depth 1 to 3)                                                                                                 |
| Check names before linking   | `POST /api/v1/brains/{brainId}/graph/resolve` with `{ "refs": ["a slug", "a title", "a/path.md"] }` (the page each would link to, or near misses)                                |
| Search                       | `GET /api/v1/brains/{brainId}/search?q=&mode=` (`exact`, `semantic` or `hybrid`)                                                                                                 |

An edit sent with a stale `expectedVersion`, or one that lands while someone else is saving, is refused with a 409 and the message "This page was changed by someone else. Reload and try again."; read the page again and resend.

## Partitions

In a [partitioned brain](/brains/partitions) the clients (or sites, or matters) are managed here as well as on the Clients page. Roles are the caller's role on the brain: a service credential carries the role it was issued with, so a Write credential can push and assign, an Admin credential can manage partitions, and only an Owner credential or an owner's connection can purge.

| Do this                | Request                                                                                                                  | Role  |
| ---------------------- | ------------------------------------------------------------------------------------------------------------------------ | ----- |
| List, searchable       | `GET /api/v1/brains/{brainId}/partitions?query=&status=`                                                                 | Read  |
| Get one                | `GET /api/v1/brains/{brainId}/partitions/{key}`                                                                          | Read  |
| Create with its folder | `POST /api/v1/brains/{brainId}/partitions` with `{ "name", "key"?, "domains"?, "aliases"?, "externalRef"? }`             | Admin |
| Update                 | `PUT /api/v1/brains/{brainId}/partitions/{key}`                                                                          | Admin |
| Archive or restore     | `POST /api/v1/brains/{brainId}/partitions/{key}/archive` or `/restore`                                                   | Admin |
| Import many            | `POST /api/v1/brains/{brainId}/partitions/import` with an array of the same fields                                       | Admin |
| Purge, permanently     | `DELETE /api/v1/brains/{brainId}/partitions/{key}?purge=true` (refused without the flag; the partition must be archived) | Owner |
| Assign an inbox item   | `POST /api/v1/brains/{brainId}/inbox/{itemId}/partition` with `{ "key", "rememberDomains" }`                             | Write |

Inbox listing takes `?partition={key}` and `?unassigned=true`. Search takes `?partition=`, `?folder=` and `?includeArchived=true` (archived partitions are left out of results unless asked for).

## Members

A brain's access list can be managed here as well as on its Members page, so onboarding a person or an agent can be scripted. The caller needs Admin or Owner on the brain: a service credential counts with the role it was issued, so a read-and-write credential is refused with a 403 and can never widen anyone's access. Only workspace members can be granted (invite them to the workspace first); one grant per person; the last Owner cannot be demoted or removed. Name the person by their user id or their email.

| Do this       | Request                                                                                                                                   | Role  |
| ------------- | ----------------------------------------------------------------------------------------------------------------------------------------- | ----- |
| List members  | `GET /api/v1/brains/{brainId}/members` (user id, email, display name, role, when granted)                                                 | Admin |
| Grant access  | `POST /api/v1/brains/{brainId}/members` with `{ "email" or "userId", "role" }` (`Read`, `Write`, `Admin` or `Owner`; `Read` when omitted) | Admin |
| Change a role | `PUT /api/v1/brains/{brainId}/members/{userId}` with `{ "role" }` (the email works in place of the id)                                    | Admin |
| Revoke access | `DELETE /api/v1/brains/{brainId}/members/{userId}`                                                                                        | Admin |

A grant for someone who already has access is refused with a 409; a grant for someone who is not in the workspace, a demotion of the last Owner, or an unknown role is a 400 with the reason. There is no agent tool for any of this, on purpose: an agent in a conversation cannot add or remove members.

## Receiving from third-party tools

If instead you want an outside tool to push into Cortex (rather than calling the API from your own code), use the [webhook route](/reference/webhooks), which is designed for that and has its own authentication.
