# Notes and documents API

> Read, search and write every Markdown document in an organization, and the personalization document of the member a key acts as.

Every organization, project, campaign, creator, video, payout method, payout structure and payout payment has a Markdown document (`readme`), and so does every member. These endpoints read and write all of them in one way. Over MCP the same endpoints are the `list_notes`, `get_notes`, `update_notes`, `get_my_profile`, `update_my_readme` and `list_members` tools. See [Notes and documents](https://www.trackagoat.com/docs/agents/notes.md) for how people and agents use them.

> **Warning:**
>
> Every document is visible to everyone in the organization and every agent connected to it, including each member's personalization document. Don't store anything private in one.

`entity_type` is one of `organization`, `project`, `campaign`, `creator`, `video`, `payout_method`, `payout_structure`, `payout_payment` or `member`. For `member`, the id is a user id, and `me` means the member the key acts as.

## GET /api/v2/notes

Lists documents that have text, newest first within each type, with a preview.

| Parameter | Type | Description |
|-----------|------|-------------|
| `entity_type` | string | Only this kind of document. |
| `project_id` | uuid | The project and the documents inside it. Org-wide documents (organization, members, payout methods) are left out. |
| `q` | string | Case-insensitive text that must appear in the document. |
| `limit` | integer | Items per type, 1 to 100. Default 20. |
| `offset` | integer | Rows to skip. Needs `entity_type`. |

Without `entity_type`, every type is searched and each returns up to `limit` items. `meta.totals` has the number of matches per type and `meta.has_more` names the types with more; page through one of them with `entity_type` and `offset`.

```bash
curl -H "Authorization: Bearer tga_<key>" \
  "https://www.trackagoat.com/api/v2/notes?q=launch"
```

```json
{
  "data": [
    {
      "entity_type": "project",
      "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "name": "Spring Launch",
      "project_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "readme_preview": "## Goal 2M views by May 31. Creators post 3 times a week...",
      "readme_length": 1204,
      "readme_version": "9f2c4e1a7b3d5f60"
    }
  ],
  "error": null,
  "meta": { "totals": { "organization": 0, "project": 1, "campaign": 0 }, "has_more": [], "limit": 20, "offset": 0 }
}
```

## GET /api/v2/notes/\{entity_type\}/\{id\}

Returns one document in full.

| Field | Description |
|-------|-------------|
| `readme` | The Markdown text, or `null`. |
| `readme_version` | A hash of the text. Pass it as `expected_readme_version` to replace the document. |
| `can_edit` | Whether the member the key acts as may change it (scopes aside). |
| `edit_rule` | Who may change it, in words. |
| `app_path` | Where people edit it in the app. |

Returns **404** `not_found` for anything outside the key's organization, including members of other organizations.

## POST /api/v2/notes/\{entity_type\}/\{id\}

Appends to or replaces a document. Needs the `write` scope. Honours `Idempotency-Key`.

```json
{ "mode": "append", "content": "Views dropped 40% week over week." }
```

```json
{ "mode": "replace", "content": "# Spring Launch\n\n...", "expected_readme_version": "9f2c4e1a7b3d5f60" }
```

- `append` adds `content` as a new section at the end, followed by `*Added by <key name> via API, YYYY-MM-DD*` (`via MCP` from the MCP server). If someone edits the document at the same moment, the section is added on top of their edit.
- `replace` overwrites the whole document. If it changed since you read `expected_readme_version`, nothing is written and the response is **409** `notes_conflict` with `meta.current_readme_version`. An empty `content` clears it.

Who can write follows the app:

| Document | Who can change it |
|----------|-------------------|
| `organization`, `project`, `payout_method`, `payout_structure`, `payout_payment` | A key that acts as an org admin. Otherwise **403** `insufficient_role`. |
| `campaign`, `creator`, `video` | Any key with `write`. |
| `member` | Only the member's own key or sign-in. Otherwise **403** `not_your_document`. |

`POST /api/v2/creators/{id}/notes` and `POST /api/v2/videos/{id}/notes` keep working and behave the same.

`PATCH /api/v2/campaigns/{id}` and `PATCH /api/v2/organizations/{orgId}/projects/{projectId}` also take a `readme`, which replaces the document. Send `expected_readme_version` with it to get the same **409** `notes_conflict` instead of overwriting a newer edit. See [Campaigns](https://www.trackagoat.com/docs/api-reference/campaigns.md) and [Projects](https://www.trackagoat.com/docs/api-reference/projects.md).

## GET /api/v2/me

Who the key acts as, and their personalization document.

```json
{
  "data": {
    "id": "8d1c2f0e-1b2a-4c3d-9e8f-7a6b5c4d3e2f",
    "display_name": "Sam Lee",
    "organization": { "id": "1b9d6bcd-bbfd-4b2d-9b5d-ab8dfbbd4bed", "name": "Acme", "role": "admin" },
    "connection": { "via": "api_key", "scopes": ["read", "write"] },
    "readme": "## About me\nPartnerships lead...",
    "readme_version": "0c4f1d2e3a5b6c7d",
    "readme_length": 412,
    "readme_visibility": "Everyone in the organization, and their agents, can read this document."
  },
  "error": null,
  "meta": null
}
```

## PATCH /api/v2/me

Replaces the personalization document of the member the key acts as. Needs the `write` scope.

```json
{ "readme": "## About me\n...", "expected_readme_version": "0c4f1d2e3a5b6c7d" }
```

With `expected_readme_version`, an edit made in the app since your read returns **409** `notes_conflict` and nothing is written. Without it, the write replaces whatever is there.

## GET /api/v2/members

The organization's members: `user_id`, `display_name`, `role`, `joined_at`, `is_you`, and whether each has a personalization document (`has_readme`, `readme_length`, `readme_preview`). Read one with `GET /api/v2/notes/member/{user_id}`.

## Errors

| Status | `meta.code` | When |
|--------|-------------|------|
| 400 | `invalid_entity_type` | `entity_type` isn't one of the types above. |
| 400 | `notes_too_long` | The document would be longer than 50,000 characters. |
| 403 | `insufficient_scope` | A write without the `write` scope. |
| 403 | `insufficient_role` | An org-admin document and the key acts as a member who isn't an org admin. |
| 403 | `not_your_document` | Writing another member's personalization document. |
| 404 | `not_found` | Not in the key's organization. |
| 409 | `notes_conflict` | The document changed since `expected_readme_version`. |
| 409 | `notes_busy` | The record kept changing while saving. Retry. |

---
Source: https://www.trackagoat.com/docs/api-reference/notes
