API Reference
Programmatic access to workspaces and memories. Organize, version, and serve project context to any LLM via REST.
Authentication
All API requests require a Bearer token in the Authorization header.
Content-Type: application/json. Responses use the same content type.
Read-Only Token
Access GET endpoints only. Perfect for AI tools that consume context.
Full Access Token
Access all endpoints — GET, POST, PATCH, DELETE. For management and automation.
Rate Limiting
API requests are limited to 100 requests per minute per token.
{
"error": {
"code": "RATE_LIMITED",
"message": "Rate limit exceeded. Please try again later.",
"status": 429
}
}Error Handling
All errors follow a consistent JSON structure with a code, message, and HTTP status.
{
"error": {
"code": "ERROR_CODE",
"message": "Human-readable description",
"details": { ... }, // optional, for validation errors
"status": 4xx
}
}| Status | Code | Description |
|---|---|---|
401 | UNAUTHORIZED | Invalid or missing access token |
403 | FORBIDDEN | Insufficient permissions or inactive workspace |
404 | NOT_FOUND | Resource not found |
422 | VALIDATION_ERROR | Invalid request data (includes details) |
429 | RATE_LIMITED | Rate limit exceeded |
Workspaces
List Workspaces
Returns all active workspaces for the current account.
| Name | Type | Description |
|---|---|---|
| page | integer | Page number (default: 1) |
[
{
"id": 1,
"name": "Project Alpha",
"description": "Main project workspace",
"memories_count": 42,
"archived": false,
"created_at": "2026-01-15T10:30:00Z",
"updated_at": "2026-02-04T14:22:00Z",
"url": "https://recuerd0.ai/workspaces/1"
}
]X-Page, X-Per-Page, X-Total, X-Total-Pages, and Link are included in all list responses.
Get Workspace
Returns a single workspace with memory count and pinned status.
{
"id": 1,
"name": "Project Alpha",
"description": "Main project workspace",
"memories_count": 42,
"archived": false,
"created_at": "2026-01-15T10:30:00Z",
"updated_at": "2026-02-04T14:22:00Z",
"url": "https://recuerd0.ai/workspaces/1"
}Get Workspace Context
Returns a compact "wake-up" snapshot of a workspace for AI agents — workspace metadata, a small set of orienting memories, and stats. When the requesting user has no pinned memories in the workspace, the endpoint returns the most recently updated memories instead of an empty list; context_source reports pins or recent. Supports HTTP caching via ETag.
| Name | Type | Default | Description |
|---|---|---|---|
| limit | integer | 10 | Maximum memories to return (1–50). |
| include_body | boolean | true | Whether to include each memory's body content. |
| max_body_chars | integer | 500 | Maximum characters of body to return per memory (100–5000). Longer bodies are truncated. |
| category | string | Filter memories to one category. See Memory Categories. |
{
"workspace": {
"id": 1,
"name": "Project Alpha",
"description": "Main project workspace",
"memories_count": 42,
"state": "active",
"updated_at": "2026-04-01T12:00:00Z",
"url": "https://recuerd0.ai/workspaces/1"
},
"memories": [
{
"id": 17,
"title": "Architecture Notes",
"source": "manual",
"category": "decision",
"tags": ["design", "core"],
"pinned_at": "2026-03-28T09:14:00Z",
"updated_at": "2026-04-01T11:42:00Z",
"url": "https://recuerd0.ai/workspaces/1/memories/17",
"body": "# Architecture\n\nThe system is split into…",
"body_truncated": true,
"links_count": 3
}
],
"context_source": "pins",
"stats": {
"total_memories": 42,
"total_pinned": 3,
"returned": 1,
"returned_pinned": 1
},
"generated_at": "2026-04-06T10:00:00Z"
}
The pinned_memories array and stats.returned_pinned remain as deprecated aliases of memories and stats.returned.
Create Workspace
Creates a new workspace.
| Name | Type | Description | |
|---|---|---|---|
| workspace[name] | string | required | Workspace name (max 100 characters) |
| workspace[description] | string | Workspace description |
{
"workspace": {
"name": "New Project",
"description": "A new workspace for the team"
}
}{
"id": 2,
"name": "New Project",
"description": "A new workspace for the team",
"memories_count": 0,
"archived": false,
"created_at": "2026-02-04T15:00:00Z",
"updated_at": "2026-02-04T15:00:00Z",
"url": "https://recuerd0.ai/workspaces/2"
}Update Workspace
Updates an existing workspace.
| Name | Type | Description |
|---|---|---|
| workspace[name] | string | Workspace name |
| workspace[description] | string | Workspace description |
Archive Workspace
Archives a workspace. Archived workspaces become read-only.
Unarchive Workspace
Restores an archived workspace.
Memories
Memory Categories
Every memory carries exactly one category from a locked four-value enum. Category defaults to general and is used to filter list, search, and workspace context endpoints. Versions inherit the parent memory's category unless overridden.
| Value | Label | Purpose |
|---|---|---|
decision | Decision | Architecture choices, library picks, tradeoffs with stated reasoning |
discovery | Discovery | Non-obvious findings — gotchas, root causes, patterns, library quirks |
preference | Preference | User-stated rules ("always X", "never Y") |
general | General | Catch-all and default |
category query parameter is supported on list, search, and workspace context endpoints to filter results to a single category.
Memory Links
Memory links — also called "tunnels" — are undirected, unlabeled "see also" connections between two memories within the same account. They cross workspace boundaries: a memory in your rails-app workspace can link to a memory in your mobile-app workspace. Use them to express that two memories cover the same topic without duplicating content.
| Method | Path | Description |
|---|---|---|
GET | /workspaces/:workspace_id/memories/:memory_id/links.json | List linked memories with their workspace info |
POST | /workspaces/:workspace_id/memories/:memory_id/links.json | Create a link to another memory by id |
DELETE | /workspaces/:workspace_id/memories/:memory_id/links/:id.json | Remove a link. :id is the other memory's id, not a join row id |
422. Storage is undirected: linking A↔B is the same as B↔A and the server normalizes the pair so the link is only created once. There is no cardinality limit, links carry no labels or metadata, and deleting a memory cascades to remove all of its links. The DELETE endpoint identifies the link by the other memory's id, not a join row id. Memory and pinned-memory JSON responses include a links_count field showing how many links a memory has.
List Memory Links
Returns all memories linked to a given memory, with each linked memory's workspace embedded.
[
{
"id": 118,
"title": "Auth strategy (mobile)",
"category": "decision",
"tags": ["auth", "mobile"],
"source": "manual",
"updated_at": "2026-03-22T11:05:00Z",
"url": "https://recuerd0.ai/workspaces/2/memories/118",
"workspace": {
"id": 2,
"name": "mobile-app",
"url": "https://recuerd0.ai/workspaces/2"
}
},
{
"id": 204,
"title": "Auth migration runbook",
"category": "discovery",
"tags": ["auth", "infra"],
"source": "manual",
"updated_at": "2026-04-02T08:30:00Z",
"url": "https://recuerd0.ai/workspaces/5/memories/204",
"workspace": {
"id": 5,
"name": "infra",
"url": "https://recuerd0.ai/workspaces/5"
}
}
]Create Memory Link
Creates an undirected link between this memory and another memory in the same account.
| Name | Type | Description | |
|---|---|---|---|
| to_memory_id | integer | required | The id of the other memory to link to. Must belong to the same account. |
{
"to_memory_id": 118
}{
"id": 118,
"title": "Auth strategy (mobile)",
"category": "decision",
"tags": ["auth", "mobile"],
"source": "manual",
"updated_at": "2026-03-22T11:05:00Z",
"url": "https://recuerd0.ai/workspaces/2/memories/118",
"workspace": {
"id": 2,
"name": "mobile-app",
"url": "https://recuerd0.ai/workspaces/2"
}
}422 if to_memory_id belongs to a different account, references the same memory, or the link already exists.
Delete Memory Link
Removes a link between two memories. The path :id is the other memory's id — not a separate join row id.
List Memories
Returns all memories (latest versions only) for a workspace. Supports filtering by title pattern, tags, and source.
| Name | Type | Description |
|---|---|---|
| page | integer | Page number (default: 1) |
| per_page | integer | Items per page (1–100, default: 25) |
| title | string | Glob pattern for title matching. * matches any characters, ? matches one |
| tags | string | Comma-separated tag list. Returns memories with ALL specified tags |
| source | string | Exact match on source field |
| category | string | Filter to one category. See Memory Categories. |
| sort | string | Sort field: updated_at (default), created_at, title |
| direction | string | Sort direction: desc (default), asc |
GET /workspaces/1/memories.json?title=Meeting*
GET /workspaces/1/memories.json?tags=api,design&sort=title&direction=asc
GET /workspaces/1/memories.json?category=decision&per_page=50[
{
"id": 1,
"title": "Meeting Notes",
"version": 1,
"source": "manual",
"category": "decision",
"tags": ["meetings", "q1"],
"created_at": "2026-01-20T09:00:00Z",
"updated_at": "2026-02-03T16:45:00Z",
"url": "https://recuerd0.ai/workspaces/1/memories/1"
}
]Browse Memories
Returns memories across all active workspaces for the current account. Supports the same filtering parameters as List Memories. JSON-only endpoint.
| Name | Type | Description |
|---|---|---|
| page | integer | Page number (default: 1) |
| per_page | integer | Items per page (1–100, default: 25) |
| workspace_id | integer | Filter to a specific workspace |
| title | string | Glob pattern for title matching. * matches any characters, ? matches one |
| tags | string | Comma-separated tag list. Returns memories with ALL specified tags |
| source | string | Exact match on source field |
| category | string | Filter to one category. See Memory Categories. |
| sort | string | Sort field: updated_at (default), created_at, title |
| direction | string | Sort direction: desc (default), asc |
GET /memories.json?title=*architecture*
GET /memories.json?tags=api,design&sort=title&direction=asc
GET /memories.json?workspace_id=1&source=manualGet Memory
Returns a memory with its content. Supports line range parameters to read specific portions.
| Name | Type | Description |
|---|---|---|
| line_start | integer | First line to return (1-based, inclusive). Default: 1 |
| line_end | integer | Last line to return (1-based, inclusive). Default: last line |
GET /workspaces/1/memories/1.json
GET /workspaces/1/memories/1.json?line_start=1&line_end=20
GET /workspaces/1/memories/1.json?line_start=50&line_end=75{
"id": 1,
"title": "Meeting Notes",
"version": 1,
"source": "manual",
"category": "decision",
"tags": ["meetings", "q1"],
"created_at": "2026-01-20T09:00:00Z",
"updated_at": "2026-02-03T16:45:00Z",
"url": "https://recuerd0.ai/workspaces/1/memories/1",
"content": {
"body": "# Meeting Notes\n\nDiscussed Q1 goals...",
"total_lines": 142,
"line_start": 1,
"line_end": 142
},
"links_count": 3,
"workspace": {
"id": 1,
"name": "Project Alpha",
"url": "https://recuerd0.ai/workspaces/1"
}
}content object always includes total_lines, line_start, and line_end. When line range parameters are provided, body contains only the requested lines. Returns 422 if line_start > line_end.
| Name | Type | Description |
|---|---|---|
| mode | string | Set to grep to search within the memory |
| q | string | Search query required |
| context | integer | Lines before and after each match (0–10, default: 0) |
| before | integer | Lines before match, overrides context (0–10) |
| after | integer | Lines after match, overrides context (0–10) |
GET /workspaces/1/memories/1.json?mode=grep&q=architecture
GET /workspaces/1/memories/1.json?mode=grep&q=design&context=2{
"id": 1,
"title": "Meeting Notes",
...
"content": {
"total_lines": 142,
"matches": [
{
"line_number": 12,
"line": "Discussed architecture decisions.",
"context_before": ["## Design"],
"context_after": ["The team agreed on microservices."]
}
]
}
}content object returns matches and total_lines instead of body. Returns 422 if q parameter is missing.
For large memories, prefer a two-step read: call grep mode first to find each match's line_number, then call again with line_start/line_end to fetch a window of surrounding context.
GET /workspaces/1/memories/1.json?mode=grep&q=TODO{
"content": {
"total_lines": 412,
"matches": [
{ "line_number": 47, "line": "TODO: revisit caching" }
]
}
}GET /workspaces/1/memories/1.json?line_start=40&line_end=55head= or tail= parameter. Head is line_start=1&line_end=N; tail is line_start=(total_lines - N + 1)&line_end=total_lines. Because total_lines is echoed on every response, clients can compute tail without an extra round trip.
Create Memory
Creates a new memory with content.
| Name | Type | Description |
|---|---|---|
| memory[title] | string | Memory title (max 255 characters) |
| memory[content] | string | Memory body (Markdown) |
| memory[source] | string | Source identifier |
| memory[tags] | array | Array of tag strings |
| memory[category] | string | One of decision, discovery, preference, general (defaults to general). See Memory Categories. |
{
"memory": {
"title": "API Documentation",
"content": "# Overview\n\nThis document describes...",
"category": "decision",
"tags": ["docs", "api"]
}
}Update Memory
Updates an existing memory.
| Name | Type | Description |
|---|---|---|
| memory[title] | string | Memory title |
| memory[content] | string | Memory body |
| memory[source] | string | Source identifier |
| memory[tags] | array | Array of tags |
| memory[category] | string | One of decision, discovery, preference, general. See Memory Categories. |
Delete Memory
Deletes a memory and all its versions.
Memory Versions
Create Version
Creates a new version of a memory. Fields default to the parent version values if not provided.
| Name | Type | Description |
|---|---|---|
| version[title] | string | Version title (defaults to parent) |
| version[content] | string | Version body (defaults to parent) |
| version[source] | string | Source identifier (defaults to parent) |
| version[tags] | array | Tags (defaults to parent) |
| version[category] | string | Category (defaults to the parent memory's category). See Memory Categories. |
{
"version": {
"content": "# Updated Content\n\nRevised version..."
}
}{
"id": 5,
"title": "Meeting Notes",
"version": 2,
"source": "manual",
"category": "decision",
"tags": ["meetings", "q1"],
"created_at": "2026-02-04T16:00:00Z",
"updated_at": "2026-02-04T16:00:00Z",
"url": "https://recuerd0.ai/workspaces/1/memories/5",
"content": {
"body": "# Updated Content\n\nRevised version..."
},
"workspace": {
"id": 1,
"name": "Project Alpha",
"url": "https://recuerd0.ai/workspaces/1"
}
}Search
REST Search Memories
Full-text search across all memories in active workspaces. This REST endpoint accepts raw FTS5 query operators, requires at least three characters, and does not union tag matches.
| Name | Type | Description | |
|---|---|---|---|
| q | string | required | Search query (3–100 characters) |
| page | integer | Page number (default: 1) | |
| workspace_id | integer | Filter results to a specific workspace | |
| category | string | Filter results to one category. See Memory Categories. | |
| mode | string | Response mode: snippet (default) or grep | |
| context | integer | Lines before and after each match, like grep -C (0–10, default: 0). Only with mode=grep | |
| before | integer | Lines before each match, like grep -B (0–10). Overrides context. Only with mode=grep | |
| after | integer | Lines after each match, like grep -A (0–10). Overrides context. Only with mode=grep |
| Operator | Example | Description |
|---|---|---|
| Term | architecture | Matches documents containing the substring |
| AND | architecture AND design | Both terms must appear |
| OR | meeting OR standup | Either term can appear |
| NOT | design NOT draft | First term must appear, second must not |
| Phrase | "project timeline" | Exact phrase match |
| Column | title:architecture | Search only in title field |
| Column | body:implementation | Search only in body field |
| Grouping | (meeting OR standup) AND notes | Parentheses for precedence |
release notes is a raw multi-term FTS5 query here. MCP and workspace search treat the same input as an exact phrase or exact tag. The paths differ by design: raw power for API clients, safe defaults for agents and the UI.
{
"query": "architecture AND design",
"total_results": 3,
"results": [
{
"id": 1,
"title": "Design Doc",
"version": 1,
"version_label": "v1",
"has_versions": false,
"category": "decision",
"tags": ["design"],
"source": "manual",
"snippet": "Initial architecture overview...",
"workspace": { ... }
}
]
}GET /search.json?q=architecture&mode=grep&context=1
{
"query": "architecture",
"total_results": 2,
"results": [
{
"id": 1,
"title": "Design Doc",
...
"total_lines": 45,
"matches": [
{
"line_number": 12,
"line": "The architecture uses a layered approach.",
"context_before": ["## System Design"],
"context_after": ["Each layer communicates through interfaces."]
}
],
"workspace": { ... }
}
]
}422 with VALIDATION_ERROR.
matches and total_lines instead of snippet. FTS5 finds candidate memories, then line-level matching extracts specific lines with context.