memaxdocs
REST API

Memories API

Create, read, update, and delete memories via the REST API.

Create a memory

POST /v1/memories

Request body

Send the target hub in the X-Hub-ID request header (or omit for the caller's personal hub):

POST /v1/memories
Content-Type: application/json
Authorization: Bearer mxk_...
X-Hub-ID: hub_abc
{
  "content": "The auth system uses JWT with short-lived access tokens...",
  "title": "Auth architecture",
  "hint": "architecture documentation for the auth system",
  "tags": ["auth", "jwt", "security"],
  "hub_reason": "shared across the platform team",
  "source_agent": "claude-code",
  "project_context": { "repo": "github.com/memaxlabs/memax", "branch": "main" }
}
FieldTypeRequiredDescription
contentstringyesThe memory content (markdown / text / code / structured).
titlestringAuto-derived if omitted.
hintstringContext hint for AI processing (improves classification).
kindstringOverride auto-classification: episodic, semantic, procedural, rationale.
stabilitystringOverride auto-classification: volatile, evolving, stable.
tagsstring[]Tags.
content_typestringtext, markdown, code, chat, transcript, file, structured. Auto-inferred from extension.
sourcestringOrigin: cli, import, hook, web, api, sdk, mcp, extraction.
source_agentstringAgent slug (claude-code, cursor, etc.).
assisted_by_agentstringAgent that helped (for human-authored pushes).
initiation_typestringhuman_direct, human_requested_agent, agent_proactive, agent_automatic, import, unknown.
source_pathstringRelative path (used for dedup when re-ingesting).
hub_reasonstringWhy this belongs in the shared hub. Accepted by the model but not enforced — the server does not reject team-hub pushes that omit it.
project_contextobject{ repo, project, branch }.
file_refobjectAttachment reference from POST /v1/uploads.

Target hub is set via the X-Hub-ID request header, not a body field. The PushRequest struct has no hub_id field — the server resolves the target hub from the header (and falls back to the caller's personal hub when absent).

There is no category field. Use kind/stability for classification or tags for filterable grouping.

The boundary enum is defined (private | hub | team | org) but the server always writes boundary = "private" on create — see Access & visibility. Don't rely on boundary for access control; it's hub-membership-based.

Response

{
  "data": {
    "id": "mem_a1b2c3d4",
    "hub_id": "hub_abc",
    "owner_id": "usr_xyz",
    "title": "Auth architecture",
    "content_type": "markdown",
    "content_hash": "...",
    "summary": "…",
    "tags": ["auth", "jwt", "security"],
    "kind": "semantic",
    "stability": "evolving",
    "boundary": "private",
    "state": "active",
    "source": "api",
    "version": 1,
    "created_at": "2026-04-21T10:30:00Z"
  }
}

state on the response depends on how the memory was ingested:

  • Inline text / markdown (the normal memax push / REST POST /v1/memories path) returns with state = "active" immediately — the row is queryable as soon as the response lands. Classification, chunking, and embedding still run asynchronously after the response in the background processor; the memory becomes fully recallable (and its AI-derived fields like summary, kind, stability populate) within a few seconds.
  • File attachments (file_ref — PDFs, images) and URL links (content_type=link) start at state = "processing" and transition to active only when the worker has finished extraction + embedding (typically under 5 seconds; can be longer for large PDFs).

List memories

GET /v1/memories?limit=20&cursor=<opaque>&sort=newest

Query parameters

ParameterTypeDescriptionDefault
limitnumberPage size.20
cursorstringPagination cursor (opaque).
sortstringnewest (by created_at desc) or relevant (by access_count desc, tiebroken by created_at desc).newest
hub_idstringScope to a specific hub (ID, not slug).all hubs
topic_idstringScope to memories in a topic.

There is no category, tags, or boundary query filter on this endpoint today. Use POST /v1/recall for semantic filtering.

Response

{
  "data": {
    "memories": [
      /* Memory[] */
    ],
    "next_cursor": "eyJpZCI6...",
    "has_more": true,
    "total": 2457
  }
}

Get a memory

GET /v1/memories/{id}

Returns a full Memory object (same shape as the Create response, plus content and any attached attachments[]).

GET /v1/memories/{id}/related

Returns memories the server considers related to the one at id (used by the web app's memory detail "Related" section).

Update a memory

PATCH /v1/memories/{id}

Partial update. Include only fields you want to change.

{
  "title": "Updated auth architecture",
  "tags": ["auth", "jwt"]
}

Delete a memory

DELETE /v1/memories/{id}

Hard delete — the memory row, its chunks, and any attachments are removed. Not recoverable. Returns the standard { "data": { ... } } envelope (not 204).

Soft-archive is a separate state (state = archived) used by lifecycle / TTL flows; it is not what DELETE /v1/memories/{id} does.

Batch delete:

POST /v1/memories/batch-delete

with { "ids": ["mem_a", "mem_b"] }.

Move memories between topics / hubs

POST /v1/memories/batch-move
{
  "ids": ["mem_a"],
  "hub_id": "hub_target",
  "topic_id": "top_xyz"
}

Attachments

Use POST /v1/uploads to get a presigned upload URL, PUT the file directly to object storage, then pass the resulting file_ref in POST /v1/memories. Download via GET /v1/memories/{id}/attachments/{attachmentID}/download.