API Reference
Overview
The GrooveOS API lets you build on top of your team's shared memory: read and write memory items, search your team's knowledge, manage tasks and contacts, and query the entity graph — all programmatically. Every call carries your team scope; results never cross team boundaries.
Base URL: https://api.grooveos.app
This page documents the surface a developer on your team uses with an
xbt_ token. Internal endpoints (Drive OAuth callbacks, GitHub
webhooks, operator admin endpoints) are out of scope here — see the
open-source repo
for the full surface.
Authentication & headers
# All authenticated requests need:
Authorization: Bearer {your xbt_ token}
X-Team-Scope: {your team slug}
Mint an xbt_ token from your profile
Sign in with GitHub at grooveos.app/account/teams/, then go to Settings → API tokens to create a token. Tokens are user-scoped — they inherit your team memberships and your role per team. Treat your token like a password: never commit it to version control. See Authentication for the full sign-in flow.
Quick start
# Test your token
curl https://api.grooveos.app/v1/me \
-H "Authorization: Bearer $TOKEN"
# Search team memory
curl "https://api.grooveos.app/v1/memory/search?q=Q2+target" \
-H "Authorization: Bearer $TOKEN" \
-H "X-Team-Scope: your-team"
Health
GET /v1/healthz
No auth required. Useful for monitoring or warm-up checks.
curl https://api.grooveos.app/v1/healthz
# Response (200 OK):
{"status": "ok", "version": "1.0.0"}
Profile
GET /v1/me
Returns the authenticated user, their team memberships, and which teams they are admin of.
curl https://api.grooveos.app/v1/me \
-H "Authorization: Bearer $TOKEN"
# Response (200 OK):
{
"id": "a1b2c3d4-...",
"email": "alice@your-team.com",
"github_login": "alicedev",
"is_admin": false,
"teams": ["your-team", "another-team"]
}
GET /v1/me/github
Returns the GitHub identity attached to your account — login, GitHub user ID, avatar, and the org-membership check used by the auto-join flow.
curl https://api.grooveos.app/v1/me/github \
-H "Authorization: Bearer $TOKEN"
# Response:
{
"github_username": "alicedev",
"github_id": 8234567,
"github_avatar_url": "https://avatars.githubusercontent.com/u/8234567",
"is_org_member": true,
"org": "your-org"
}
Teams
GET /v1/teams
Lists every team the authenticated user belongs to, with their role per team.
curl https://api.grooveos.app/v1/teams \
-H "Authorization: Bearer $TOKEN"
# Response:
[
{"scope": "your-team", "name": "Your Team", "role": "admin"},
{"scope": "another-team", "name": "Another Team", "role": "member"}
]
Memory
Memory items implement the 7-field tagging contract. Every write must include:
team_scope, project_scope, visibility,
confidence, truth_level, source,
validation_status. Missing fields return HTTP 422.
POST /v1/memory/upsert
Create or update a memory item. If an item with the same source
already exists in the team, it is updated in place (vector + metadata).
curl -X POST https://api.grooveos.app/v1/memory/upsert \
-H "Authorization: Bearer $TOKEN" \
-H "X-Team-Scope: your-team" \
-H "Content-Type: application/json" \
-d '{
"item": {
"content": "The Q2 fundraising target is 2M€",
"team_scope": "your-team",
"project_scope": "fundraising",
"visibility": "team",
"confidence": 0.9,
"truth_level": "WORKING",
"source": "api:my-script",
"validation_status": "pending"
}
}'
# Response (201 Created):
{"id": "mem_abc123...", "team_scope": "your-team", "truth_level": "WORKING"}
truth_level cannot be PATCHed directly
You can set truth_level when first upserting an item. To promote
a fact to VALIDATED, CANONICAL, or PUBLIC
afterwards, use Truth-level promotions below — a
direct PATCH returns 405 by design.
GET /v1/memory/search
Semantic search across your team's memory. Filtered to your team scope automatically.
| Parameter | Type | Description |
|---|---|---|
q | string | Required. Natural-language query. |
project_scope | string | Filter by project slug. |
truth_level_min | string | Minimum truth level (default EPHEMERAL). |
limit | int | Results per page (default 10, max 100). |
curl "https://api.grooveos.app/v1/memory/search?q=fundraising+target&truth_level_min=WORKING&limit=5" \
-H "Authorization: Bearer $TOKEN" \
-H "X-Team-Scope: your-team"
# Response:
[
{
"id": "mem_abc123...",
"content": "The Q2 fundraising target is 2M€...",
"score": 0.947,
"team_scope": "your-team",
"project_scope": "fundraising",
"truth_level": "WORKING",
"confidence": 0.9,
"source": "librechat:conv_abc123:msg_7"
}
]
GET /v1/memory/{item_id}
Fetch a single memory item. Returns 404 if the item is not in your team's scope.
PATCH /v1/memory/{item_id}
Update mutable fields: content, project_scope,
visibility, confidence, validation_status,
metadata. truth_level is rejected with 405 — use
promotions instead.
curl -X PATCH https://api.grooveos.app/v1/memory/mem_abc123 \
-H "Authorization: Bearer $TOKEN" \
-H "X-Team-Scope: your-team" \
-H "Content-Type: application/json" \
-d '{"confidence": 0.95, "validation_status": "human_reviewed"}'
DELETE /v1/memory/{item_id}
Hard-delete a memory item (removes Postgres row + Qdrant vector + graph node). Returns 204 No Content. For soft delete with 30-day recovery, see Brain Monitor.
Conversations & messages
POST /v1/conversations
Create a conversation record. Useful when you want to attach memory items and messages to a thread you control.
curl -X POST https://api.grooveos.app/v1/conversations \
-H "Authorization: Bearer $TOKEN" \
-H "X-Team-Scope: your-team" \
-H "Content-Type: application/json" \
-d '{
"title": "Q2 Fundraising Strategy Session",
"project_scope": "fundraising",
"source": "api:my-script"
}'
# Response (201 Created):
{
"id": "conv_abc123",
"title": "Q2 Fundraising Strategy Session",
"team_scope": "your-team",
"project_scope": "fundraising"
}
GET /v1/conversations
List the team's conversations. Paginate with limit (default 20) and offset; filter by project_scope.
GET /v1/conversations/{id}
Get conversation metadata. Use GET /v1/messages?conversation_id= for the message list.
POST /v1/messages
Append a message to a conversation. If conversation_id doesn't
exist, it is created on the fly. Used by LibreChat and Open WebUI bridges, and
by your own integrations.
curl -X POST https://api.grooveos.app/v1/messages \
-H "Authorization: Bearer $TOKEN" \
-H "X-Team-Scope: your-team" \
-H "Content-Type: application/json" \
-d '{
"conversation_id": "conv_abc123",
"role": "user",
"content": "What is our fundraising target for Q2?",
"source": "api:my-script"
}'
Truth-level promotions
Truth-level promotion is the formal workflow for advancing a fact from
EPHEMERAL → WORKING → VALIDATED
→ CANONICAL → PUBLIC. Every promotion needs
a justification; promotions to VALIDATED and above require team
admin approval.
POST /v1/promotions
Submit a promotion request for a memory item.
curl -X POST https://api.grooveos.app/v1/promotions \
-H "Authorization: Bearer $TOKEN" \
-H "X-Team-Scope: your-team" \
-H "Content-Type: application/json" \
-d '{
"item_id": "mem_abc123",
"target_level": "VALIDATED",
"justification": "Confirmed in board meeting on 2026-05-01."
}'
# Response (202 Accepted):
{
"promotion_id": "promo_xyz",
"item_id": "mem_abc123",
"target_level": "VALIDATED",
"status": "pending"
}
GET /v1/promotions
List pending promotions for your team. Team-admin only; supports ?status= and ?item_id= filters.
PATCH /v1/promotions/{promotion_id}
Approve or reject a pending promotion. Team-admin only. Approval updates the item's truth_level atomically.
curl -X PATCH https://api.grooveos.app/v1/promotions/promo_xyz \
-H "Authorization: Bearer $TOKEN" \
-H "X-Team-Scope: your-team" \
-H "Content-Type: application/json" \
-d '{"decision": "approved", "note": "Verified in board minutes."}'
Brain Monitor
The Brain Monitor surface unifies seven entity types — memory_item,
granola_note, conversation, message,
team_message, task, contact — under one
feed. These endpoints power the Brain Monitor
page; you can call them yourself to build dashboards or polling loops.
GET /v1/brain/events
List entities for one team. Cursor-based pagination via since + cursor.
| Parameter | Type | Description |
|---|---|---|
entity_type | string? | Filter to one of the seven types. |
truth_level | string? | Filter on truth level. |
since | ISO 8601 | Only rows after this timestamp (polling). |
cursor | string? | Next-page cursor from the previous response. |
limit | int | Page size (default 50, max 200). |
include_deleted | boolean | Include soft-deleted rows. |
curl "https://api.grooveos.app/v1/brain/events?limit=20" \
-H "Authorization: Bearer $TOKEN" \
-H "X-Team-Scope: your-team"
# Response:
{
"items": [
{
"entity_type": "memory_item",
"entity_id": "mem_abc",
"team_scope": "your-team",
"truth_level": "WORKING",
"deleted_at": null,
"source": "librechat:conv_abc123",
"created_at": "2026-05-17T12:30:00Z",
"preview": "Q2 planning confirmed for May 15..."
}
],
"next_cursor": "eyJj..."
}
PATCH /v1/brain/events/{entity_type}/{entity_id}
Inline-update the truth_level on any entity. Only the row's author
or a team admin can call this; otherwise 403 with the locked wording "You
can only edit items you created. Contact a team admin to modify items created
by others."
curl -X PATCH "https://api.grooveos.app/v1/brain/events/memory_item/mem_abc" \
-H "Authorization: Bearer $TOKEN" \
-H "X-Team-Scope: your-team" \
-H "Content-Type: application/json" \
-d '{"truth_level": "VALIDATED"}'
DELETE /v1/brain/events/{entity_type}/{entity_id}
Soft-delete a row. The entity is hidden from feeds, searches, and AI context; it stays restorable for 30 days before being purged from Postgres, the vector store, and the entity graph.
curl -X DELETE "https://api.grooveos.app/v1/brain/events/task/task_xyz" \
-H "Authorization: Bearer $TOKEN" \
-H "X-Team-Scope: your-team"
# 204 No Content
POST /v1/brain/events/{entity_type}/{entity_id}/restore
Restore a soft-deleted row within the 30-day window.
curl -X POST "https://api.grooveos.app/v1/brain/events/task/task_xyz/restore" \
-H "Authorization: Bearer $TOKEN" \
-H "X-Team-Scope: your-team"
CRM
Contact directory, auto-populated from chats, meetings, and agents. Starter teams receive 403 on every CRM endpoint. See CRM for the user-facing guide.
GET /v1/crm/contacts
List the team's contacts.
curl "https://api.grooveos.app/v1/crm/contacts?limit=50&offset=0" \
-H "Authorization: Bearer $TOKEN" \
-H "X-Team-Scope: your-team"
POST /v1/crm/contacts
Create a new contact. Email must be unique within the team (409 on conflict — use PUT for upsert).
curl -X POST "https://api.grooveos.app/v1/crm/contacts" \
-H "Authorization: Bearer $TOKEN" \
-H "X-Team-Scope: your-team" \
-H "Content-Type: application/json" \
-d '{"email":"alice@example.com","name":"Alice Martin","company":"Acme","role":"CTO"}'
PUT /v1/crm/contacts
Upsert by (team_scope, email). Idempotent — safe to call repeatedly. New non-null fields are added; existing fields are not blanked out.
PATCH /v1/crm/contacts/{contact_id}
Update an existing contact's fields.
DELETE /v1/crm/contacts/{contact_id}
Delete a contact (soft-delete with 30-day recovery, same model as Brain Monitor).
Tasks
Action-item tracking, auto-populated from meetings and agents. See Tasks for the user-facing guide.
GET /v1/tasks
List tasks. Supports ?status=, ?project_scope=, ?since= (ISO 8601, for polling), and ?limit= / ?offset=.
# All open tasks
curl "https://api.grooveos.app/v1/tasks?status=todo" \
-H "Authorization: Bearer $TOKEN" \
-H "X-Team-Scope: your-team"
# Poll for new tasks since a timestamp
curl "https://api.grooveos.app/v1/tasks?since=2026-05-17T00:00:00Z" \
-H "Authorization: Bearer $TOKEN" \
-H "X-Team-Scope: your-team"
POST /v1/tasks
Create a task manually. created_by is set to the authenticated user. Assignee must be a member of the same team.
curl -X POST "https://api.grooveos.app/v1/tasks" \
-H "Authorization: Bearer $TOKEN" \
-H "X-Team-Scope: your-team" \
-H "Content-Type: application/json" \
-d '{
"title": "Send deck to investor",
"project_scope": "fundraising",
"due_date": "2026-05-14",
"visibility": "team",
"confidence": 1.0,
"truth_level": "WORKING",
"validation_status": "pending"
}'
GET /v1/tasks/{task_id}
Retrieve a single task by ID.
PATCH /v1/tasks/{task_id}
Update a task. Status changes produce a differentiated audit entry
(task.status_changed with from/to fields).
curl -X PATCH "https://api.grooveos.app/v1/tasks/task_xyz" \
-H "Authorization: Bearer $TOKEN" \
-H "X-Team-Scope: your-team" \
-H "Content-Type: application/json" \
-d '{"status": "done"}'
DELETE /v1/tasks/{task_id}
Delete a task (logged to audit trail).
Meetings (Granola)
Once your team admin has connected Granola (see Meetings), meeting notes flow into memory automatically. If you use another meeting tool, you can push notes directly with the endpoint below.
POST /v1/integrations/granola/ingest
Push one meeting note. Writes a memory item + contacts + tasks atomically.
Idempotent on note_id — pushing the same note twice is a no-op.
curl -X POST "https://api.grooveos.app/v1/integrations/granola/ingest" \
-H "Authorization: Bearer $TOKEN" \
-H "X-Team-Scope: your-team" \
-H "Content-Type: application/json" \
-d '{
"note_id": "ext_note_abc123",
"title": "Investor call — Series A",
"summary": "Discussed Q2 targets. Alice will send deck by Friday.",
"participants": [{"email":"alice@example.com","name":"Alice Martin"}],
"action_items": [{"title":"Send deck","due_date":"2026-05-22"}],
"created_at": "2026-05-17T10:00:00Z"
}'
Entity graph
GET /v1/graph/neighbors
Query the temporal entity graph for relationships connected to a given entity
in your team's scope. Each edge includes its validity window
(valid_from / valid_until).
| Parameter | Type | Description |
|---|---|---|
entity | string | Required. Entity name (person, project, concept). |
depth | int | Traversal depth, default 1 (max 3). |
curl "https://api.grooveos.app/v1/graph/neighbors?entity=Alice" \
-H "Authorization: Bearer $TOKEN" \
-H "X-Team-Scope: your-team"
# Response:
{
"entity": "Alice",
"team_scope": "your-team",
"neighbors": [
{
"name": "Acme.Engineering",
"relationship": "WAS_TECH_LEAD",
"valid_from": "2025-01-15",
"valid_until": "2025-05-31"
}
]
}
System prompt
GET /v1/system-prompt
Returns your team's CANONICAL facts pre-formatted as plain text,
ready to inject into an LLM system prompt. Use this so your own AI integrations
always know your team's ground truth.
curl https://api.grooveos.app/v1/system-prompt \
-H "Authorization: Bearer $TOKEN" \
-H "X-Team-Scope: your-team"
# Response (200 OK, text/plain):
Team knowledge (CANONICAL, your-team):
- Q2 fundraising target: 2M€ (confirmed 2026-05-01)
- Tech lead: Alice (since 2025-06-01)
- Current runway: 18 months (as of 2026-04-15)
Error codes
| Code | Meaning | Common causes |
|---|---|---|
400 | Bad Request | team_scope in payload doesn't match the X-Team-Scope header; malformed JSON. |
401 | Unauthorized | Missing token; expired or revoked xbt_; invalid signature. |
403 | Forbidden | Not a member of the requested team; non-admin attempting an admin-only action; Starter team calling a paid endpoint. |
404 | Not Found | Item, conversation, promotion, contact, or task does not exist in this team's scope. |
405 | Method Not Allowed | Attempted to PATCH truth_level directly — use POST /v1/promotions. |
409 | Conflict | Email already exists for the team (on POST /crm/contacts); duplicate integration registration. |
422 | Unprocessable Entity | Missing required tagging field on a memory item (one of the 7). |
429 | Too Many Requests | Rate limit exceeded — see below. |
Every error response includes a detail field with a human-readable message:
{"detail": "Missing required field: truth_level. All memory items must carry the 7 tagging contract fields."}
Rate limits
| Plan | Limit |
|---|---|
| Free | 60 requests / minute |
| Team | 300 requests / minute |
| Enterprise | Custom limits |
Building an agent or automation?
Background agents that continuously write memory items can hit the 300/min ceiling. Reach out at team@grooveos.app to discuss Enterprise rate limits.
SDK examples
Official SDKs are coming soon. Until then, the REST API works cleanly with any HTTP client.
Python
import requests
BASE = "https://api.grooveos.app"
TOKEN = "xbt_your_token_here"
TEAM = "your-team"
def search_memory(query: str):
response = requests.get(
f"{BASE}/v1/memory/search",
params={"q": query, "truth_level_min": "WORKING"},
headers={
"Authorization": f"Bearer {TOKEN}",
"X-Team-Scope": TEAM,
},
)
response.raise_for_status()
return response.json()
results = search_memory("Q2 target")
JavaScript / TypeScript
const BASE = "https://api.grooveos.app";
const TOKEN = "xbt_your_token_here";
const TEAM = "your-team";
async function searchMemory(query) {
const url = new URL(`${BASE}/v1/memory/search`);
url.searchParams.set("q", query);
url.searchParams.set("truth_level_min", "WORKING");
const res = await fetch(url, {
headers: {
"Authorization": `Bearer ${TOKEN}`,
"X-Team-Scope": TEAM,
},
});
if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
return res.json();
}
const results = await searchMemory("Q2 target");
What's next
- Memory & Knowledge — Understand truth levels and how promotion works.
- Brain Monitor — The UI that sits on top of the Brain Monitor endpoints.
- CRM · Tasks · Meetings — Higher-level surfaces of these endpoints.