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 for how people and agents use them.
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.
curl -H "Authorization: Bearer tga_<key>" \
"https://www.trackagoat.com/api/v2/notes?q=launch"{
"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.
{ "mode": "append", "content": "Views dropped 40% week over week." }{ "mode": "replace", "content": "# Spring Launch\n\n...", "expected_readme_version": "9f2c4e1a7b3d5f60" }appendaddscontentas a new section at the end, followed by*Added by <key name> via API, YYYY-MM-DD*(via MCPfrom the MCP server). If someone edits the document at the same moment, the section is added on top of their edit.replaceoverwrites the whole document. If it changed since you readexpected_readme_version, nothing is written and the response is 409notes_conflictwithmeta.current_readme_version. An emptycontentclears 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 and Projects.
GET/api/v2/me#
Who the key acts as, and their personalization document.
{
"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.
{ "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. |