# truememory docs > Truememory Agent Service — persistent memory, personas, and an OpenAI-compatible AI Gateway. Index: https://truememory-docs.mvstg.com/llms.txt # Agents Source: https://truememory-docs.mvstg.com/ai-gateway/agents/ · Markdown: https://truememory-docs.mvstg.com/ai-gateway/agents/index.md An **agent** is a server-side configuration that decides how Truememory handles a chat request: which system prompt pipeline runs, which server-side tools are available, whether memory is read and written, and whether safety checks apply. You pick one per request with the `X-Agent-Id` header; if you never send the header, you get the general-purpose `chatbot` and can ignore this page entirely. :::note[Agents are not personas] An agent is *infrastructure* (a processing pipeline, fixed per instance). Your user's named AI companion — its name, personality, avatar — is a [persona](/ai-gateway/personas/), which is *data* the agent loads at request time. "The user's agent" in app language means their persona. ::: All requests require a Bearer token from [Truememory Auth](/authentication/overview/). ## Selecting an Agent Pass the `X-Agent-Id` header: ```bash curl -X POST https://your-tie-host/v1/chat/completions \ -H "X-Agent-Id: research-assistant" \ -H "Authorization: Bearer $TOKEN" \ -d '{"model": "anthropic/claude-sonnet-4-5", "messages": [{"role": "user", "content": "Hello"}]}' ``` If omitted, the default agent (`chatbot`) is used. ## Available Agents | Agent | Description | Memory | Safety | |-------|-------------|--------|--------| | `chatbot` | General-purpose chatbot | shared | none | | `onboarding` | Unified identity field collection — runs before memory is established | none | none | | `eliza` | AI wellness coach — nutrition guidance, meal logging, supplement tracking | shared | none | | `research-assistant` | Web search and calculator | shared | LlamaGuard | | `rag-assistant` | Database search (RAG) | isolated | LlamaGuard | | `command-agent` | Command execution | none | none | | `bg-task-agent` | Background task processing | none | none | - **Memory: shared** — memories are accessible across all agents that use the shared pool. - **Memory: isolated** — memories are scoped to that specific agent. See [Memory](/memory/overview/) for details. - **Safety: LlamaGuard** — user inputs and agent outputs are checked against Meta's Llama Guard content safety classifier before processing. Requests flagged as unsafe are rejected. Memory scope is configured per agent — clients cannot override it. All agents support client-side tool calling via the `tools` parameter. ### Onboarding Agent A unified identity-collection agent that runs the same field set across all apps. Use it to collect a user's identity fields through a natural one-question-at-a-time conversation, before the user has memory established. See the full guide at [Platform → Onboarding](/platform/onboarding/). **What it collects (unified field set):** | Field ID | Default Question | |----------|-----------------| | `preferred_name` | "What should I call you?" | | `date_of_birth` | "When were you born?" | | `location` | "Where in the world are you based?" | Apps can override the question text for any unified field and append their own app-specific questions — all via `skill_instructions`. An app with its own flow can also skip the unified fields it doesn't list (`include_unified_fields: false`), add its own `rules`, and drop the final confirmation turn (`confirm_before_complete: false`) — see [Asking only your own questions](/platform/onboarding/#asking-only-your-own-questions). **`skill_instructions` format (JSON):** ```json { "acknowledge_source_app": true, "questions": [ { "id": "preferred_name", "question": "What should we call you?" }, { "id": "location", "question": "Where in the world are you based?" }, { "id": "currentFocus", "question": "What are you focused on right now?" } ], "already_captured": { "preferred_name": "Julio" } } ``` | Field | Description | |-------|-------------| | `questions` | App's question list. Unified field IDs override the default text; unknown IDs become app-specific questions appended after unified fields | | `already_captured` | Fields already collected in a previous session (for resume). Truememory skips these | | `acknowledge_source_app` | When `true`, the agent greets the user with "I already know a bit about you from [previous app]" if prior data exists | **Merge rules:** Truememory always collects all unified fields — apps cannot drop them. Apps control question wording and order. App-specific questions always appear after unified fields. **Observable events (in SSE stream):** The onboarding agent emits four observable tool calls through OpenAI-compatible streaming chunks (same shape as any chat-completions tool call): After each field is answered — appears inside a `delta.tool_calls` chunk: ```json {"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"id":"call_abc","type":"function","function":{"name":"capture_field","arguments":"{\"field_id\":\"location\",\"value\":\"Asia/Kuala_Lumpur\"}"}}]}}]} ``` When the user explicitly opts out of an optional question — store as an opt-out signal: ```json {"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"id":"call_skp","type":"function","function":{"name":"skip_field","arguments":"{\"field_id\":\"currentFocus\",\"reason\":\"privacy\"}"}}]}}]} ``` When all fields are collected — terminal success event: ```json {"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"id":"call_xyz","type":"function","function":{"name":"onboarding_complete","arguments":"{\"fields\":{\"preferred_name\":\"Julio\",\"date_of_birth\":\"1995-05-23\",\"location\":\"Kuala Lumpur\"}}"}}]}}]} ``` User stopped early OR safety cap fired — terminal partial event: ```json {"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"id":"call_abr","type":"function","function":{"name":"onboarding_aborted","arguments":"{\"fields\":{\"preferred_name\":\"Julio\"},\"reason\":\"user_stopped\"}"}}]}}]} ``` - `capture_field` — per-field progress event for live UI feedback (checkmarks, progress dots). Values may still be corrected by the user, **so don't persist on this event**. - `skip_field` — fires when the user opts out of an optional question. The field will be absent from the final `onboarding_complete` payload. Persist as an opt-out flag if you need to remember the user's choice. - `onboarding_complete` — terminal success event. **Only persist on this event.** - `onboarding_aborted` — terminal partial event (user opted out, malformed config, or 20-turn safety cap). Treat as partial data; route to a fallback rather than auto-persisting. Inspect `reason` to distinguish — `"max_turns_exceeded (...)"` indicates the safety cap fired. After `onboarding_complete`, your app shows the user a confirmation screen. On confirm, write to auth metadata — split by ownership: - **Unified fields** (`preferred_name`, `date_of_birth`, `location`) → `PATCH /auth/me` (with `metadata` field) - **App-specific fields** (anything else) → `PATCH /auth/me/app-metadata?app=your_app` Both endpoints take the user's Truememory Bearer token — the same `Authorization: Bearer $TOKEN` you use for chat completions. See [Platform → Onboarding](/platform/onboarding/#after-confirmation) for the full payload shapes. **Example request:** ```bash curl -X POST https://your-tie-host/v1/chat/completions \ -H "X-Agent-Id: onboarding" \ -H "Authorization: Bearer $TOKEN" \ -d '{ "model": "anthropic/claude-sonnet-4-6", "messages": [{"role": "user", "content": "Hi"}], "stream": true, "persona_id": "your-companion-persona-id", "skill_instructions": "{\"questions\": [{\"id\": \"location\", \"question\": \"Where in the world are you?\"}], \"already_captured\": {}}" }' ``` > The onboarding agent has **no memory** — it runs before the user's memory graph is established. Confirmed field values should be written by your app backend to auth metadata after the user confirms on the confirmation screen. --- ### Eliza (Wellness Coach) App-specific agent for the Vibrantly app: evidence-based nutrition guidance with meal and supplement logging via **client-side tools** (your app defines them in `tools` and executes the returned `tool_calls` — see [Tool calling](/ai-gateway/features/tool-calling/)). Other apps should not select this agent; its behavior is owned by Vibrantly. --- ## Message Compression Moved — compression is a per-request chat feature, not an agent concern. See [Message compression](/ai-gateway/features/message-compression/). --- Query `GET /info` to see all agents on your instance: ```json { "agents": [ { "key": "chatbot", "description": "A simple chatbot.", "memory_enabled": true, "memory_scope": "shared" }, { "key": "eliza", "description": "An AI wellness coach for the Vibrantly app — nutrition guidance, meal logging, and supplement tracking.", "memory_enabled": true, "memory_scope": "shared" }, { "key": "research-assistant", "description": "A research assistant with web search and calculator.", "memory_enabled": true, "memory_scope": "shared" } ], "default_agent": "chatbot" } ``` # Audio (Speech & Transcription) Source: https://truememory-docs.mvstg.com/ai-gateway/audio/ · Markdown: https://truememory-docs.mvstg.com/ai-gateway/audio/index.md Truememory provides OpenAI-compatible audio endpoints for speech-to-text (transcription) and text-to-speech (TTS). These are drop-in replacements for OpenAI's `/v1/audio/transcriptions` and `/v1/audio/speech` — any client that works with OpenAI's audio API works with Truememory by changing the base URL. :::note[Model ID format on this endpoint] Audio endpoints accept bare OpenAI IDs (`whisper-1`, `gpt-4o-mini-tts`) and `google/`-prefixed Gemini IDs (`google/gemini-2.5-flash`). Note this differs from chat, where Gemini uses the `vertexai/` prefix — see [Model ID formats](/ai-gateway/overview/#model-id-formats). ::: :::tip[Looking for Gemini-native voice?] This page covers the OpenAI-compatible file-based endpoints. For real-time voice conversations, Gemini TTS, and WebSocket STT — see [Real-Time Voice](/ai-gateway/voice/). ::: All requests require a Bearer token from [Truememory Auth](/authentication/overview/). Both `/v1/audio/transcriptions` and `/v1/audio/speech` accept tri-mode auth — see [Ways to authenticate](/authentication/overview/): | Caller | Method | |--------|--------| | Browser / mobile app | User token: `Authorization: Bearer ` | | Backend service (BlinkLife / Vibrantly) | Service key on user's behalf: `Authorization: Bearer $AUTH_SECRET` + `X-On-Behalf-Of: ` | | Backend service (stateless) | Service key only: `Authorization: Bearer $AUTH_SECRET` | The audio endpoints are stateless (no per-user memory or thread writes), so the service-only mode is allowed for both — unlike the WebSocket voice endpoints, which require `X-On-Behalf-Of` because they touch user state. ## Speech-to-Text (Transcription) Convert audio to text. Truememory proxies to OpenAI Whisper and returns the transcript. ### Endpoint ``` POST /v1/audio/transcriptions ``` ### Parameters Send as `multipart/form-data`: | Parameter | Type | Default | Description | |-----------|------|---------|-------------| | `file` | file | required | Audio file (mp3, mp4, mpeg, mpga, m4a, wav, webm) | | `model` | string | `whisper-1` | Transcription model. Use `google/gemini-2.5-flash` for Gemini-powered transcription. Files capped at 25 MB. | | `language` | string | null | ISO 639-1 language code (e.g. `en`). Improves accuracy if known. | | `prompt` | string | null | Guide the model's style or continue a previous segment | | `response_format` | string | `json` | Output format: `json`, `text`, `srt`, `verbose_json`, `vtt` | | `temperature` | float | 0.0 | Sampling temperature (0–1). Lower is more deterministic. | ### Example ```bash curl -X POST https://your-tie-host/v1/audio/transcriptions \ -H "Authorization: Bearer $TOKEN" \ -F file=@recording.webm \ -F model=whisper-1 ``` ### Response Default (`json` format): ```json { "text": "Hello, I'd like to log my breakfast. I had oatmeal with blueberries." } ``` With `response_format=verbose_json`, the response includes timestamps and segment-level detail. ### AI SDK Integration The [Vercel AI SDK](https://sdk.vercel.ai/docs) supports transcription via `experimental_transcribe`. Point the OpenAI provider at your Truememory instance: ```typescript import { createOpenAI } from "@ai-sdk/openai"; import { experimental_transcribe as transcribe } from "ai"; const tie = createOpenAI({ baseURL: "https://your-tie-host/v1", apiKey: "$TOKEN", }); const result = await transcribe({ model: tie.transcription("whisper-1"), audio: audioBuffer, // Uint8Array, Buffer, base64 string, or URL }); console.log(result.text); // "Hello, I'd like to log my breakfast." ``` --- ## Text-to-Speech Convert text to spoken audio. Truememory proxies to OpenAI's TTS models and streams audio bytes back. ### Endpoint ``` POST /v1/audio/speech ``` ### Parameters Send as JSON: | Parameter | Type | Default | Description | |-----------|------|---------|-------------| | `model` | string | `gpt-4o-mini-tts` | TTS model (`gpt-4o-mini-tts`, `tts-1`, `tts-1-hd`) | | `input` | string | required | Text to convert to speech (max 4096 characters) | | `voice` | string | `alloy` | Voice: `alloy`, `ash`, `ballad`, `coral`, `echo`, `fable`, `nova`, `onyx`, `sage`, `shimmer`, `verse` | | `response_format` | string | `mp3` | Audio format: `mp3`, `opus`, `aac`, `flac`, `wav`, `pcm` | | `speed` | float | 1.0 | Speed multiplier (0.25–4.0) | ### Example ```bash curl -X POST https://your-tie-host/v1/audio/speech \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $TOKEN" \ -d '{ "model": "gpt-4o-mini-tts", "input": "Great job logging your meals today! You hit your protein target.", "voice": "nova" }' \ --output response.mp3 ``` ### Response Raw audio bytes in the requested format. The `Content-Type` header reflects the format (e.g. `audio/mpeg` for mp3). ### AI SDK Integration ```typescript import { createOpenAI } from "@ai-sdk/openai"; import { experimental_generateSpeech as generateSpeech } from "ai"; const tie = createOpenAI({ baseURL: "https://your-tie-host/v1", apiKey: "$TOKEN", }); const result = await generateSpeech({ model: tie.speech("gpt-4o-mini-tts"), text: "Great job logging your meals today!", voice: "nova", }); // result.audio is a Uint8Array of audio bytes ``` --- ## Gemini TTS (same endpoint, different model) `/v1/audio/speech` routes to Gemini when `model` is a Gemini TTS variant — same pattern as `/v1/audio/transcriptions` switching between Whisper and Gemini. No second endpoint to learn. For real-time conversational voice, see [Real-Time Voice](/ai-gateway/voice/). > **Note:** Google's [OpenAI compatibility layer](https://ai.google.dev/gemini-api/docs/openai) does not expose a TTS endpoint — only audio *understanding* (input). The Gemini path on `/v1/audio/speech` is Truememory's own OpenAI-shaped facade that calls Gemini's native SDK (`google-genai` with `response_modalities=["AUDIO"]`). Callers see a unified API; Truememory handles the bridging. ```bash POST /v1/audio/speech Authorization: Bearer Content-Type: application/json ``` ```json { "model": "google/gemini-2.5-flash-tts", "input": "Great job today!", "voice": "Kore", "language": "en", "response_format": "pcm" } ``` ### Differences vs OpenAI TTS | | OpenAI | Gemini | |--|--|--| | `model` | `gpt-4o-mini-tts`, `tts-1`, `tts-1-hd` | `google/gemini-2.5-flash-tts` | | `voice` | `alloy`, `nova`, `coral`, etc. (see [OpenAI Voices](#openai-voices)) | `Kore`, `Aoede`, `Puck`, etc. (see [Gemini Voices](#gemini-voices)) | | `language` | Auto-detected from input | Required — BCP-47 code (`en`, `ms`, `es`) | | `response_format` | `mp3` / `opus` / `aac` / `flac` / `wav` / `pcm` | `pcm` / `wav` only — others return `400 VALIDATION_ERROR` | | `speed` | 0.25–4.0 | Ignored | ### Example ```bash # Raw PCM — 24 kHz signed 16-bit little-endian mono curl -X POST https://your-tie-host/v1/audio/speech \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $TOKEN" \ -d '{ "model": "google/gemini-2.5-flash-tts", "input": "Great job today!", "voice": "Kore" }' \ --output response.pcm # WAV — playable by any audio player without client-side transcoding curl -X POST https://your-tie-host/v1/audio/speech \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $TOKEN" \ -d '{ "model": "google/gemini-2.5-flash-tts", "input": "Great job today!", "voice": "Kore", "response_format": "wav" }' \ --output response.wav ``` ### Response `pcm` (default): raw PCM — **24 kHz, signed 16-bit little-endian, mono**. `Content-Type: audio/pcm;rate=24000`. `wav`: same PCM wrapped in a standard 44-byte WAV header. `Content-Type: audio/wav`. Playable directly in browsers and mobile apps without transcoding. #### Usage headers Every successful Gemini TTS response includes token-usage headers so callers can record billing data without inspecting the audio body: | Header | Source | |--|--| | `X-Usage-Prompt-Tokens` | `usage_metadata.prompt_token_count` | | `X-Usage-Completion-Tokens` | `usage_metadata.candidates_token_count` | | `X-Usage-Total-Tokens` | `usage_metadata.total_token_count` | Headers are always present — they default to `"0"` when the SDK omits `usage_metadata`. The OpenAI TTS path (`gpt-4o-mini-tts` etc.) does not emit these headers. --- ## Common Pattern: Voice Chat A typical voice-enabled chat flow combines both endpoints: 1. **User speaks** → record audio on the client 2. **Transcribe** → `POST /v1/audio/transcriptions` → get text 3. **Chat** → `POST /v1/chat/completions` with the transcribed text → get AI response 4. **Speak** → `POST /v1/audio/speech` with the AI response text → play audio ```mermaid sequenceDiagram participant User participant Client participant Truememory User->>Client: Speaks Client->>Truememory: POST /v1/audio/transcriptions (audio file) Truememory-->>Client: { "text": "..." } Client->>Truememory: POST /v1/chat/completions (transcribed text) Truememory-->>Client: AI response text Client->>Truememory: POST /v1/audio/speech (response text) Truememory-->>Client: Audio bytes Client->>User: Plays audio ``` Steps 3 and 4 can overlap — start TTS as soon as the chat response text is available, rather than waiting for the full response. ## Models ### Transcription | Model | Description | |-------|-------------| | `whisper-1` | OpenAI Whisper — general-purpose, supports 50+ languages | | `google/gemini-2.5-flash` | Gemini 2.5 Flash — accurate, supports audio/webm and other browser formats | ### Text-to-Speech | Model | Description | |-------|-------------| | `gpt-4o-mini-tts` | Latest, most natural-sounding. Default. | | `tts-1` | Optimized for low latency | | `tts-1-hd` | Optimized for quality | ### OpenAI Voices | Voice | Tone | |-------|------| | `alloy` | Neutral, balanced | | `ash` | Warm, conversational | | `ballad` | Soft, expressive | | `coral` | Clear, friendly | | `echo` | Steady, authoritative | | `fable` | Expressive, storytelling | | `nova` | Warm, engaging | | `onyx` | Deep, rich | | `sage` | Calm, measured | | `shimmer` | Bright, upbeat | | `verse` | Versatile, adaptive | ## Gemini Voices Used with `POST /v1/audio/speech` (when `model=google/gemini-2.5-flash-tts`) and the real-time voice agent. | Voice | Character | |-------|-----------| | `Kore` | Firm, grounded | | `Puck` | Playful, upbeat | | `Charon` | Calm, measured | | `Fenrir` | Confident, direct | | `Aoede` | Smooth, warm | | `Leda` | Clear, friendly | | `Orus` | Steady, authoritative | | `Zephyr` | Light, energetic | Full catalog: [Gemini voice options](https://cloud.google.com/vertex-ai/generative-ai/docs/live-api/voice-options) # Brains Source: https://truememory-docs.mvstg.com/ai-gateway/brains/ · Markdown: https://truememory-docs.mvstg.com/ai-gateway/brains/index.md A **brain** is a named collection of [knowledge packs](/ai-gateway/knowledge-packs/). Instead of listing pack IDs on every chat request, you group the packs once under a brain and attach the whole set with a single `brain_id`. A user might have one brain for their company handbook, another for a product catalog — each a stable bundle they can point a conversation at. A brain owns nothing on its own. It is just a name plus a list of which packs are in it. Packs go in and out, and the brain uses whatever packs are in it at the moment of the request. The documents, their ingestion, and their access rules all still live on the packs themselves (see [Knowledge Packs](/ai-gateway/knowledge-packs/)). **Think of it like** a playlist. The songs (packs) exist independently; a playlist (brain) just references them in one place. Deleting a playlist never deletes the songs, and the same song can sit on many playlists. **Where that metaphor breaks down:** a brain only points at its packs — it never copies them. If a pack is deleted after you add it, the brain simply stops returning it, and there is nothing left behind to clean up. ```mermaid flowchart LR subgraph Build["Assemble once"] direction TB B["Create a brain
name · description"] --> M["Add knowledge packs
POST /brains/{id}/knowledge-packs"] end subgraph Use["Every chat request"] direction TB R["POST /v1/chat/completions
brain_id"] --> D["Resolve brain → its pack IDs"] D --> C["Hybrid search + inject as
Relevant Document Context"] end M -.->|"brain now holds N packs"| R style B fill:#f0f9ff,stroke:#3b82f6,color:#181818 style M fill:#ecfdf5,stroke:#10b981,color:#181818 style R fill:#f0f9ff,stroke:#3b82f6,color:#181818 style D fill:#e6f4ff,stroke:#0a99ff,color:#181818 style C fill:#ecfdf5,stroke:#10b981,color:#181818 ``` :::note[Requires the PostgreSQL memory backend] Brains are stored alongside the memory database. On an instance without it configured, every brain endpoint returns `501 BRAIN_SYSTEM_UNAVAILABLE`. ::: All brain endpoints require authentication. User-scoped endpoints (`/brains`) accept either a user Bearer token from [Truememory Auth](/authentication/overview/), or the calling app's service token plus `X-On-Behalf-Of: ` when your backend proxies for a user. Admin endpoints (`/admin/brains/*`) require the service token or an admin user token — see [Admin endpoints](#admin-endpoints). ## Using a brain in a chat request This is the whole point of a brain. On a `/v1/chat/completions` request, pass `brain_id` (or `brain_ids` for several at once): ```bash POST https://your-tie-host/v1/chat/completions Authorization: Bearer $TOKEN Content-Type: application/json { "model": "anthropic/claude-sonnet-4-5", "messages": [ { "role": "user", "content": "What's our refund window for annual plans?" } ], "brain_id": "b1a2c3..." } ``` Truememory resolves the brain to the pack IDs it currently holds, then runs the same retrieval as `knowledge_pack_ids` — hybrid search over the packs, top chunks injected under **Relevant Document Context**. Everything from [Knowledge Packs → Retrieval](/ai-gateway/knowledge-packs/#retrieval) applies: only `ready` packs are searched, and pack [visibility](/ai-gateway/knowledge-packs/#visibility) is enforced first. ### Grounding a request in several brains Pass `brain_ids` to attach more than one brain to the same request — the handbook plus the product catalog, say: ```json { "model": "anthropic/claude-sonnet-4-5", "messages": [{ "role": "user", "content": "Does the Pro plan follow the standard refund window?" }], "brain_ids": ["b1a2c3...", "d4e5f6..."] } ``` Every named brain resolves in one lookup, and the packs are merged into a single deduplicated set before retrieval — a pack that sits in two of the brains is searched once. `brain_id` is the singular alias for the same field: send both and Truememory resolves their union. A few behaviors worth knowing: - **`knowledge_pack_ids` wins.** If a request sends a non-empty `knowledge_pack_ids`, the brains are ignored and the explicit IDs are used. - **An empty brain injects nothing.** If the brains hold no packs (or their packs were deleted), no document context is added — the request proceeds normally. - **Only memory-enabled agents resolve them.** Brains are read on the same path as memory context. Agents that do not use memory, or requests with memory reads disabled, skip resolution. - **An unreachable brain is a 404.** Resolution is scoped to what the caller can read — brains they own, hold an entitlement for, or that are public. An ID that fails that check returns `BRAIN_NOT_FOUND` with the offending ID in `details`, rather than answering with less grounding than you asked for. In a multi-brain request, one bad ID fails the whole request. ## Creating and managing a brain ```bash # Create POST https://your-tie-host/brains Authorization: Bearer $TOKEN Content-Type: application/json { "name": "Company Handbook", "description": "HR policies and SOPs", "app_id": "vibrantly" } ``` `name` is required; `description` and `app_id` are optional. The response returns the brain with its `id`, `user_id`, `app_id`, and timestamps. | Endpoint | Behavior | |---|---| | `POST /brains` | Create a brain owned by the caller | | `GET /brains` | List the caller's brains. Filters: `app_id`; paginate with `limit` (default 50, max 200) and `offset` | | `GET /brains/{id}` | Fetch one brain | | `PATCH /brains/{id}` | Update `name` and/or `description`. `name` cannot be set to null | | `DELETE /brains/{id}` | Delete the brain. Its packs are untouched — only the collection is removed | ## Adding and removing packs Add and remove packs through these endpoints. Adding a pack that is already in the brain is safe — it is not added twice. ```bash # Add a pack to a brain POST https://your-tie-host/brains/{brain_id}/knowledge-packs Authorization: Bearer $TOKEN Content-Type: application/json { "pack_id": "7f3c..." } ``` | Endpoint | Behavior | |---|---| | `POST /brains/{id}/knowledge-packs` | Add a pack (body `{ "pack_id": "..." }`). The pack must be one the caller owns or a public pack | | `GET /brains/{id}/knowledge-packs` | List the packs currently in the brain | | `DELETE /brains/{id}/knowledge-packs/{pack_id}` | Remove a pack from the brain (the pack itself is not deleted) | A brain points at its packs rather than holding copies of them. So a pack that is deleted after you add it simply disappears from the brain's list and from retrieval — no leftover entry to remove, and no error. ## Admin endpoints The `/admin/brains/*` block manages brains across all users without the per-user ownership check. It is gated by the Truememory service token (`AUTH_SECRET`) or an admin user token; end-user tokens cannot reach it. The admin surface mirrors the user one — create, list, fetch, update, delete, and add or remove packs — with two differences: - **Ownerless (app-owned or global) brains.** `POST /admin/brains` accepts an optional `user_id`. Set it to assign the brain to a user; leave it `null` to create a brain with no owner — an app-scoped brain (with `app_id`) or a fully global one (both `null`). - **No ownership filter.** `GET /admin/brains` lists across everyone, with optional `user_id` and `app_id` filters plus `limit`/`offset`. Admin pack-add does not require the pack to be owned by any particular user. ```bash # Create a global brain (no owner) scoped to one app POST https://your-tie-host/admin/brains Authorization: Bearer $AUTH_SECRET Content-Type: application/json { "name": "Shared Playbook", "app_id": "vibrantly", "user_id": null } ``` ## Common pitfalls - **Expecting a brain to add itself.** Like packs, brains are opt-in per request. A brain injects context only when a request passes its `brain_id` (or lists it in `brain_ids`). - **Sending `brain_id` and `knowledge_pack_ids` together.** The explicit `knowledge_pack_ids` wins and the brain is ignored — not merged. Pass one or the other. (`brain_id` and `brain_ids`, by contrast, *are* merged.) - **Resolving a brain you cannot read.** Resolution is scoped to the caller. A brain you neither own, hold an entitlement for, nor can see as public returns `404 BRAIN_NOT_FOUND` — including when it is one ID among several. - **Deleting a brain to remove documents.** Deleting a brain removes only the collection. The packs — and their entitlements and skill tags — remain. Delete the packs themselves if that is the intent. - **Adding a pack you cannot see.** A user add requires a pack you own or a public pack; anything else is rejected. # Unified API (OpenAI compat) Source: https://truememory-docs.mvstg.com/ai-gateway/chat-completions/ · Markdown: https://truememory-docs.mvstg.com/ai-gateway/chat-completions/index.md Truememory AI Gateway offers an OpenAI-compatible `/v1/chat/completions` endpoint, enabling integration with multiple AI providers using a single URL. This is a drop-in replacement for OpenAI, Cloudflare AI Gateway, or LiteLLM — change the base URL and it works. ## Authentication All requests require a Bearer token in the `Authorization` header. See [Ways to authenticate](/authentication/overview/) for how to get one. | Caller | Method | Details | |--------|--------|---------| | Browser / mobile app | [User tokens](/authentication/email-password/) | Sign in via Truememory Auth, pass the JWT | | Backend service | [Service accounts](/authentication/service-accounts/) | Use `AUTH_SECRET`, optionally with `X-On-Behalf-Of` for user context | ## Endpoint URL ``` POST /v1/chat/completions ``` Switch providers by changing the `model` parameter. For example: `anthropic/claude-sonnet-4-5`, `openai/gpt-5.1`, `vertexai/gemini-2.5-flash`. ## Parameters | Parameter | Type | Default | Description | |-----------|------|---------|-------------| | `model` | string | required | Model to use (e.g. `anthropic/claude-sonnet-4-5`, `openai/gpt-5.1`) | | `messages` | array | required | Conversation messages in OpenAI format. Each message's `content` can be a plain string or an array of content parts mixing text and images — see [Vision](/ai-gateway/features/vision/). If the array includes a `system` or `developer` message, it replaces the agent's built-in prompt for that request (memory and personas are still injected). Each message also accepts an optional `cache_control` field — see [Prompt caching](/ai-gateway/features/prompt-caching/). | | `tools` | array | null | Tool definitions in OpenAI function calling format. See [Tool calling](/ai-gateway/features/tool-calling/). | | `stream` | boolean | false | Enable SSE streaming | | `stream_options` | object | null | Streaming options. Set `{"include_usage": true}` to receive a final chunk with token usage (only applies when `stream` is `true`). | | `temperature` | float | null | Sampling temperature. Ignored on `anthropic/claude-opus-5-5`, `anthropic/claude-sonnet-5-5` and `anthropic/claude-haiku-5-5`, which do not accept sampling parameters. | | `max_tokens` | integer | null | Maximum tokens to generate | | `top_p` | float | null | Nucleus sampling parameter. Ignored on `anthropic/claude-opus-5-5`, `anthropic/claude-sonnet-5-5` and `anthropic/claude-haiku-5-5`. | | `stop` | string/array | null | Stop sequences | | `thread_id` | string | null | Conversation thread for state persistence (see [Threads](#threads)) | | `parent_message_id` | string | null | Id of the last stored message the client received on this thread (the `message_id` of the previous response). When set, anything already stored after it is not appended again, so a failed request can be re-sent unchanged. `messages` holds the messages after it, and may also start with history that ends at it; that history is dropped exactly as it is without this field. Ignored without `thread_id`. See [Retrying a failed turn](/ai-gateway/threads/#retrying-a-failed-turn). | | `store` | boolean | `true` | When `true` (default), the call is recorded in the conversation thread and shows up in `/threads`. Set to `false` for one-off agent calls — for example, a background helper that should run the agent without leaving a trace in the user's chat history. | | `memory` | object | null | Per-request memory controls. `{"read": true, "write": false}` lets the agent recall facts but skips saving anything new from this turn; both default to `true` on user-scoped requests. The thread itself is still recorded either way (so it still appears in `/threads`) — only the memory graph writes are suppressed. | | `persona_id` | string | null | Persona to use for this request. Falls back to the user's [active persona](/ai-gateway/personas/) if omitted. | | `context_management` | array | null | Context-management strategies for this request (OpenAI-compatible). Include a `{"type": "compaction", ...}` entry to fold older thread history into a rolling summary checkpoint once it crosses a token threshold. See [Thread compaction](/ai-gateway/features/thread-compaction/). | | `metadata` | object | null | Up to 16 key-value string pairs attached to this request. Persisted with the thread and returned in history. Echoed in the SSE first chunk when streaming. See [Custom metadata](/ai-gateway/features/custom-metadata/). | | `skill_instructions` | string | null | Caller-composed skill prompt. Injected after persona, before memory context. Purely additive — never replaces persona or base instructions. See [Skill instructions](/ai-gateway/features/skill-instructions/). | | `knowledge_pack_ids` | string[] | null | Ground the answer in specific uploaded documents. See [Knowledge Packs](/ai-gateway/knowledge-packs/#retrieval). | | `skill_id` | string | null | Ground the answer in every knowledge pack tagged with this [skill](/ai-gateway/skills/). Can be combined with `knowledge_pack_ids`. See [Knowledge Packs — Retrieval](/ai-gateway/knowledge-packs/#retrieval). | | `brain_id` | string | null | Ground the answer in every pack in a [brain](/ai-gateway/brains/) (a named collection of packs). Singular alias for `brain_ids`; send both and their union is resolved. Ignored when `knowledge_pack_ids` is also provided. Resolved on the memory path — requires a memory-enabled agent and memory reads on (see [Brains](/ai-gateway/brains/)). | | `brain_ids` | string[] | null | Ground the answer in several [brains](/ai-gateway/brains/) at once. Their packs merge into one deduplicated set. Every ID must be readable by the caller — an unreachable one returns `404 BRAIN_NOT_FOUND`. | | `graph_ids` | string[] | null | Blend one or more [graph namespaces](/memory/graph-namespacing/) (shared knowledge graphs) into this request's memory retrieval, alongside the user's own memory. | ## Custom Prompt Mode If you want to replace the agent's built-in prompt with your own instructions, send a `system` or `developer` message in the `messages` array. When Truememory sees one of those roles, your message **replaces** the agent's base instructions for that request: - your `system` or `developer` message becomes the top-level instruction instead of the agent's built-in prompt - Truememory **still injects** persistent memory context so the model knows what it remembers about the user - Truememory **still injects** the active persona (if any) - thread history and turn recording **still work** normally - client-defined tools in the `tools` parameter still work This is useful when you want full prompt control for a specific request while keeping Truememory's memory and conversation features. ### Example: caller-owned prompt ```bash curl -X POST https://your-tie-host/v1/chat/completions \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $TOKEN" \ -d '{ "model": "openai/gpt-5.1", "messages": [ {"role": "developer", "content": "You are a concise support assistant. Answer in 3 bullet points max."}, {"role": "user", "content": "How do I rotate an API key?"} ] }' ``` ## Examples A minimal request — memory and the active persona are injected automatically, nothing extra to send: ```bash curl -X POST https://your-tie-host/v1/chat/completions \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $TOKEN" \ -d '{ "model": "anthropic/claude-sonnet-4-5", "messages": [{"role": "user", "content": "What did I say I was working on?"}] }' ``` With `"stream": true`, tokens arrive as SSE chunks in the standard OpenAI shape — content lives in `choices[0].delta.content`, and the stream ends with a `finish_reason` chunk followed by `data: [DONE]`: ``` data: {"id":"chatcmpl-abc123","object":"chat.completion.chunk","created":1711234567,"model":"anthropic/claude-sonnet-4-5","choices":[{"index":0,"delta":{"content":"Hel"},"finish_reason":null}]} data: {"id":"chatcmpl-abc123","object":"chat.completion.chunk","created":1711234567,"model":"anthropic/claude-sonnet-4-5","choices":[{"index":0,"delta":{"content":"lo!"},"finish_reason":null}]} data: {"id":"chatcmpl-abc123","object":"chat.completion.chunk","created":1711234567,"model":"anthropic/claude-sonnet-4-5","choices":[{"index":0,"delta":{},"finish_reason":"stop"}]} data: [DONE] ``` Use the interactive picker below for per-language variants (OpenAI SDKs, Vercel AI SDK, cURL) and image inputs: ### Response ```json { "id": "chatcmpl-abc123", "object": "chat.completion", "created": 1711234567, "model": "anthropic/claude-sonnet-4-5", "choices": [ { "index": 0, "message": { "role": "assistant", "content": "The capital of France is Paris." }, "finish_reason": "stop" } ], "usage": { "prompt_tokens": 12, "completion_tokens": 8, "total_tokens": 20, "cache_read_tokens": 0, "cache_creation_tokens": 0 }, "metadata": null } ``` If `metadata` was included in the request, it is echoed back here. ### Understanding the `usage` object Every time you call the AI, it counts the pieces of text (called **tokens**) that go in and out. You pay your provider for these tokens, so the `usage` object is how you know what just got spent. Think of it like a taxi meter: `prompt_tokens` is how far you rode in, `completion_tokens` is how far the AI drove back, and `total_tokens` is the full fare. | Field | What it means (in plain English) | |-------|-----------------------------------| | `prompt_tokens` | How much **you** sent in (your messages + system prompt + memory + persona). Bigger question = bigger number. | | `completion_tokens` | How much the **AI** wrote back. A one-word answer is tiny. A long essay is big. | | `total_tokens` | Simply `prompt_tokens + completion_tokens`. The whole bill. | | `cache_read_tokens` | Of your `prompt_tokens`, how many were **reused from a cached copy** the provider had already saved. These are WAY cheaper — Anthropic charges up to **10× less** for cache reads. | | `cache_creation_tokens` | How many tokens the provider **saved into cache** for next time. Slightly pricier than a normal token, but it makes future calls much cheaper. | :::tip[Why cache tokens matter] If you keep sending the same long system prompt or the same big conversation history, Anthropic and OpenAI can cache it. The next time you ask, the cached part is nearly free. These fields let you see that savings (or the one-time cost of creating the cache) so you can calculate your real bill. See [Prompt caching](/ai-gateway/features/prompt-caching/) for how to control it. ::: :::note[When these are zero] Most providers (Gemini, Groq, Mistral, etc.) don't support prompt caching, so `cache_read_tokens` and `cache_creation_tokens` will always be `0` for them. OpenAI reports cache reads but not cache creation, so `cache_creation_tokens` is always `0` for OpenAI calls. That's not a bug — there's just nothing to report. ::: ## Features Request-level capabilities — each has its own page under [Features](/ai-gateway/features/overview/): - [Prompt caching](/ai-gateway/features/prompt-caching/) — reuse a stable prompt prefix at up to 10x lower cost - [Vision (image input)](/ai-gateway/features/vision/) — send images via content parts - [Tool calling](/ai-gateway/features/tool-calling/) — client-defined functions the model can call - [Custom metadata](/ai-gateway/features/custom-metadata/) — key-value pairs persisted with the thread - [Message compression](/ai-gateway/features/message-compression/) — summarise long histories to save tokens - [Skill instructions](/ai-gateway/features/skill-instructions/) — additive caller-composed skill prompts ## Threads Pass a `thread_id` to maintain conversation state across requests. Truememory uses it to persist the message history so the LLM has context from previous turns. - If **omitted**, Truememory auto-generates a UUID for the thread. Each request starts a new conversation. - If **provided**, Truememory resumes the conversation from where it left off. Use any string (e.g. a UUID you generate client-side). See [Threads](/ai-gateway/threads/) for the full API reference (list, rename, delete, history). ## Streaming Usage When streaming (`stream: true`), token usage is available by setting `stream_options.include_usage` to `true`. ```bash curl -X POST https://your-tie-host/v1/chat/completions \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $TOKEN" \ -d '{ "model": "anthropic/claude-sonnet-4-5", "stream": true, "stream_options": {"include_usage": true}, "messages": [ {"role": "user", "content": "Hello!"} ] }' ``` When enabled, Truememory emits a final SSE chunk after the `finish_reason` chunk with an empty `choices` array and the accumulated `usage`: ``` data: {"id":"chatcmpl-abc123","object":"chat.completion.chunk","created":1711234567,"model":"anthropic/claude-sonnet-4-5","choices":[],"usage":{"prompt_tokens":9,"completion_tokens":12,"total_tokens":21,"cache_read_tokens":0,"cache_creation_tokens":0}} data: [DONE] ``` The final `usage` chunk includes the same fields as the non-streaming response, including `cache_read_tokens` and `cache_creation_tokens`. Numbers are accumulated across every internal step (tool calls, memory lookups, etc.) so you get one clean bill for the whole request. See [Understanding the `usage` object](#understanding-the-usage-object) for what each field means. If `stream_options` is omitted or `include_usage` is `false`, no usage chunk is emitted. ## Agent Selection By default, requests use the `chatbot` agent. To use a different agent, pass the `X-Agent-Id` header: ```bash curl -X POST https://your-tie-host/v1/chat/completions \ -H "X-Agent-Id: research-assistant" \ -H "Authorization: Bearer $TOKEN" \ -d '{"model": "anthropic/claude-sonnet-4-5", "messages": [...]}' ``` If the request includes a `system` or `developer` message, that message replaces the agent's built-in prompt for that request. Memory and personas are still injected. ## Per-App Thread Isolation To scope threads to a specific application, pass the `X-App-Id` header. Threads sent with an app ID are isolated — they won't appear in other apps' thread lists, and can only be resumed, renamed, or deleted with the same `X-App-Id`. ```bash curl -X POST https://your-tie-host/v1/chat/completions \ -H "X-App-Id: vibrantly" \ -H "X-Agent-Id: chatbot" \ -H "Authorization: Bearer $TOKEN" \ -d '{"model": "anthropic/claude-sonnet-4-5", "messages": [{"role": "user", "content": "Hello"}], "thread_id": "my-thread"}' ``` When `X-App-Id` is omitted, threads are unscoped and visible to all apps. See [Threads — Per-App Thread Isolation](/ai-gateway/threads/#per-app-thread-isolation) for details. For the full agent registry (memory scope and safety behavior per agent), see [Agents](/ai-gateway/agents/#available-agents) — the single source of truth. Query `GET /info` to see the agents on your instance. ## Supported Providers - [Anthropic](https://docs.anthropic.com/) - [OpenAI](https://platform.openai.com/) - [Google Vertex AI](https://cloud.google.com/vertex-ai) - [Z.ai (GLM)](https://docs.z.ai/) — e.g. `zai/glm-4.6`, `zai/glm-4.7`, `zai/glm-5.2` Query `GET /info` to see which models are currently available on your instance. # Custom metadata > Attach key-value pairs to a request — persisted with the thread and echoed back to the client. Source: https://truememory-docs.mvstg.com/ai-gateway/features/custom-metadata/ · Markdown: https://truememory-docs.mvstg.com/ai-gateway/features/custom-metadata/index.md Attach arbitrary key-value string pairs to a [chat completions](/ai-gateway/chat-completions/) request using the root-level `metadata` parameter. This is useful for storing client context (source app, user location, image URLs, UI hints) alongside the conversation without changing the message schema. **Constraints:** | Limit | Value | |-------|-------| | Max keys per request | 16 | | Max key length | 64 characters | | Max value length | 512 characters | | Value type | string only | **Example:** ```bash curl -X POST https://your-tie-host/v1/chat/completions \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $TOKEN" \ -d '{ "model": "anthropic/claude-sonnet-4-5", "messages": [ {"role": "user", "content": "Describe this image"} ], "metadata": { "session_id": "8b94776f-31ae-49ac-9f06-3be137d69186", "platform": "ios", "app_version": "2.4.1" } }' ``` Metadata is echoed back in the response and persisted with the thread. When you retrieve [thread history](/ai-gateway/threads/#get-thread-history), metadata appears on the user message it was attached to. When streaming, metadata is included in the **first SSE chunk** so clients can act on it immediately (e.g. render skill-specific UI before content arrives): ``` data: {"id":"chatcmpl-abc123","object":"chat.completion.chunk","created":1711234567,"model":"anthropic/claude-sonnet-4-5","choices":[{"index":0,"delta":{"role":"assistant"},"finish_reason":null}],"metadata":{"active_skill":"menu","super_skill":"keto"}} ``` Subsequent chunks do not contain metadata. :::note Metadata is for client use. The LLM does not see it — only `content` is visible to the model. ::: One documented exception: `"compression": "true"` inside `metadata` is read by Truememory as a control flag — see [Message compression](/ai-gateway/features/message-compression/). # Message compression > Summarise older turns in long threads to reduce token usage before forwarding to the LLM. Source: https://truememory-docs.mvstg.com/ai-gateway/features/message-compression/ · Markdown: https://truememory-docs.mvstg.com/ai-gateway/features/message-compression/index.md For long-running threads, the accumulated conversation history can grow large and consume a significant portion of the model's context window. Truememory supports opt-in message compression via [Headroom](https://github.com/chopratejas/headroom), which intelligently summarises older turns to reduce token usage before forwarding to the LLM. ## Enabling compression Pass `"compression": "true"` inside the [`metadata`](/ai-gateway/features/custom-metadata/) field of any `/v1/chat/completions` request: ```bash curl -X POST https://your-tie-host/v1/chat/completions \ -H "Authorization: Bearer $TOKEN" \ -H "X-Agent-Id: chatbot" \ -d '{ "model": "anthropic/claude-sonnet-4-6", "messages": [{"role": "user", "content": "Continue our discussion..."}], "thread_id": "my-long-thread", "metadata": {"compression": "true"} }' ``` The flag is **per-request** — you can enable it selectively (e.g. only after a thread reaches a certain length) rather than on every call. ## How it works 1. Truememory replays the thread history as usual 2. Before forwarding to the LLM, the assembled message array is passed through Headroom's compression algorithm, which summarises older turns into a compact representation while preserving recent context 3. The compressed messages are sent to the LLM provider — the `usage` token counts in the response reflect what was actually sent Compression is applied **after** all Truememory processing (persona injection, memory retrieval, cache markers) so none of those features are affected. ## Behaviour on failure If Headroom compression fails for any reason (library not available, network error, unexpected format), Truememory automatically falls back to sending the uncompressed message history. The request always completes — compression failure is never surfaced as an error to the caller. ## When to use it | Scenario | Recommendation | |----------|----------------| | Short threads (< ~20 turns) | Not needed — Headroom is a no-op on short histories | | Long threads approaching context limits | Enable compression to reduce token usage and cost | | Latency-sensitive requests | Compression adds a small synchronous overhead — profile if P99 matters | :::note Compression uses the OpenAI tokeniser internally to estimate token counts. For non-OpenAI models (Anthropic, Gemini, etc.) the compression boundaries may be slightly miscalibrated — the result is still correct, but the trimming threshold may be off by a small margin. ::: # Overview > Request-level capabilities of the chat completions endpoint — caching, vision, tools, metadata, compression, compaction, and skill prompts. Source: https://truememory-docs.mvstg.com/ai-gateway/features/overview/ · Markdown: https://truememory-docs.mvstg.com/ai-gateway/features/overview/index.md Every feature below is a capability of the [Unified API](/ai-gateway/chat-completions/) — you opt in per request with a parameter or header, no configuration step required. | Feature | What it does | Opt in with | |---------|--------------|-------------| | [Prompt caching](/ai-gateway/features/prompt-caching/) | Reuse a stable prompt prefix across requests at up to 10× lower cost (Anthropic routes) | `cache_control` on a message | | [Vision (image input)](/ai-gateway/features/vision/) | Send images to vision-capable models alongside text | content parts in `messages` | | [Tool calling](/ai-gateway/features/tool-calling/) | Let the model call functions your app defines and executes | `tools` | | [Custom metadata](/ai-gateway/features/custom-metadata/) | Attach key-value pairs to a request, persisted with the thread and echoed back | `metadata` | | [Message compression](/ai-gateway/features/message-compression/) | Summarise older turns in long threads to reduce token usage | `"compression": "true"` in `metadata` | | [Thread compaction](/ai-gateway/features/thread-compaction/) | Fold older turns into a rolling summary checkpoint so long-thread cost stays flat | `context_management` | | [Skill instructions](/ai-gateway/features/skill-instructions/) | Inject caller-composed skill prompts without replacing the agent's identity | `skill_instructions` | Features compose freely — a single request can cache its system prompt, send an image, define tools, and carry metadata at the same time. # Prompt caching > Mark the stable portion of a prompt so repeat requests reuse it at up to 10× lower cost. Source: https://truememory-docs.mvstg.com/ai-gateway/features/prompt-caching/ · Markdown: https://truememory-docs.mvstg.com/ai-gateway/features/prompt-caching/index.md If you keep sending the same long prefix on every [chat completions](/ai-gateway/chat-completions/) request — a multi-thousand-token system prompt, a growing conversation history, the same reference document — you don't have to keep paying for it. With Claude (Anthropic) routes, you can **explicitly mark** the stable portion to cache. Subsequent calls reuse the cached prefix at roughly 10× cheaper, and you only pay the full rate for what's new. (Some other providers — OpenAI, Gemini — do their own caching with different mechanics; see the [Provider support](#provider-support) table below.) You see the savings in the [`usage` object](/ai-gateway/chat-completions/#understanding-the-usage-object): `cache_read_tokens` goes up, and the cached portion drops to the cache-read price. **Think of it like a hotel keycard:** the first time you check in, there's a small activation cost. After that, the same card opens your door instantly until the lease expires. If you change rooms (modify the cached content), you need a new card. **Where the metaphor breaks down:** the cache has a short TTL (5 minutes by default), and the cached portion must match exactly — Anthropic requires "100% identical prompt segments" across requests, so even a single-character change in the cached prefix invalidates the cache. ## When to use it - **Long, stable system prompts** sent on every request (persona, instructions, tool schemas) - **Multi-turn conversations** where prior turns don't change between calls - **Document Q&A** where the same long document is repeatedly referenced If your prefix is short or changes every call, caching costs more than it saves — there's a small surcharge on the first call to write the cache. ## How to send a cache marker Set `cache_control` on a message to cache everything up through and including that message. The next request that arrives within the TTL with the same prefix reuses the cache. ```bash curl -X POST https://your-tie-host/v1/chat/completions \ -H "Authorization: Bearer $TOKEN" \ -d '{ "model": "anthropic/claude-sonnet-4-5", "messages": [ { "role": "system", "content": "...long stable instructions...", "cache_control": {"type": "ephemeral"} }, { "role": "user", "content": "Earlier question" }, { "role": "assistant", "content": "Earlier answer", "cache_control": {"type": "ephemeral"} }, { "role": "user", "content": "New question — not cached, charged at full rate" } ] }' ``` This example uses two markers: one after the system prompt (caches a long stable prefix), one after the last assistant reply (extends the cache through the prior conversation). Anthropic accepts up to **4 cache breakpoints per request**. ## Per-content-part caching For messages with mixed content (text + images), put `cache_control` on individual content parts. Useful when you want to cache one specific block — say, a big document — while keeping the surrounding text uncached. ```json { "role": "user", "content": [ { "type": "text", "text": "Use this 500-page manual as context...", "cache_control": {"type": "ephemeral"} }, { "type": "text", "text": "Now my question is..." } ] } ``` ## Automatic server-side breakpoints (Anthropic) Even when you don't set `cache_control` yourself, Truememory marks the parts of an Anthropic request that are most likely to be stable across turns — the system prompt, the tool schemas, and recent user turns — so caching works out of the box. Any markers you add on top compose with these. Think of Truememory as drawing a dotted line between "the part that's the same every call" and "the part that's new this call." You can redraw the line yourself with `cache_control`, but you don't have to. - **Stable system prompt** — Truememory's built-in instructions (plus persona and skill instructions when present) sit inside the cached prefix. - **Tool schemas** — When tools are bound, the last tool definition carries a cache marker so the whole tool schema stays inside the reusable prefix. Anthropic orders tools before the system prompt, so without this marker the schema would invalidate the cache on every change. - **Rolling human turns** — The two most recent user turns are marked, extending the cache through the prior conversation. Only the volatile suffix of the current turn — a live listener transcript appended at request time, for example — lands outside the cache. - **Dynamic prompt split** — When a `system` or `developer` override is sent as content parts with a `cache_control` marker, Truememory keeps the boundary you drew: text up to and including the marker is cached; anything after stays dynamic. If the override is a plain string, Truememory auto-detects known volatile headings (date/time, page context, live listener context, retrieved graph context) and moves those below the boundary so per-request data doesn't poison the cache key. ## Provider support | Provider | How Truememory handles `cache_control` | |----------|--------------------------------| | `anthropic/...` (routed via Vertex AI on this instance) | Forwarded to Anthropic. Cache hits show up as `cache_read_tokens` in the response. Truememory also applies the automatic server-side breakpoints listed above. | | OpenAI | The field is passed through to the OpenAI client. OpenAI does its own **automatic** caching on long stable prefixes and reports it via `cache_read_tokens` regardless of whether you set the marker — you don't need `cache_control` to benefit from OpenAI caching. | | Gemini, Groq, Mistral, others | The field is passed through but ignored by these providers via the chat completions API. `cache_read_tokens` stays `0`. (Gemini does support caching, but through a separate `cachedContent` API not exposed via this endpoint.) | :::tip[Longer TTL] The default 5-minute cache window is fine for active conversations. For longer sessions, Anthropic offers an opt-in `ttl` field: `{"type": "ephemeral", "ttl": "1h"}`. See the [Anthropic prompt-caching docs](https://docs.anthropic.com/en/docs/build-with-claude/prompt-caching) for current pricing and constraints. ::: :::note[Verifying cache hits] Set `stream_options.include_usage: true` (or use the non-streaming endpoint) to see `cache_read_tokens` and `cache_creation_tokens` per call. If `cache_read_tokens` stays `0` after several requests with the same prefix, double-check that the cached content is byte-identical across calls and that requests arrived within the TTL window. See [Understanding the `usage` object](/ai-gateway/chat-completions/#understanding-the-usage-object) for what each field means. ::: # Skill instructions > Inject caller-composed skill prompts into the system prompt without replacing the agent's identity. Source: https://truememory-docs.mvstg.com/ai-gateway/features/skill-instructions/ · Markdown: https://truememory-docs.mvstg.com/ai-gateway/features/skill-instructions/index.md Use the `skill_instructions` parameter on [chat completions](/ai-gateway/chat-completions/) requests to inject caller-composed skill prompts into the LLM system prompt. This is how app backends send skill-specific behavior to Truememory without Truememory needing to know about the app's skill definitions. Truememory inserts the text at a fixed position in the prompt stack: ``` ┌─────────────────────────┐ │ Agent base instructions │ ← Truememory owns (e.g. Eliza personality, safety rules) ├─────────────────────────┤ │ Persona │ ← Truememory's persona system ├─────────────────────────┤ │ skill_instructions │ ← Your text goes here ├─────────────────────────┤ │ Memory context │ ← Truememory adds from the user's knowledge graph ├─────────────────────────┤ │ Conversation history │ ← Messages └─────────────────────────┘ ``` **Key behavior:** - **Additive** — never replaces the agent's base instructions or persona - **Optional** — if omitted, behavior is unchanged (persona + memory as before) - Truememory does not interpret, cache, or fetch `skill_instructions` — it only appends the text ## Example ```bash curl -X POST https://your-tie-host/v1/chat/completions \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $TOKEN" \ -d '{ "model": "anthropic/claude-sonnet-4-5", "messages": [ {"role": "user", "content": "What should I order from this Italian restaurant?"} ], "skill_instructions": "You are in Menu Analysis mode. Break down each menu item by calories, protein, carbs, and fat. Apply the Keto lens: flag items exceeding 20g net carbs.", "metadata": { "active_skill": "menu", "super_skill": "keto" } }' ``` In this example, the LLM sees Eliza's personality and safety rules, then the menu analysis instructions, then any memories about the user (e.g. allergies). The [`metadata`](/ai-gateway/features/custom-metadata/) is echoed back to the client but not shown to the LLM. :::note `skill_instructions` differs from [Custom Prompt Mode](/ai-gateway/chat-completions/#custom-prompt-mode). A `system`/`developer` message **replaces** the agent's base instructions. `skill_instructions` is **additive** — the agent's identity stays intact. ::: To manage the skill definitions themselves (create, tag knowledge packs, store prompts), see [Skills](/ai-gateway/skills/). # Thread compaction > Fold older turns into a rolling summary checkpoint so long-lived threads stop growing in cost — opt in per request with context_management. Source: https://truememory-docs.mvstg.com/ai-gateway/features/thread-compaction/ · Markdown: https://truememory-docs.mvstg.com/ai-gateway/features/thread-compaction/index.md Every turn on a [thread](/ai-gateway/threads/) replays the conversation history to the model, so input tokens — and cost — grow with every message. On long-lived threads this compounds: a companion-style conversation can reach 40k+ input tokens for a one-word user message, and nothing ever shrinks it. Thread compaction bounds that growth. Before each model call, Truememory checks the upcoming request against a budget; when it's over, older turns fold into a **rolling summary checkpoint** first, and the model call runs on the compacted window. Subsequent requests replay the checkpoint plus the messages that came after it — not the full transcript — so per-turn cost stays flat no matter how long the thread lives. Compaction never deletes anything: the full transcript stays in the thread, [`/history`](/ai-gateway/threads/#history) keeps returning every message (checkpoints are internal and never shown), and [memory extraction](/memory/overview/) still sees the complete conversation. ## Enabling compaction Add a `compaction` entry to `context_management` on any `/v1/chat/completions` request (naming follows the OpenAI compaction API): ```bash curl -X POST https://your-tie-host/v1/chat/completions \ -H "Authorization: Bearer $TOKEN" \ -H "X-Agent-Id: chatbot" \ -d '{ "model": "anthropic/claude-sonnet-4-6", "messages": [{"role": "user", "content": "Hello again!"}], "thread_id": "my-long-thread", "context_management": [ { "type": "compaction", "compact_threshold": 6000, "keep_tokens": 3000, "model": "anthropic/claude-haiku-4-5" } ] }' ``` The entry's **presence enables compaction for that request**; omit it to disable. There is no server-side setting — the caller decides on every request. ### Options | Field | Required | Default | What it does | |-------|----------|---------|--------------| | `type` | yes | — | Must be `"compaction"`. | | `compact_threshold` | no | overflow mode | Compact when the replayed window exceeds this many tokens — explicit cost control. When omitted, compaction is **overflow protection**: it fires when the estimated request (history + system prompt + tools) approaches the model's context window minus a 20k buffer (or minus `max_tokens`, whichever is larger). On large-context models that means it fires rarely — set a threshold if you want cost-driven compaction. | | `keep_tokens` | no | `8000` | Budget of recent turns carried **verbatim** inside the checkpoint. | | `model` | no | request model | Model for the summarization call. Pass a small model — the request model is usually oversized (and overpriced) for summarization. | ## How it works 1. Before every model call — including each iteration of a tool loop — Truememory estimates the upcoming request. Under budget, the check is a no-op costing nothing. Over budget, the summarizer runs first (adding its latency to that turn) so the expensive model call proceeds on the compacted window. 2. Over the threshold, older turns are summarized into an **anchored rolling summary**: each compaction feeds the previous checkpoint's summary back to the summarizer to update ("preserve still-true details, remove stale ones, merge new facts"), so facts converge instead of being re-derived every time. The most recent `keep_tokens` of conversation ride along verbatim. 3. The checkpoint is stored as a message in the thread. The next request replays `checkpoint + everything after it`. 4. If the summarization call fails, the turn is unaffected — the thread simply stays uncompacted until the next attempt. Compaction requires a `thread_id` in practice: without one there is no replayed history to compact. ### Turning it off — and back on Compaction is a per-request contract, so the toggle is fully reversible: - **Omit the entry** on a request and the thread replays its complete raw history, exactly as if compaction had never run. Existing checkpoints stay stored but never reach the model. - **Re-enable it later** and replay resumes from the newest existing checkpoint. ## When to use it Compaction is built for **long-lived conversational threads** — companions, assistants, support sessions — where history growth is the dominant marginal cost. Measured on a production companion thread, late-thread messages dropped from $0.068 to $0.036 with a 6000/3000 configuration, and stayed there instead of growing without bound. It is the wrong tool when: - **Threads are short-lived.** A thread that never crosses the threshold never compacts; the option is harmless but pointless. - **You need verbatim recall of arbitrary old details.** A summary is lossy by design. Durable facts (names, dates, decisions) are the [memory graph](/memory/overview/)'s job — it extracts them from the full transcript regardless of compaction. Keep `keep_tokens` at 3000 or above for conversational products so recent nuance survives verbatim. - **You want request-scoped compression without persistence.** [Message compression](/ai-gateway/features/message-compression/) compresses the outgoing prompt for a single request and stores nothing; compaction writes a durable checkpoint that changes how all subsequent requests replay. ## Supported agents The agent handling the request must have compaction wired into its graph — currently `chatbot` (the default) and `eliza`. Requesting compaction on an agent without support fails loudly rather than silently ignoring the option: ```json { "code": "AGENT_COMPACTION_UNSUPPORTED", "message": "Agent 'research-assistant' does not support thread compaction.", "hint": "Omit context_management, or use an agent with compaction wired in (e.g. 'chatbot')." } ``` ## Compaction events When a checkpoint is written during a turn, the response says so — mirroring how the OpenAI compaction API surfaces a compaction item: - **Non-streaming** responses carry a top-level field: ```json "context_management": [{ "type": "compaction", "status": "completed" }] ``` - **Streaming** responses emit one extra chunk with the same field, early in the stream before the reply text (compaction runs pre-flight). The summary itself is never streamed. The event is purely informational — threads are server-managed, so unlike OpenAI there is nothing the client needs to store or send back. Use it to drive debug panels or usage dashboards. ## Observability and billing Each summarization call appears in tracing as a `thread_compaction` observation (tagged `compaction`) inside the request's trace, so its spend is filterable per feature. Summarization tokens are **not** added to the response's `usage` — they are platform-side cost, typically fractions of a cent per compaction with a small summarizer model. :::tip[Choosing thresholds] `compact_threshold` caps your worst-case history spend per turn; `keep_tokens` sets how much recent conversation survives verbatim. Lower values save more but summarize more aggressively. A `6000` / `3000` split roughly halves late-thread marginal cost for a chat product while keeping the last ~10–15 exchanges word-for-word. ::: # Tool calling > Define functions the model can call, execute them in your app, and send results back. Source: https://truememory-docs.mvstg.com/ai-gateway/features/tool-calling/ · Markdown: https://truememory-docs.mvstg.com/ai-gateway/features/tool-calling/index.md Truememory supports two types of tools on [chat completions](/ai-gateway/chat-completions/) requests: - **Client tools** — You define them in the `tools` parameter. Truememory returns `tool_calls` for your app to execute locally and send results back. - **Internal tools** — `memory_search`, `memory_write`, and `memory_get` run server-side, invisible to the client. See [Memory](/memory/overview/). ## Step 1: Send message with tools ```bash curl -X POST https://your-tie-host/v1/chat/completions \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $TOKEN" \ -d '{ "model": "anthropic/claude-sonnet-4-5", "messages": [ {"role": "user", "content": "What tasks do I have today?"} ], "tools": [ { "type": "function", "function": { "name": "list_tasks", "description": "List user tasks filtered by status", "parameters": { "type": "object", "properties": { "status": { "type": "string", "enum": ["INBOX", "NEXT_UP", "IN_PROGRESS", "WAITING"] } } } } } ] }' ``` ## Step 2: Response with tool_calls When the model wants to call a tool, `finish_reason` is `"tool_calls"`: ```json { "choices": [ { "message": { "role": "assistant", "content": null, "tool_calls": [ { "id": "call_xyz789", "type": "function", "function": { "name": "list_tasks", "arguments": "{\"status\": \"INBOX\"}" } } ] }, "finish_reason": "tool_calls" } ] } ``` ## Step 3: Send tool results Execute the tool locally, then send the result back: ```bash curl -X POST https://your-tie-host/v1/chat/completions \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $TOKEN" \ -d '{ "model": "anthropic/claude-sonnet-4-5", "messages": [ {"role": "user", "content": "What tasks do I have today?"}, {"role": "assistant", "content": null, "tool_calls": [{"id": "call_xyz789", "type": "function", "function": {"name": "list_tasks", "arguments": "{\"status\": \"INBOX\"}"}}]}, {"role": "tool", "tool_call_id": "call_xyz789", "content": "[{\"title\": \"Buy groceries\"}, {\"title\": \"Review PR #42\"}]"} ], "tools": [{"type": "function", "function": {"name": "list_tasks", "parameters": {"type": "object", "properties": {"status": {"type": "string"}}}}}] }' ``` The model may request multiple rounds of tool calls. Keep sending results until `finish_reason` is `"stop"`. # Vision (image input) > Send images to vision-capable models using the content parts format. Source: https://truememory-docs.mvstg.com/ai-gateway/features/vision/ · Markdown: https://truememory-docs.mvstg.com/ai-gateway/features/vision/index.md You can send images to vision-capable models through [chat completions](/ai-gateway/chat-completions/) by using the content parts format instead of a plain string for `content`. This follows the [OpenAI Vision API](https://platform.openai.com/docs/guides/vision) format. Each message's `content` can be either: - A **string** (text-only, the default) - An **array of content parts** mixing text and images ## Content part types | Type | Fields | Description | |------|--------|-------------| | `text` | `text` | A text segment | | `image_url` | `image_url.url`, `image_url.detail` | An image, either as a direct URL or base64 data URI | The `detail` parameter controls image resolution processing: | Value | Description | |-------|-------------| | `auto` | Let the model decide (default) | | `low` | Faster, lower resolution, fewer tokens | | `high` | Higher resolution, more tokens | ## Sending an image URL ```bash curl -X POST https://your-tie-host/v1/chat/completions \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $TOKEN" \ -d '{ "model": "anthropic/claude-sonnet-4-5", "messages": [ { "role": "user", "content": [ { "type": "text", "text": "What is in this image?" }, { "type": "image_url", "image_url": { "url": "https://example.com/photo.jpg", "detail": "auto" } } ] } ] }' ``` ## Sending a base64-encoded image ```bash curl -X POST https://your-tie-host/v1/chat/completions \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $TOKEN" \ -d '{ "model": "anthropic/claude-sonnet-4-5", "messages": [ { "role": "user", "content": [ { "type": "text", "text": "Describe this image." }, { "type": "image_url", "image_url": { "url": "data:image/jpeg;base64,/9j/4AAQSkZJRg...", "detail": "low" } } ] } ] }' ``` :::note Vision support depends on the underlying model. Models like `anthropic/claude-sonnet-4-5`, `openai/gpt-5.1`, and `vertexai/gemini-2.5-flash` support image input. If a model does not support vision, the provider will return an error. ::: # Image Generation Source: https://truememory-docs.mvstg.com/ai-gateway/image-generation/ · Markdown: https://truememory-docs.mvstg.com/ai-gateway/image-generation/index.md Truememory provides an OpenAI-compatible image generation endpoint that routes to multiple providers. Any client that works with OpenAI's `/v1/images/generations` works with Truememory by changing the base URL. :::note[Model ID format on this endpoint] Image models use bare OpenAI IDs or `google/`-prefixed IDs (`google/gemini-3.1-flash-image`) — not the `vertexai/` prefix chat uses. See [Model ID formats](/ai-gateway/overview/#model-id-formats). ::: All requests require a Bearer token from [Truememory Auth](/authentication/overview/). ## Endpoint ``` POST /v1/images/generations ``` ## Supported Models | Model | Provider | Format | |-------|----------|--------| | `openai/gpt-image-1.5` | OpenAI | `url` or `b64_json` | | `openai/dall-e-3` | OpenAI | `url` or `b64_json` | | `google/gemini-3.1-flash-image` | Google Gemini ("Nano Banana 2", Vertex AI) | `b64_json` only | | `google/gemini-3.1-flash-lite-image` | Google Gemini ("Nano Banana 2 Lite", Vertex AI) | `b64_json` only | | `google/gemini-2.5-flash-image` | Google Gemini ("Nano Banana", Vertex AI) | `b64_json` only | | `google/gemini-3-pro-image` | Google Gemini ("Nano Banana Pro", Vertex AI) | `b64_json` only | Use the `provider/model` format (e.g. `openai/gpt-image-1.5`). You can also omit the provider prefix for OpenAI models (e.g. `dall-e-3`). The Gemini image models ("Nano Banana" and "Nano Banana Pro") generate a single image per request (`n` must be `1`). ## Parameters Send as JSON: | Parameter | Type | Default | Description | |-----------|------|---------|-------------| | `model` | string | required | Image model to use (e.g. `openai/gpt-image-1.5`, `google/gemini-3.1-flash-image`) | | `prompt` | string | required | Text description of the desired image | | `n` | integer | 1 | Number of images to generate. OpenAI: 1–10. Google: 1 only — Gemini image models return a single image. | | `size` | string | null | Image size: `1024x1024`, `1792x1024`, `1024x1792`. For Google models, this maps to aspect ratios (1:1, 16:9, 9:16). | | `quality` | string | null | Image quality (OpenAI only): `auto`, `low`, `medium`, `high`, `hd` | | `response_format` | string | `b64_json` | `url` or `b64_json`. Google models always return `b64_json` regardless of this setting. | | `style` | string | null | Image style (dall-e-3 only): `natural`, `vivid` | ## Examples ### OpenAI (gpt-image-1.5) ```bash curl -X POST https://your-tie-host/v1/images/generations \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $TOKEN" \ -d '{ "model": "openai/gpt-image-1.5", "prompt": "A serene mountain landscape at sunset with a reflective lake", "n": 1, "size": "1024x1024", "quality": "auto" }' ``` ### Google Gemini ("Nano Banana") ```bash curl -X POST https://your-tie-host/v1/images/generations \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $TOKEN" \ -d '{ "model": "google/gemini-3.1-flash-image", "prompt": "A serene mountain landscape at sunset with a reflective lake", "size": "1024x1024" }' ``` Use `google/gemini-3-pro-image` for the higher-quality "Nano Banana Pro" model. ### DALL-E 3 ```bash curl -X POST https://your-tie-host/v1/images/generations \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $TOKEN" \ -d '{ "model": "openai/dall-e-3", "prompt": "A serene mountain landscape at sunset with a reflective lake", "style": "natural" }' ``` ## Response The response follows OpenAI's image generation format: ```json { "created": 1711900000, "data": [ { "b64_json": "iVBORw0KGgo...", "url": null, "revised_prompt": "A tranquil mountain landscape during golden hour..." } ] } ``` | Field | Type | Description | |-------|------|-------------| | `created` | integer | Unix timestamp of when the response was generated | | `data` | array | List of generated images | | `data[].b64_json` | string or null | Base64-encoded image data (when `response_format` is `b64_json`) | | `data[].url` | string or null | Temporary URL to the generated image (when `response_format` is `url`, OpenAI models only) | | `data[].revised_prompt` | string or null | The prompt that was actually used (OpenAI models may revise your prompt for safety/quality) | ## Provider Differences | Feature | OpenAI | Google Gemini (Nano Banana) | |---------|--------|-----------------------------| | Max images per request | 10 | 1 | | `response_format: "url"` | Temporary URL returned | Not supported — always returns `b64_json` | | `style` parameter | Supported (dall-e-3 only) | Ignored | | `quality` parameter | Supported | Ignored | | `revised_prompt` | Returned when prompt is modified | Not returned | | Size handling | Passed as-is (e.g. `1024x1024`) | Mapped to aspect ratio (e.g. `1:1`) | ## SDK Integration Since the endpoint is OpenAI-compatible, you can use the standard OpenAI SDK: ### Python ```python from openai import OpenAI client = OpenAI( base_url="https://your-tie-host/v1", api_key="$TOKEN", ) response = client.images.generate( model="openai/gpt-image-1.5", prompt="A serene mountain landscape at sunset", n=1, size="1024x1024", ) print(response.data[0].b64_json[:50]) # base64 image data ``` ### TypeScript / Node.js ```typescript import OpenAI from "openai"; const client = new OpenAI({ baseURL: "https://your-tie-host/v1", apiKey: "$TOKEN", }); const response = await client.images.generate({ model: "openai/gpt-image-1.5", prompt: "A serene mountain landscape at sunset", n: 1, size: "1024x1024", }); console.log(response.data[0].b64_json?.slice(0, 50)); ``` ## Error Handling | Status | Code | When | |--------|------|------| | 400 | `VALIDATION_ERROR` | Unsupported model, `n` exceeds provider limit, invalid parameters | | 401 | `AUTH_TOKEN_MISSING` | No Bearer token provided | | 502 | `SERVICE_UPSTREAM_ERROR` | The upstream provider (OpenAI or Google) returned an error | | 503 | `SERVICE_UNAVAILABLE` | Required provider credentials not configured (e.g. `OPENAI_API_KEY` or `GCP_PROJECT_ID`) | # Knowledge Packs Source: https://truememory-docs.mvstg.com/ai-gateway/knowledge-packs/ · Markdown: https://truememory-docs.mvstg.com/ai-gateway/knowledge-packs/index.md A **knowledge pack** is a document you upload to Truememory — PDF, Markdown, or plain text — so an agent can quote from it when it answers. Truememory parses the file, splits it into chunks, embeds them, and stores them. At request time it pulls the passages most relevant to what the user asked and adds them to the agent's system prompt as a **Relevant Document Context** section. The point is grounding: instead of hoping the model already knows your handbook, pricing sheet, or SOP, you attach the source and let the agent answer from it. Your app decides which packs apply to a given request; Truememory handles storage, retrieval, and access. To attach a whole set of packs at once instead of listing IDs on every request, group them under a [brain](/ai-gateway/brains/). **Think of it like** a shared reference shelf. You put a document on the shelf (upload), tag which agents or skills may pull it down (see [Retrieval](#retrieval)), and the agent quotes the relevant page when it answers. **Where that metaphor breaks down:** the agent never receives the whole document. Retrieval returns only the highest-scoring chunks for the current question, not the full file. ```mermaid flowchart TD subgraph Ingest["1 · Upload once"] direction LR U["Upload a file
PDF · Markdown · text"] --> P["Parse → chunk → embed"] P --> S["Stored & searchable
status: ready"] end subgraph Ask["2 · Every chat request"] direction LR R["POST /v1/chat/completions
skill_id and/or knowledge_pack_ids"] --> E["Embed the user's message"] E --> H["Hybrid search
vector + keyword (RRF)"] H --> C["Inject top chunks as
Relevant Document Context"] C --> A["Grounded answer"] end S -.->|"only visible, ready packs"| H style U fill:#f0f9ff,stroke:#3b82f6,color:#181818 style P fill:#e6f4ff,stroke:#0a99ff,color:#181818 style S fill:#ecfdf5,stroke:#10b981,color:#181818 style R fill:#f0f9ff,stroke:#3b82f6,color:#181818 style E fill:#e6f4ff,stroke:#0a99ff,color:#181818 style H fill:#e6f4ff,stroke:#0a99ff,color:#181818 style C fill:#e6f4ff,stroke:#0a99ff,color:#181818 style A fill:#ecfdf5,stroke:#10b981,color:#181818 ``` All knowledge pack endpoints require authentication. User-scoped endpoints (`/knowledge-packs`) accept either: - a user Bearer token from [Truememory Auth](/authentication/overview/) - the calling app's service token plus `X-On-Behalf-Of: ` when your backend is proxying for a user Admin endpoints (`/admin/knowledge-packs/*`) require the service token or an admin user token — see [Admin endpoints](#admin-endpoints). ## Retrieval This is how a pack actually reaches the model. On a `/v1/chat/completions` request, opt in with any of these fields: | Field | Type | What it does | |---|---|---| | `skill_id` | string \| null | Pull packs tagged with this skill — the `id` of a [skill registry](/ai-gateway/skills/) entry (see [Assigning packs to a skill](#assigning-packs-to-a-skill)) | | `knowledge_pack_ids` | string[] \| null | Pull these specific packs by id | | `brain_id` | string \| null | Pull every pack in a [brain](/ai-gateway/brains/). Ignored when `knowledge_pack_ids` is also set | | `brain_ids` | string[] \| null | Pull every pack in several brains, merged and deduplicated. `brain_id` is the singular alias — both are honoured together | All are optional. `skill_id` and `knowledge_pack_ids` can be combined; `brain_id`/`brain_ids` resolve to a set of pack IDs and are a shorthand for the second. **When all are absent, no document context is injected** — retrieval only runs when you ask for it. ```bash POST https://your-tie-host/v1/chat/completions Authorization: Bearer $TOKEN Content-Type: application/json { "model": "anthropic/claude-sonnet-4-5", "messages": [ { "role": "user", "content": "What's our refund window for annual plans?" } ], "knowledge_pack_ids": ["7f3c..."] } ``` Under the hood: Truememory embeds the user's latest message, runs a hybrid search (vector similarity + keyword match, fused with reciprocal rank fusion) over the chunks you're allowed to see, and injects the top results into the system prompt under a **Relevant Document Context** heading. Only packs with status `ready` are searched. A pack is retrievable on a request only if the caller can see it — see [Visibility](#visibility). `skill_id` and `knowledge_pack_ids` narrow *within* what's visible; they never widen it. ## Uploading a pack ```bash POST https://your-tie-host/knowledge-packs?skill_id=&app_id=vibrantly Authorization: Bearer $TOKEN Content-Type: multipart/form-data file=@handbook.pdf ``` Accepted file types are PDF (`application/pdf`), Markdown (`text/markdown`), and plain text (`text/plain`). The `skill_id` and `app_id` query params are optional — they tag and scope the pack (see [Visibility](#visibility) and [Assigning packs to a skill](#assigning-packs-to-a-skill)). Ingestion runs in the background, so the response comes back immediately with `status: "pending"`. The status then moves through: `pending` → `processing` → `ready` (searchable) — or `error` if parsing or embedding fails. Poll `GET /knowledge-packs` (or the admin detail endpoint) to watch the status. A pack is only returned by [Retrieval](#retrieval) once it reaches `ready`. If ingestion fails, re-upload — there is no automatic retry. ```bash GET https://your-tie-host/knowledge-packs Authorization: Bearer $TOKEN ``` Returns the packs the calling user owns — both the ones they uploaded and the ones they've been [granted access to](#ownership-and-entitlements). Delete a pack you own with `DELETE /knowledge-packs/{pack_id}`. ## Visibility Every pack has three tiers of visibility, derived from `user_id` and `app_id`: | `user_id` | `app_id` | Who can retrieve it | |---|---|---| | set | — | Only that user | | `null` | `vibrantly` | Every user of that app | | `null` | `null` | Everyone, every app (global) | Retrieval always applies this filter first, then narrows by `skill_id` / `knowledge_pack_ids`. A user cannot pull a pack scoped to another user or another app, even if they pass its id. ## Ownership and entitlements Visibility answers "who is allowed to see this." **Entitlements** answer a different question: "which specific users own this pack." A pack can be a product that's granted (or sold) to many users, and ownership is **cross-app** — a grant made through any app makes the pack show up for that user everywhere they go. **Think of it like** a per-seat license. The pack is the product; an entitlement is one user's seat, valid across every app on the platform. The pack's own `user_id` still records who *uploaded* it; entitlements are a separate grant layer on top. The `app_id` on an entitlement records the app the user acquired it through — attribution for billing, nothing more. It does not restrict where the pack appears; a user with a grant sees it from every app. ```bash # Grant a user access to a pack (idempotent) POST https://your-tie-host/admin/knowledge-packs/{pack_id}/entitlements Authorization: Bearer $AUTH_SECRET Content-Type: application/json { "user_id": "1111...", "app_id": "blinklife" } ``` | Endpoint | Behavior | |---|---| | `POST /admin/knowledge-packs/{id}/entitlements` | Grant ownership to a user. Re-granting updates the acquiring `app_id` | | `DELETE /admin/knowledge-packs/{id}/entitlements/{user_id}` | Revoke a user's access | | `GET /admin/knowledge-packs/{id}/entitlements` | List everyone with access, enriched with each user's name and email | Truememory stores the grant only — it takes no part in pricing or purchase flow. Your backend confirms a sale (or any access decision) and records it here. ## Managing the document These admin endpoints let an operator review and revise a pack after upload. | Endpoint | Behavior | |---|---| | `GET /admin/knowledge-packs/{id}` | Fetch a single pack's metadata | | `GET /admin/knowledge-packs/{id}/content` | The extracted text (reconstructed from the stored chunks), plus `has_original` | | `PUT /admin/knowledge-packs/{id}/content` | Replace the extracted text and re-embed it in place | | `PUT /admin/knowledge-packs/{id}/file` | Re-upload a file; re-ingests in place, keeping the same id and its entitlements | | `GET /admin/knowledge-packs/{id}/file` | Download the original uploaded file | | `GET /admin/knowledge-packs/{id}/events` | Change log: `uploaded`, `text_edited`, `file_reuploaded` with timestamps | Editing the text or re-uploading a file wipes the old chunks and re-embeds, so retrieval reflects the change once the pack returns to `ready`. Both keep the same pack id — so entitlements and any `skill_id` tag survive the change. **Original file availability.** Truememory only retains the raw uploaded file when object storage is configured (`GCS_BUCKET` set). Without it, ingestion keeps the extracted text and embeddings but discards the original — `has_original` is `false` and the download endpoint returns 404. The extracted-text endpoints work either way. ## Assigning packs to a skill A pack carries an optional `skill_id` tag. Tagging a pack to a skill is what lets a chat request pull it in by passing that `skill_id` (see [Retrieval](#retrieval)). Set or clear the tag without re-uploading: ```bash PUT https://your-tie-host/admin/knowledge-packs/{pack_id}/skill Authorization: Bearer $AUTH_SECRET Content-Type: application/json { "skill_id": "9a2b1f6e-1111-2222-3333-444455556666" } ``` The tag is a soft association, not ownership: deleting the skill clears the tag but keeps the document. A pack holds one `skill_id` at a time, so re-assigning moves it. ## Admin endpoints The `/admin/knowledge-packs/*` block manages packs across all users without the per-user visibility filter. It's gated by the Truememory service token (`AUTH_SECRET`) or an admin user token; end-user tokens cannot reach it. Use it to create global or app-scoped packs, list across users, and run the management and entitlement operations above. ```bash # Create a global pack (user_id = null) scoped to one app POST https://your-tie-host/admin/knowledge-packs?app_id=vibrantly Authorization: Bearer $AUTH_SECRET Content-Type: multipart/form-data file=@pricing.md ``` `GET /admin/knowledge-packs` lists across everyone with `user_id`, `app_id`, `skill_id`, and `status` filters plus `limit`/`offset`. ## Common pitfalls - **Expecting a pack to be used automatically.** It isn't. A request injects document context only when it passes `skill_id`, `knowledge_pack_ids`, `brain_id`, or `brain_ids`. Remember the precedence: explicit `knowledge_pack_ids` override (and disable) the brain fields. Uploading a pack does nothing on its own. - **Searching a pack that isn't `ready` yet.** Ingestion is asynchronous. A freshly uploaded pack is `pending`/`processing` and is skipped by retrieval until it reaches `ready`. - **Passing an id you can't see.** `knowledge_pack_ids` narrows within your visibility; it never bypasses it. An id scoped to another user or app returns nothing. - **Confusing the acquiring `app_id` with a scope.** On an entitlement, `app_id` is attribution only. Ownership is cross-app — the pack shows up for that user from every app regardless. - **Expecting the original file back without object storage.** When `GCS_BUCKET` is unset, only the extracted text is retained; the original download is unavailable. - **Deleting a skill to remove its documents.** Deleting a skill only clears the `skill_id` tag on its packs; the packs remain. Delete the packs themselves if that's the intent. # Overview > An OpenAI-compatible gateway with persistent memory, personas, and model routing built in. Source: https://truememory-docs.mvstg.com/ai-gateway/overview/ · Markdown: https://truememory-docs.mvstg.com/ai-gateway/overview/index.md The AI Gateway sits between your application and LLM providers. It exposes the same `/v1/chat/completions` endpoint every OpenAI SDK already knows — and layers persistent memory, personas, tool-call routing, and observability on top, so your app doesn't have to build them. ## Base URL and placeholders Truememory is deployed per environment, so examples across these docs use the placeholder **`https://your-tie-host`** — replace it with your instance's gateway URL (ask whoever operates your deployment; for the Mindvalley staging environment this is provided with your credentials). "TIE" is the platform's internal codename — you'll see it in the placeholder and in a few wire-level names (`tie.bearer`); it's the same product. Two credentials appear in examples: `$TOKEN` (a user's Bearer token from [Truememory Auth](/authentication/overview/)) and `$AUTH_SECRET` (the backend service-account secret). ## Features - **OpenAI-compatible** — Drop-in replacement for OpenAI, Cloudflare AI Gateway, or LiteLLM - **Custom prompt mode** — Send your own `system` or `developer` message to override the agent's built-in prompt while keeping memory and personas - **Memory** — Persistent user memory via Graphiti knowledge graph, injected automatically - **Personas** — Custom system prompts per user, injected automatically - **Client tool calling** — Pass tool definitions per-request, Truememory routes tool_calls back for execution - **Internal tools** — Memory search/write execute server-side, invisible to the client - **Thread persistence** — Maintain conversation state across requests - **Audio** — Speech-to-text and text-to-speech via OpenAI-compatible endpoints - **Image Generation** — Generate images via OpenAI-compatible `/v1/images/generations` with OpenAI and Google Gemini image models - **Observability** — All requests traced via Langfuse - **Multi-agent** — Select different agents via `X-Agent-Id` header ## Supported providers - [Anthropic](https://docs.anthropic.com/) (via Vertex AI) — Claude Haiku 4.5, Claude Sonnet 4.5, Claude Sonnet 4.6, Claude Opus 4.5, Claude Opus 4.6 - [OpenAI](https://platform.openai.com/) — GPT-5 Nano, GPT-5 Mini, GPT-5.1 - [Google Vertex AI](https://cloud.google.com/vertex-ai) — Gemini 2.5 Flash, Gemini 2.5 Flash Lite, Gemini 2.5 Pro, Gemini 3 Flash (preview), Gemini 3.1 Pro (preview) - [Z.AI](https://docs.z.ai/) — the GLM family (GLM-4.x, GLM-5.x) Query `GET /info` on your instance to see which models are currently available — it returns the exact wire IDs. Browse them with runnable examples in the [model catalog](/models/). ### Model ID formats The `model` value is provider-prefixed, and the prefix depends on the endpoint: | Endpoint | Anthropic | OpenAI | Google | Z.AI | |---|---|---|---|---| | `/v1/chat/completions` | `anthropic/claude-sonnet-4-6` | `openai/gpt-5.1` | `vertexai/gemini-2.5-flash` | `zai/glm-4.7` | | `/v1/audio/*`, `/v1/images/*` | — | bare IDs accepted (`whisper-1`, `gpt-4o-mini-tts`) | `google/gemini-2.5-flash-tts`, `google/gemini-2.5-flash-image` | — | Yes, Google appears as `vertexai/` on chat and `google/` on media endpoints — copy the prefix from the endpoint's own docs when in doubt. ## Where to start | Page | Description | |------|-------------| | [Unified API](/ai-gateway/chat-completions/) | Send messages via the OpenAI-compatible `/v1/chat/completions` endpoint | | [Agents](/ai-gateway/agents/) | Multi-agent routing, agent selection, and the `X-Agent-Id` header | | [Memory](/memory/overview/) | Persistent memory, context prefetch, and memory management | | [Personas](/ai-gateway/personas/) | Custom system prompts per user — create, activate, and manage | # Personas Source: https://truememory-docs.mvstg.com/ai-gateway/personas/ · Markdown: https://truememory-docs.mvstg.com/ai-gateway/personas/index.md A **persona** is a saved identity for the AI your user talks to. Instead of sending a system prompt with every request, you store it once as a persona; when the user has an *active* persona, Truememory injects its prompt into every conversation automatically. A persona carries five things: - `name` — what the AI is called (e.g. "Aria") - `archetype` — a short personality-type label (e.g. `SAGE`, `EXPLORER`); apps map it to their own avatars and visual styles - `system_prompt` — the compiled instructions the model actually receives - `soul_document` / `human_profile` — optional structured personality (tone, boundaries, learned adaptations) and free-form notes about the user; shared across apps so the AI feels like the same "person" everywhere - `avatar` / `avatar_config` — an avatar image URL, and an optional JSON object your app defines for a dynamic avatar the user can change (look, colours, accessories, voice) Personas come in two kinds: **presets** (system-provided, read-only) and the user's own **non-preset** persona — each user has exactly one, and in app language that persona *is* "the user's agent". :::note[Personas are not agents] `X-Agent-Id` selects a server-side processing pipeline ([Agents](/ai-gateway/agents/)); a persona is the data that pipeline loads to speak as your user's companion. Named companion → persona. Different tools/safety behavior → agent. ::: You can also override the active persona on a per-request basis by passing `persona_id` in the `/v1/chat/completions` request body. This uses the specified persona for that request only, without changing the user's active persona. If `persona_id` is omitted, the active persona is used. See [Chat Completions](/ai-gateway/chat-completions/) for details. If you need to override the agent's built-in prompt for a single request, send a `system` or `developer` message in `/v1/chat/completions`. Your message replaces the agent's base instructions, but the active persona and memory context are still injected. All persona endpoints require a Bearer token from [Truememory Auth](/authentication/overview/). ## The persona is the user's agent Each user has **one non-preset persona** — their agent — shared across every app on the platform. Truememory is the single source of truth: an agent created in one app is immediately available in all others, and shared state (`archetype`, `avatar`, `avatar_config`, `soul_document`, `human_profile`) lives on the persona so apps never sync it themselves. For the create-or-reuse flow (check `GET /personas` first, never create a second non-preset persona), follow the [Agent Creation cookbook](/platform/agent-creation/). For collecting identity fields conversationally, see [Onboarding](/platform/onboarding/). ## List Personas Returns both preset personas (system-defined personas available to all users; these cannot be edited or deleted) and custom personas created by the current user. The `is_active` flag indicates which persona is currently selected. ```bash GET https://your-tie-host/personas Authorization: Bearer $TOKEN ``` **Response:** ```json [ { "id": "preset-formal", "user_id": null, "name": "Formal Writer", "description": "Writes in formal, professional tone", "system_prompt": "You always write in a formal, professional tone.", "is_preset": true, "is_active": false, "archetype": null, "created_at": "2026-01-01T00:00:00Z", "updated_at": "2026-01-01T00:00:00Z" }, { "id": "custom-abc123", "user_id": "11111111-2222-3333-4444-555555555555", "name": "Aria", "description": null, "system_prompt": "You are Aria, a SAGE companion...", "is_preset": false, "is_active": true, "archetype": "SAGE", "soul_document": { "voiceAndStyle": [{ "text": "Speak with warmth and clarity", "source": "SAGE" }], "antiPatterns": [], "learnedAdaptations": [], "boundaries": [], "customInstructions": [] }, "human_profile": "Jules is a software engineer working on an AI startup in KL...", "avatar": "https://storage.example.com/avatars/aria.png", "avatar_config": { "skin": "teal", "accessories": ["glasses"], "voice": "warm-1" }, "created_at": "2026-03-20T10:00:00Z", "updated_at": "2026-03-20T10:00:00Z" } ] ``` ## Create a Custom Persona ```bash POST https://your-tie-host/personas Authorization: Bearer $TOKEN Content-Type: application/json { "name": "Aria", "description": "SAGE companion for BlinkLife", "system_prompt": "You are Aria, a SAGE companion...", "archetype": "SAGE", "soul_document": { "voiceAndStyle": [], "antiPatterns": [], "learnedAdaptations": [], "boundaries": [], "customInstructions": [] }, "human_profile": null, "avatar": "https://storage.example.com/avatars/aria.png", "avatar_config": { "skin": "teal", "accessories": ["glasses"], "voice": "warm-1" } } ``` Returns the created persona object with HTTP status `201 Created`. Only `name` and `system_prompt` are required. `archetype` is the app-defined personality type — each app maps this to its own avatar assets and visual style. | Field | Type | Description | |---|---|---| | `soul_document` | JSON (optional) | Structured personality: `voiceAndStyle`, `antiPatterns`, `learnedAdaptations`, `boundaries`, `customInstructions`. Each section is an array of `{ text, source }` entries. | | `human_profile` | string (optional) | Free-form text the AI writes about the specific user — goals, communication style, values, context. | | `avatar` | string (optional) | URL of the persona's avatar image. Shared across all apps using this persona. | | `avatar_config` | JSON object (optional) | Settings for a dynamic avatar. Your app chooses the keys. See [Avatar config](#avatar-config). | ### Avatar config `avatar_config` is a place for your app to keep the settings of a dynamic avatar — the things the user can change, such as look, colours, accessories, or voice. Truememory does not read it: you send a JSON object, and every persona response gives the same object back. It is never added to the prompt, so it does not change how the AI talks. Use `soul_document` for that. - It must be a JSON object (`{...}`). A string, array, or number gets a `422` error. - It can be at most 64 KB as JSON. Store images somewhere else and put their URLs in the object. - On update, the new object replaces the old one completely. Truememory does not merge them, so send the full object each time. Send `null` to remove it. - Other apps can read it. On a public persona, every user can see it, like `avatar`. Do not put private data in it. **Before creating, always check if a non-preset persona already exists** (see [The persona is the user's agent](#the-persona-is-the-users-agent) above). ## Set Active Persona ```bash PUT https://your-tie-host/personas/active/{persona_id} Authorization: Bearer $TOKEN ``` **Response:** ```json { "status": "active", "persona_id": "preset-formal" } ``` ## Get Active Persona ```bash GET https://your-tie-host/personas/active Authorization: Bearer $TOKEN ``` Returns the active persona object with `is_active: true` and `activated_at` (when it was set active), or `404` if no persona is active. ## Clear Active Persona Reverts to default behavior (no custom system prompt). ```bash DELETE https://your-tie-host/personas/active Authorization: Bearer $TOKEN ``` **Response:** ```json { "status": "cleared" } ``` ## Update a Custom Persona ```bash PUT https://your-tie-host/personas/{persona_id} Authorization: Bearer $TOKEN Content-Type: application/json { "name": "Updated Name", "system_prompt": "Updated prompt...", "soul_document": { "voiceAndStyle": [{ "text": "Warm, direct, playful", "source": "user_preference" }], "antiPatterns": [{ "text": "Never uses corporate jargon", "source": "learned" }] }, "human_profile": "Jules now lives in Singapore and is focused on...", "avatar": "https://storage.example.com/avatars/aria-v2.png", "avatar_config": { "skin": "teal", "accessories": ["glasses", "scarf"], "voice": "warm-1" } } ``` Returns the updated persona object, or `404` if the persona does not exist, is not owned by the current user, or is a preset. ## Delete a Custom Persona ```bash DELETE https://your-tie-host/personas/{persona_id} Authorization: Bearer $TOKEN ``` **Response:** ```json { "status": "deleted", "persona_id": "custom-abc123" } ``` Returns `404` if the persona does not exist, is not owned by the current user, or is a preset. # Getting started > From zero to a streaming, memory-backed chat response in three requests. Source: https://truememory-docs.mvstg.com/ai-gateway/quickstart/ · Markdown: https://truememory-docs.mvstg.com/ai-gateway/quickstart/index.md Truememory's AI Gateway is OpenAI-compatible: if your code can call OpenAI, pointing it at Truememory is a base-URL change. This page gets you from nothing to a streaming, memory-backed response. ## 1. Get your two inputs - **Base URL** — Truememory is deployed per environment; the docs use the placeholder `https://your-tie-host`. Ask whoever operates your deployment for your gateway URL. - **A credential** — either a **user token** (sign a user in through [Truememory Auth](/authentication/email-password/) and use the returned JWT) or, from a backend, the shared **service secret** ([`AUTH_SECRET`](/authentication/service-accounts/), issued by the platform team) with `X-On-Behalf-Of` to act for a user. ## 2. First request ```bash curl -X POST https://your-tie-host/v1/chat/completions \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $TOKEN" \ -d '{ "model": "anthropic/claude-sonnet-4-6", "messages": [{"role": "user", "content": "Hello! Who are you?"}] }' ``` That's a complete integration: the response comes back in OpenAI's shape, and if the user has an [active persona](/ai-gateway/personas/) or existing [memory](/memory/overview/), both were injected automatically — you never manage them per request. ## 3. Stream it Add `"stream": true` and read Server-Sent Events; content arrives in `choices[0].delta.content` and the stream ends with `data: [DONE]`: ```bash curl -N -X POST https://your-tie-host/v1/chat/completions \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $TOKEN" \ -d '{ "model": "anthropic/claude-sonnet-4-6", "stream": true, "messages": [{"role": "user", "content": "Tell me a two-line poem."}] }' ``` ## 4. Keep the conversation Pass a `thread_id` (any stable string you generate) and Truememory persists the conversation so follow-up requests have context: ```bash curl -X POST https://your-tie-host/v1/chat/completions \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $TOKEN" \ -d '{ "model": "anthropic/claude-sonnet-4-6", "thread_id": "0f2e5f3a-demo-thread", "messages": [{"role": "user", "content": "Remember this number: 42"}] }' ``` ## Where to next | You want | Go to | |---|---| | Every request parameter, tools, caching, compression | [Unified API](/ai-gateway/chat-completions/) | | A named AI companion with a personality | [Personas](/ai-gateway/personas/) | | Long-term memory across sessions and apps | [Agent Memory](/memory/overview/) | | Ground answers in your documents | [Knowledge Packs](/ai-gateway/knowledge-packs/) | | Speech, images, real-time voice | [Audio](/ai-gateway/audio/) · [Images](/ai-gateway/image-generation/) · [Voice](/ai-gateway/voice/) | | Which models you can use | [Model catalog](/models/) | # Skills Source: https://truememory-docs.mvstg.com/ai-gateway/skills/ · Markdown: https://truememory-docs.mvstg.com/ai-gateway/skills/index.md Skills are reusable prompt templates stored in Truememory — short, named instructions like "summarize this thread" or "translate to formal English" that an app can pull up by key and run on demand. Truememory stores the template; the calling app decides when to invoke it and how to render it (a slash command, a button in the UI, a quick action menu, etc.). If your app already builds prompt presets on its own backend, skills move that storage into Truememory so the same set is available everywhere the user goes — BlinkLife, Vibrantly, and any future app on the platform — without each app maintaining its own copy. These endpoints manage **app-facing skill records in the server database**. They are separate from the repository's `.agents/skills` or prompt-instruction folders, which are development-time instructions for LLM agents working on this codebase. All skill endpoints require authentication. User-scoped endpoints accept either: - a user Bearer token from [Truememory Auth](/authentication/overview/) - the calling app's service token plus `X-On-Behalf-Of: ` when your backend is proxying the request for a user Admin endpoints require the service token (see [Admin endpoints](#admin-endpoints) below). ## Integration flow Use skills in two steps: 1. Store or fetch the skill template through `/skills`. 2. Invoke your agent by sending the skill's `prompt` through `/v1/chat/completions`, usually in `skill_instructions` or as part of your caller-composed prompt. For example, a slash-command menu can load `GET /skills`, render each skill by `name`, `icon`, and `argument_hint`, then fetch the selected row by `key` before sending its `prompt` into the chat request. ```bash # Backend proxying for a user GET https://your-tie-host/skills?is_active=true Authorization: Bearer $AUTH_SECRET X-On-Behalf-Of: 11111111-2222-3333-4444-555555555555 ``` ```bash # Later, invoke the selected template POST https://your-tie-host/v1/chat/completions Authorization: Bearer $AUTH_SECRET X-On-Behalf-Of: 11111111-2222-3333-4444-555555555555 Content-Type: application/json { "model": "anthropic/claude-sonnet-4-5", "messages": [ { "role": "user", "content": "Summarize this thread for my product standup." } ], "skill_instructions": "Summarize the conversation so far in 3 short bullet points.", "metadata": { "active_skill": "summarize-thread" } } ``` Truememory does not automatically inject a skill's `prompt` into chat completion requests. Your app decides which skill applies, reads the stored `prompt`, and passes it via `skill_instructions`. There is one automatic connection: passing the skill's `id` as `skill_id` on a chat request retrieves every [knowledge pack](/ai-gateway/knowledge-packs/#retrieval) tagged with that skill. ## What a skill actually is A skill is a row of caller-defined data. Truememory does not interpret the `prompt` text, the `allowed_tools` list, the `category`, or the `metadata` blob — it stores and returns them. Your app decides what to do with each field when it invokes the skill. This matters because: - A skill named `summarize` does not automatically run a summarization — your app reads the skill's `prompt` and sends it through `/v1/chat/completions` (or wherever it routes prompts). - `allowed_tools` is metadata, not enforcement. Truememory will not block tool calls based on this list; the caller is responsible for honoring it at invocation time. - `icon` and `argument_hint` exist to help your UI render the skill — Truememory never reads them. `category` is the one exception: Truememory accepts it as an exact-match filter on `GET /skills?category=…`. - `metadata` is a caller-owned JSON object. Use it for integration hints your app understands, not for Truememory control flags. Anything Truememory acts on is a real field instead — see `use_case` and `detection_patterns` below, which were promoted out of `metadata` for exactly this reason. ## Trigger fields Two fields describe *when* a skill applies, as opposed to what it does. Both are optional — a skill without them is still invokable by key, it just never gets selected automatically. | Field | Type | Purpose | |---|---|---| | `use_case` | string | When the skill applies, in natural language: `"Triggers when a user shares a photo of food, a menu, or ingredients."` | | `detection_patterns` | string[] | Example utterances that should invoke it: `["what are the macros in this", "can I eat these berries"]` | Write `use_case` to describe the *trigger*, not the behaviour — "Triggers when the user asks for a recap" beats "Summarizes conversations." `detection_patterns` are matched **semantically, not literally**. List distinct phrasings rather than keyword variants: "can I eat these berries" also covers "are these mushrooms safe", so a separate entry for every synonym is wasted effort. Up to 32 entries, 500 characters each. Both were previously written into `metadata`. That was the right place while Truememory only stored them; once selection reads them they are control flags, which `metadata` explicitly is not for. Existing values were copied into the new columns by migration 037, and reads still fall back to `metadata` so a client that has not switched keeps working. Send them as real fields going forward. Keys that remain caller-owned — `platform_skill_type`, `icon_data`, `model_tier` — should stay in `metadata`. Truememory does not act on them. ### Automatic selection Send `skill_selection: "auto"` on a chat completion and Truememory matches the user's message against the trigger data of every skill visible to that caller and app, injecting the closest matches: ```json { "model": "anthropic/claude-sonnet-4-5", "messages": [{ "role": "user", "content": "what are the macros in this" }], "skill_selection": "auto" } ``` Defaults to `"off"`, so nothing changes until you ask for it. It combines with `skills`: explicitly named keys always apply, and matches are appended after them without duplicating one you already named. Only skills you could already list are candidates, and only those carrying trigger data — a skill with no `use_case` or `detection_patterns` is never selected automatically, though it stays invokable by key. At most 3 skills are selected per turn. Matching is **semantic**, so paraphrases work: "how many calories in this" selects a skill whose patterns are "what are the macros in this" and "scan this nutrition label", sharing no words with either. Messages carrying little information ("hi", "thanks") skip matching entirely. Selection is best-effort. If matching cannot run, the turn proceeds with no automatically selected skills rather than failing — unlike a key you named in `skills`, which 404s when it cannot be resolved. **Think of it like:** a shared snippet library. The library stores the snippet text; the editor that opens it decides how to expand and run it. **Where that metaphor breaks down:** unlike most snippet libraries, skills have ownership scoping (presets vs. custom) and a fork relationship built in. ## Presets and custom skills Every skill is one of two kinds: | Kind | `user_id` | `is_preset` | Who can see it | Who can mutate it | |---|---|---|---|---| | **Preset** | `null` | `true` | Every authenticated user | Admins only (service token) | | **Custom** | the owner | `false` | Only the owner | Only the owner | When a user owns a custom skill whose `key` matches a preset's `key`, the user's version wins on `GET /skills/by-key/{key}`. This is intentional — it lets users override a preset without losing the preset for everyone else. ## Key naming `key` is the stable identifier your app uses to find the skill (typically the slash command name, like `/summarize`). It must: - Be 2–64 characters - Use only lowercase letters, digits, hyphens, or underscores - Start and end with a letter or digit Two different users can each own a skill with the same key (for example, both have a `summarize`). A single user cannot have two skills with the same key, and presets share one global keyspace. ## App scoping Every skill carries an `app_ids` array that controls which apps can see it. One skill can serve several apps, or every app on the platform. | `app_ids` value | Who sees the skill | |---|---| | `null` (or omitted) | Every app — the skill is global | | `["vibrantly", "blinklife"]` | Only those apps, plus nothing else | Pass `app_ids` on create or update: ```bash POST https://your-tie-host/skills Authorization: Bearer $TOKEN Content-Type: application/json { "key": "summarize-thread", "name": "Summarize Thread", "prompt": "Summarize the conversation so far in 3 short bullet points.", "app_ids": ["vibrantly", "blinklife"] } ``` Send `"app_ids": null` to make the skill global. An empty list is treated the same as `null`. **Legacy `app_id`.** The earlier scalar `app_id` field still works on create and update. Truememory folds a single `app_id` into a one-element `app_ids` list, so existing single-app callers need no changes. Responses always return `app_ids`. **Reading by app.** The `app_id` query parameter on `GET /skills` names the requesting app, not the skill. It is still a single value: ```bash GET https://your-tie-host/skills?app_id=vibrantly Authorization: Bearer $TOKEN ``` A user-facing read returns skills assigned to that app **plus** global skills (`app_ids` of `null`). Admin reads (`GET /admin/skills?app_id=…`) match the app exactly and do not include global skills. Omit the parameter to read across every app. This `app_ids` array is one of two ways to scope a read. The other is the admin-managed app/category taxonomy, reached with the `X-Use-Taxonomy` header — see [Listing through the app/category taxonomy](#listing-through-the-appcategory-taxonomy). ## List skills Returns presets plus the authenticated user's custom skills, paginated. Results are ordered presets first, then by `sort_order` ascending, then by `name`. ```bash GET https://your-tie-host/skills?category=writing&is_active=true&search=summary&limit=50&offset=0 Authorization: Bearer $TOKEN ``` | Query param | Type | Default | Description | |---|---|---|---| | `app_id` | string | — | Requesting app. Returns skills assigned to it plus global skills. Omit to read across every app. See [App scoping](#app-scoping) | | `category` | string | — | Exact match on the `category` field | | `is_active` | bool | — | Filter by active flag. Inactive skills are still listed when this is omitted | | `search` | string | — | Case-insensitive substring match on `name` or `description` | | `limit` | int | 50 | 1–200 | | `offset` | int | 0 | — | For backend-to-backend integration, use the service token and pass the user subject explicitly: ```bash GET https://your-tie-host/skills?is_active=true Authorization: Bearer $AUTH_SECRET X-On-Behalf-Of: 11111111-2222-3333-4444-555555555555 ``` **Response:** ```json { "skills": [ { "id": "0bc0...", "user_id": null, "app_ids": null, "key": "summarize", "name": "Summarize Thread", "description": "Summarize the current conversation in three bullets.", "prompt": "Summarize the conversation so far in 3 short bullet points.", "category": "productivity", "icon": "sparkles", "argument_hint": null, "allowed_tools": [], "metadata": {}, "is_preset": true, "is_active": true, "sort_order": 0, "forked_from_skill_id": null, "created_at": "2026-01-01T00:00:00Z", "updated_at": "2026-01-01T00:00:00Z" } ], "total": 1 } ``` `total` is the count of all matching rows before `limit`/`offset` — use it to paginate. ### Listing through the app/category taxonomy Alongside the `app_ids` array and the free-text `category` field, Truememory keeps an admin-managed **app/category taxonomy**: apps and categories are first-class records — each app has a unique, URL-safe **slug** — and skills are grouped into categories that are grouped under apps. To list skills through that taxonomy, send the `X-Use-Taxonomy: true` header on the same `GET /skills` endpoint. The header does not add any new endpoint — it only changes how two existing query parameters are interpreted: ```bash GET https://your-tie-host/skills?app_id=vibrantly&category=6f1c8b2a-...&is_active=true Authorization: Bearer $TOKEN X-Use-Taxonomy: true ``` | Parameter | Without the header (default) | With `X-Use-Taxonomy: true` | |---|---|---| | `app_id` | The `app_ids` scope value (see [App scoping](#app-scoping)) | An **app slug**, resolved through the taxonomy | | `category` | Exact match on the free-text `category` field | A **category id** (UUID) from the taxonomy | `is_active`, `search`, `limit`, `offset`, the ordering, and the response shape are all unchanged. Two behaviours to note in taxonomy mode: - **Unknown slug 404s.** If `app_id` is a slug no app owns, the request returns `404 APP_NOT_FOUND` — a typo fails loudly instead of returning an empty list. A valid app that simply has no linked skills returns an empty `skills` array with `total: 0`. - **No global skills.** Unlike the default read, taxonomy mode returns only the skills the taxonomy links to that app; it does **not** add global (`app_ids: null`) skills. ## Get a skill Two ways to read a single skill: by id, or by key. ```bash GET https://your-tie-host/skills/{skill_id} Authorization: Bearer $TOKEN ``` Returns 404 if the skill does not exist or is owned by another user. ```bash GET https://your-tie-host/skills/by-key/{key} Authorization: Bearer $TOKEN ``` When a user owns a skill with the same `key` as a preset, the user's version is returned (override semantics). ## Create a custom skill ```bash POST https://your-tie-host/skills Authorization: Bearer $TOKEN Content-Type: application/json { "key": "summarize-thread", "name": "Summarize Thread", "description": "Summarize the current conversation in three bullets.", "prompt": "Summarize the conversation so far in 3 short bullet points.", "category": "productivity", "app_ids": ["vibrantly"], "icon": "sparkles", "argument_hint": "[topic]", "allowed_tools": ["web_search"], "metadata": {}, "is_active": true, "sort_order": 0, "forked_from_skill_id": null } ``` Returns the created skill with HTTP `201 Created`. Only `key`, `name`, and `prompt` are required. | Field | Type | Notes | |---|---|---| | `key` | string | See [Key naming](#key-naming) | | `app_ids` | string[] \| null | Apps that can see the skill. `null` means every app. See [App scoping](#app-scoping). The legacy scalar `app_id` is also accepted | | `name` | string | 1–255 chars | | `description` | string \| null | Optional, no length cap | | `prompt` | string | The template body, sent to the LLM by the caller | | `category` | string \| null | ≤ 100 chars | | `icon` | string \| null | ≤ 255 chars. Interpreted by the caller — any identifier or URL | | `argument_hint` | string \| null | ≤ 255 chars. Hint shown to the user about expected arguments (e.g. `[topic]`) | | `allowed_tools` | string[] | Caller-defined list; Truememory stores but does not enforce | | `metadata` | object | Free-form JSON owned by the caller | | `is_active` | bool | Default `true`. Inactive skills are still listed | | `sort_order` | int | Display order; lower sorts earlier | | `forked_from_skill_id` | string \| null | Optional — see [Forking a preset](#forking-a-preset) | Omitted optional fields use server defaults: `description`, `category`, `app_ids`, `icon`, `argument_hint`, and `forked_from_skill_id` become `null` (a `null` `app_ids` means every app); `allowed_tools` becomes `[]`; `metadata` becomes `{}`; `is_active` becomes `true`; `sort_order` becomes `0`. **Errors:** - `409 SKILL_KEY_TAKEN` — the user already owns a skill with this key - `400 SKILL_FORK_INVALID` — `forked_from_skill_id` is set but does not point at an existing preset - `422` validation error — the request body does not match the schema, such as an invalid `key`, empty `prompt`, or `null` for a required update field ## Forking a preset Setting `forked_from_skill_id` on create copies the relationship — the new skill is a user-owned snapshot of the preset, with a back-pointer for analytics or UI hints. Only **preset** skills are forkable; passing the id of another user's custom skill, or any non-preset row, is rejected with `400 SKILL_FORK_INVALID`. Fork is metadata, not a live link. Edits to the preset do not propagate to forks. ## Update a custom skill ```bash PUT https://your-tie-host/skills/{skill_id} Authorization: Bearer $TOKEN Content-Type: application/json { "name": "Summarize (concise)", "prompt": "Summarize the conversation in 2 short bullets, max 12 words each." } ``` All fields are optional — send only what you want to change. Returns 404 if the skill does not exist, is not owned by the user, or is a preset. **Nullable vs. NOT NULL:** `description`, `category`, `app_ids`, `icon`, `argument_hint`, and `forked_from_skill_id` accept explicit `null` to clear the value (a `null` `app_ids` makes the skill global). The remaining fields are NOT NULL — sending `null` is rejected; omit the field to leave it unchanged. ## Delete a custom skill ```bash DELETE https://your-tie-host/skills/{skill_id} Authorization: Bearer $TOKEN ``` **Response:** ```json { "status": "deleted", "skill_id": "0bc0..." } ``` Returns 404 if the skill does not exist, is not owned by the user, or is a preset. ## Admin endpoints A second route block under `/admin/skills/*` exposes the same CRUD without ownership or preset scoping. It lets a service create presets, list skills across all users, and mutate or delete any row regardless of `is_preset`. These endpoints are gated by the Truememory service token (`AUTH_SECRET`), not a user Bearer token. The trust model is: the calling app validates that the human acting through it is an admin; Truememory trusts the service token. End-user tokens cannot reach these endpoints. `X-On-Behalf-Of` is not needed on admin calls. Admin routes are authorized by the service token, and they do not use a subject user unless you explicitly pass `user_id` in an admin request body or query string. ```bash POST https://your-tie-host/admin/skills Authorization: Bearer $AUTH_SECRET Content-Type: application/json { "key": "summarize-thread", "name": "Summarize Thread", "prompt": "Summarize the conversation so far in 3 short bullet points.", "category": "productivity", "icon": "sparkles" } ``` Omit `user_id` or set it to `null` to create a preset visible to every authenticated user. Set `user_id` to create a custom row owned by that user. Admin create and update accept `app_ids` exactly like the user endpoints (see [App scoping](#app-scoping)), so an admin can scope a preset to specific apps or share it with all of them. The legacy scalar `app_id` is accepted here too. | Endpoint | Behavior | |---|---| | `GET /admin/skills` | List across all users. Filters: `user_id`, `app_id` (exact match, no global skills), `is_preset`, `category`, `is_active`, `search`, `limit`, `offset` | | `POST /admin/skills` | Create. `user_id` null → preset; `user_id` set → row owned by that user | | `GET /admin/skills/{skill_id}` | Get any skill | | `PUT /admin/skills/{skill_id}` | Update any skill, including presets | | `DELETE /admin/skills/{skill_id}` | Delete any skill, including presets | Fork validation (`SKILL_FORK_INVALID`) still runs on admin paths — it is a data invariant, not a permission check. ## Common pitfalls - **Confusing API skills with LLM instruction files.** The `/skills` endpoints manage rows in the Truememory database for apps to use at runtime. They do not read or write `.agents/skills`, `.claude/skills`, or other repository-local instruction folders. - **Treating `allowed_tools` as enforcement.** Truememory does not block tool calls based on this list. If your app needs to restrict which tools a skill can use, enforce it in the caller before sending the prompt. - **Trying to update or delete a preset via the user endpoints.** Returns 404, not 403 — presets are invisible to the mutation path on purpose. Use the admin endpoints if you genuinely need to change a preset. - **Calling admin endpoints with an end-user token.** Admin routes require the service token. If your product has human admins, enforce that role in your app before forwarding the admin request to Truememory. - **Re-fetching presets per request to render a slash menu.** Skills change rarely; cache the list on the client and invalidate on user-driven create/update/delete. - **Assuming forks track the preset.** Fork is a static back-pointer. If the preset changes, existing forks do not. # Threads Source: https://truememory-docs.mvstg.com/ai-gateway/threads/ · Markdown: https://truememory-docs.mvstg.com/ai-gateway/threads/index.md Threads persist conversation state across requests. You never create one explicitly — pass a `thread_id` on a [chat request](/ai-gateway/chat-completions/#threads) and Truememory creates or resumes it. This page covers managing them afterwards: list, rename, delete, and history. All endpoints require a Bearer token from [Truememory Auth](/authentication/overview/). ## Creating Threads Pass a `thread_id` in your `/v1/chat/completions` request to maintain conversation state. Truememory uses it to persist message history so the LLM has context from previous turns. - If **omitted**, Truememory auto-generates a UUID for the thread. Each request starts a new conversation. - If **provided**, Truememory resumes the conversation from where it left off. Use any string (e.g. a UUID you generate client-side). :::note The `thread_id` is not returned in the response. Track it client-side if you want to continue conversations. ::: ## Retrying a Failed Turn A chat request that fails partway through can still have stored its messages on the thread. Sending the same request again would store them twice. To avoid that, anchor each request on the last message you received. - **`message_id`** — the ID of the last message the turn stored. A non-streaming response returns it as a top-level field. A streaming response adds it as a top-level key on the chunk that carries the `finish_reason` (`stop` or `tool_calls`). It is not sent when `finish_reason` is `error`, and it is only returned for requests with a `thread_id`. - **`parent_message_id`** — send the last `message_id` you received. `messages` holds the messages after it. It may also start with replayed history that ends at the parent; that history is dropped exactly as it is without `parent_message_id`. ```json { "model": "anthropic/claude-sonnet-4-5", "thread_id": "my-thread", "parent_message_id": "", "messages": [{ "role": "user", "content": "What about tomorrow?" }] } ``` If the request fails, send it again unchanged, with the same `parent_message_id` and `messages`. You never need to trim it. Truememory drops the replayed history the same way the first attempt did, compares the rest with what is already stored after the parent, and only appends what is missing. When the whole request is already stored and the turn did not finish, the model runs again on the stored thread. A server tool that already returned a result is not run again. A turn that finished is never run again. That includes a turn that ended in tool calls for your client tools: you already received those calls. You get a `409 THREAD_PARENT_STALE` instead. Read the thread [history](#get-thread-history) and use the last message's `run_id` as the new `parent_message_id`. In a client tool loop, use the `message_id` from the `tool_calls` finish as the parent of the request that sends the tool result. `parent_message_id` is ignored when `thread_id` is not set. Without it, requests behave as before. | Code | Status | When | |------|--------|------| | `THREAD_PARENT_NOT_FOUND` | 409 | `parent_message_id` is not a message on this thread | | `THREAD_PARENT_STALE` | 409 | The turn after the parent already completed, including one that ended in client tool calls (read it from [history](#get-thread-history)), or the thread holds different or later messages after the parent | | `SERVICE_UNAVAILABLE` | 503 | The thread could not be read to check the parent. Send the same request again later | ## Per-App Thread Isolation Threads can be scoped to a specific application using the `X-App-Id` header. This allows multiple apps to share the same Truememory backend while keeping their conversation threads separate. - **`X-App-Id` provided** — the thread is scoped to that app. Only requests with the same `X-App-Id` can see, rename, or delete it. - **`X-App-Id` omitted** — the thread is unscoped (shared). It is visible when listing threads without `X-App-Id`, but hidden from app-scoped lists. ```bash # Create a thread scoped to "vibrantly" curl -X POST https://your-tie-host/v1/chat/completions \ -H "Authorization: Bearer $TOKEN" \ -H "X-App-Id: vibrantly" \ -H "X-Agent-Id: chatbot" \ -d '{"model": "anthropic/claude-sonnet-4-5", "messages": [{"role": "user", "content": "Hello"}], "thread_id": "my-thread"}' ``` :::caution To resume a thread created with `X-App-Id`, you must send the same `X-App-Id` header. Omitting it will create a new, separate conversation. ::: :::note Memory (facts, entities, relationships) is **not** scoped by app. A user's memories are shared across all apps — only conversation threads are isolated. ::: ## Thread Metadata Each thread can carry an arbitrary JSON metadata object. Use it to store application-specific context that doesn't belong in the conversation itself — for example, where the thread originated, which page it's associated with, or whether it's pinned in a UI. ```json { "source": "whatsapp", "pageType": "whatsapp-dm", "pageId": "550e8400-e29b-41d4-a716-446655440000", "isPinned": false } ``` Metadata is set and updated via the [Update Thread](#update-thread) endpoint. It is returned in every thread summary (list and update responses). Truememory stores metadata as-is and does not interpret it — your application defines the schema. :::note Metadata is `null` by default. Setting it to `null` or `{}` clears any previously stored metadata. ::: ## List Threads Returns threads for the authenticated user. Supports cursor-based pagination, sorting, and date filtering. ```bash GET https://your-tie-host/threads Authorization: Bearer $TOKEN ``` **Headers:** | Header | Type | Default | Description | |--------|------|---------|-------------| | `X-App-Id` | string | null | Filter threads by app. When set, only threads matching that exact app ID are returned (unscoped threads are excluded). When omitted, all threads are returned (both scoped and unscoped). | **Query parameters:** | Parameter | Type | Default | Description | |-----------|------|---------|-------------| | `agent_id` | string | null | Filter threads by agent | | `cursor` | string | null | Pagination token from a previous response (treat as opaque; do not parse or construct it) | | `limit` | integer | 20 | Results per page (1-100) | | `order_by` | string | `created_at` | Sort field: `created_at` or `updated_at` | | `asc` | boolean | false | Sort direction: `true` for ascending (oldest first), `false` for descending (newest first) | | `created_after` | ISO 8601 datetime | null | Only return threads created on or after this timestamp | | `created_before` | ISO 8601 datetime | null | Only return threads created on or before this timestamp | **Response:** ```json { "threads": [ { "thread_id": "847c6285-8fc9-4560-a83f-4e6285809254", "agent_id": "chatbot", "app_id": "vibrantly", "title": "What is the weather in Tokyo?", "metadata": { "source": "whatsapp", "isPinned": false }, "created_at": "2026-03-25T10:00:00+00:00", "updated_at": "2026-03-25T10:05:00+00:00" } ], "next_cursor": "MjAyNi0wMy0yNVQxMDowMDowMCswMDowMHw4NDdjNjI4NS04ZmM5LTQ1NjAtYTgzZi00ZTYyODU4MDkyNTQ=" } ``` Pass `next_cursor` as the `cursor` query parameter to fetch the next page. When `next_cursor` is `null`, there are no more results. The `app_id` field is `null` for unscoped threads. The `metadata` field is `null` when no metadata has been set. ## Get Thread History Retrieve the full message history for a thread: ```bash POST https://your-tie-host/history Authorization: Bearer $TOKEN Content-Type: application/json { "thread_id": "" } ``` :::note History requires ownership: the thread must belong to the authenticated user, otherwise the endpoint returns `404 THREAD_NOT_FOUND` (a 404 rather than 403, so thread IDs cannot be probed). Note the lookup is not scoped by `X-App-Id` — pass the right `X-App-Id` for the thread's app. ::: **Response:** ```json { "messages": [ { "type": "human", "content": "What is the weather in Tokyo?", "metadata": { "session_id": "8b94776f-31ae-49ac-9f06-3be137d69186", "platform": "ios", "app_version": "2.4.1" } }, { "type": "ai", "content": "The current weather in Tokyo is...", "metadata": null } ] } ``` The `metadata` field on user messages contains the key-value pairs sent with the original request. AI messages have `metadata: null` since metadata is attached at request time, not per-response. ## Update Thread Update a thread's title and/or metadata. The title is automatically set from the first user message, but you can override it. Metadata can be set at any time. At least one of `title` or `metadata` must be provided in the request body. ```bash PATCH https://your-tie-host/threads/ Authorization: Bearer $TOKEN Content-Type: application/json # Required only if the thread was created with X-App-Id X-App-Id: vibrantly { "title": "My weather conversation", "metadata": { "isPinned": true } } ``` **Request body:** | Field | Type | Required | Description | |-------|------|----------|-------------| | `title` | string | No | New title for the thread (1-500 characters, cannot be blank) | | `metadata` | object \| null | No | Arbitrary metadata dict. Replaces existing metadata entirely. Pass `null` to clear. | You can send either field alone or both together: ```bash # Rename only { "title": "New title" } # Set metadata only { "metadata": { "source": "telegram", "isPinned": false } } # Clear metadata { "metadata": null } ``` **Response:** ```json { "thread_id": "847c6285-8fc9-4560-a83f-4e6285809254", "agent_id": "chatbot", "app_id": "vibrantly", "title": "My weather conversation", "metadata": { "isPinned": true }, "created_at": "2026-03-25T10:00:00+00:00", "updated_at": "2026-03-25T10:10:00+00:00" } ``` :::caution Metadata uses **replace** semantics — the entire metadata object is overwritten, not merged. To update a single key, read the current metadata first, modify it, and send the full object back. ::: :::note If the thread was created with `X-App-Id`, you must send the same header to update it. Mismatched or missing `X-App-Id` will return 404. ::: ## Delete Thread Permanently delete a thread and all its data (title, metadata, timestamps, agent/app association): ```bash DELETE https://your-tie-host/threads/ Authorization: Bearer $TOKEN # Required only if the thread was created with X-App-Id X-App-Id: vibrantly ``` **Response:** ```json { "status": "deleted", "thread_id": "847c6285-8fc9-4560-a83f-4e6285809254" } ``` :::caution This action is irreversible. The thread and all its data are permanently removed and the thread will no longer appear in list results. ::: :::note If the thread was created with `X-App-Id`, you must send the same header to delete it. Mismatched or missing `X-App-Id` will return 404. ::: ## Error Codes | Code | Status | When | |------|--------|------| | `AUTH_TOKEN_MISSING` | 401 | No Bearer token provided | | `THREAD_NOT_FOUND` | 404 | Thread does not exist or is not owned by the user | | `VALIDATION_ERROR` | 422 | Invalid cursor, empty title, title exceeds 500 characters, or empty update body | | `THREADS_FETCH_FAILED` | 500 | Unexpected server error while listing threads | | `THREAD_UPDATE_FAILED` | 500 | Unexpected server error while updating a thread | | `THREAD_DELETE_FAILED` | 500 | Unexpected server error while deleting a thread | | `SERVICE_UNAVAILABLE` | 501 | Thread management requires the PostgreSQL memory backend | # Real-Time Voice (Gemini Live) Source: https://truememory-docs.mvstg.com/ai-gateway/voice/ · Markdown: https://truememory-docs.mvstg.com/ai-gateway/voice/index.md Truememory provides real-time voice-to-voice conversation powered by Gemini Live. The client opens a WebSocket to Truememory, sends raw PCM audio, and receives audio back — Truememory handles auth, persona injection, memory context, tool routing, and identity guardrails transparently. Per-persona voice and language preferences are stored server-side and applied automatically to each session — each persona a user owns can have its own voice. Stateless Gemini text-to-speech is exposed via the unified [`/v1/audio/speech`](/ai-gateway/audio/#gemini-tts-same-endpoint-different-model) endpoint (same as OpenAI TTS, just with a Gemini `model`). --- ## Authentication All HTTP voice endpoints (`/v1/voice/preferences/{persona_id}`) accept the same tri-mode auth as the rest of Truememory. See [Ways to authenticate](/authentication/overview/) for details. (Standalone Gemini TTS is on the unified [`/v1/audio/speech`](/ai-gateway/audio/#gemini-tts-same-endpoint-different-model) endpoint with the same auth rules.) | Caller | Method | Details | |--------|--------|---------| | Browser / mobile app | [User tokens](/authentication/email-password/) | Sign in via Truememory Auth, pass the JWT as `Authorization: Bearer ` | | Backend service | [Service accounts](/authentication/service-accounts/) | `Authorization: Bearer $AUTH_SECRET` + `X-On-Behalf-Of: ` | These endpoints are user-scoped (preferences are per-user-per-persona, TTS billing is per-user), so the stateless service-only mode is rejected — service-key callers must include `X-On-Behalf-Of`. WebSocket sessions (`/v1/voice/agent`, `/v1/voice/stt`) authenticate via the `tie.bearer` subprotocol — see each section below. Both forms work: | Caller | Subprotocol value | Header | |--------|-------------------|--------| | Browser / mobile (direct) | `["tie.bearer", ""]` | — | | Backend proxy (BlinkLife/Vibrantly) | `["tie.bearer", "$AUTH_SECRET"]` | `X-On-Behalf-Of: ` | The proxy form requires `X-On-Behalf-Of` — without it, the connection is rejected, since we never collapse multi-tenant traffic onto a shared identity. --- ## WebSocket Voice Session Open a persistent bidirectional audio stream between the client and Gemini Live. ### Endpoint ``` WebSocket /v1/voice/agent ``` Auth via the WebSocket subprotocol header (browsers can't send custom headers on the handshake, so the bearer token is smuggled through `Sec-WebSocket-Protocol`): ```js new WebSocket("wss://your-tie-host/v1/voice/agent", ["tie.bearer", ""]) ``` The server reads the second subprotocol value as the token and accepts `tie.bearer` to complete the handshake. This keeps the token out of URL paths and proxy access logs. ### Session Lifecycle ```mermaid sequenceDiagram participant Client participant Truememory participant Gemini Client->>Truememory: WebSocket connect (Sec-WebSocket-Protocol: tie.bearer, token) Client->>Truememory: JSON session config (first message) Truememory->>Truememory: Load persona + memory + apply guardrails Truememory->>Gemini: Open Live session Truememory-->>Client: {"type": "session_ready", "session_id": "...", "thread_id": "..."} loop Bidirectional relay Client->>Truememory: Binary frame (PCM audio chunk) Truememory->>Gemini: Audio Gemini-->>Truememory: Audio + transcripts + tool calls Truememory-->>Client: Binary frame (audio) + JSON events end Client->>Truememory: {"type": "control", "action": "disconnect"} Truememory->>Truememory: Flush transcripts → thread + memory pipeline ``` ### Session Config Send this as the **first JSON message** after connecting: ```json { "voice": null, "language": null, "agent_id": "my-app", "app_id": "vibrantly", "persona_id": "your-persona-id-or-null", "thread_id": "existing-thread-id-or-null", "skill_instructions": "You are currently in the MyApp experience.\nCurrent time: Monday, April 19, 2026 at 11:30 PM\nUser timezone: Asia/Kuala_Lumpur", "tools": [], "proactive_audio": false, "enable_affective_dialog": false, "resumption_handle": null } ``` | Field | Type | Default | Description | |-------|------|---------|-------------| | `voice` | string \| null | null | Voice name override (see [Voices](#voices)). When null, Truememory uses the stored preference for the session's persona (or falls back to the user's active persona) | | `language` | string \| null | null | Language code override. When null, Truememory uses the stored preference for the session's persona (or falls back to the user's active persona) | | `agent_id` | string | `chatbot` | A **scoping key** for memory and thread history — unlike the chat API's `X-Agent-Id`, it does not select an agent from the [registry](/ai-gateway/agents/) and any stable string is valid. Use one value per app (e.g. `"blinklife"`) so voice memory stays consistent | | `app_id` | string \| null | null | Application ID for thread scoping — the WS-equivalent of the `X-App-Id` header on `/v1/chat/completions`. Without it, voice transcripts persist with `app_id=NULL` and are excluded from app-scoped `GET /threads` queries. Browsers can't send custom headers on WS upgrade, so the value travels in this field instead. | | `persona_id` | string \| null | null | Truememory persona to load. Persona sets the AI's identity and user-level preferences | | `thread_id` | string \| null | null | Existing thread for context continuity. Omit to create a new session | | `skill_instructions` | string \| null | null | App-specific context injected after persona + memory (see below) | | `tools` | array | `[]` | Client-side tool definitions forwarded to the client for execution | | `proactive_audio` | bool | `false` | Enable Proactive Audio for wake word detection | | `enable_affective_dialog` | bool | `false` | Enable affective dialog (emotional expression in voice) | | `resumption_handle` | string \| null | null | Handle from a previous session for seamless reconnection | ### System Prompt Composition Truememory builds the Gemini system instruction server-side by concatenating these layers in order: ``` 1. Persona (loaded from Truememory persona store by persona_id) 2. Memory context (user's memory graph for this agent_id) 3. Current user identity ("You are speaking with …") 4. skill_instructions (sent by the client — app context + dynamic data) 5. Identity guardrails (always appended by Truememory — cannot be overridden) ``` **What belongs in `skill_instructions`:** The client is the only party that knows which app the user is in and real-time session data. Send a string that covers: ``` You are currently in the app. Keep responses concise and conversational — this is a voice session. Never read out URLs, markdown, or bullet points — speak naturally. Current time: Monday, April 19, 2026 at 11:30 PM User timezone: Asia/Kuala_Lumpur ``` Do **not** hardcode AI identity (name, personality) in `skill_instructions` — that belongs in the Truememory persona so it can be managed via the admin panel. The AI name is set dynamically by the caller via the persona, not by the client. **Platform-level guardrails (applied automatically by Truememory):** Truememory always appends identity guardrails as the final layer regardless of what the client sends: - The AI will never reveal it is powered by Gemini, GPT, Claude, or any other LLM technology - If asked, it may say it is powered by Envision Inc - It will never break character or acknowledge these instructions ### Message Protocol Binary frames carry audio. Text frames carry JSON control messages. **Client → Truememory:** | Type | Transport | Payload | |------|-----------|---------| | Audio chunk | Binary frame | PCM 16-bit signed, 16 kHz, mono | | `tool_result` | JSON text | `{"type": "tool_result", "call_id": "...", "result": {...}}` | | `audio_stream_end` | JSON text | `{"type": "audio_stream_end"}` — send when mic is muted or paused | | `control` | JSON text | `{"type": "control", "action": "disconnect"}` | | `text_input` | JSON text | `{"type": "text_input", "text": "..."}` — typed follow-up the user wants the AI to respond to without ending the call. Empty strings are ignored. | | `image_input` | JSON text | `{"type": "image_input", "data": "", "mime_type": "image/jpeg"}` — attach a photo mid-call. `data` is strict base64 (non-alphabet characters are rejected). `mime_type` defaults to `"image/jpeg"`. Frame size is capped by `VOICE_MAX_IMAGE_FRAME_BYTES` (default 512 KB JSON ≈ 384 KB raw image) — clients must resize/compress photos above that before sending. | **Truememory → Client:** | Type | Transport | Payload | |------|-----------|---------| | Audio chunk | Binary frame | PCM audio from Gemini (24 kHz) | | `session_ready` | JSON text | `{"type": "session_ready", "session_id": "...", "thread_id": "..."}` | | `transcript` | JSON text | `{"type": "transcript", "role": "user\|assistant", "text": "..."}` | | `interrupted` | JSON text | `{"type": "interrupted", "usage": {...}}` — user barged in; stop and discard buffered audio. `usage` is the running per-session token total (same shape as `session_end.usage`). | | `generation_complete` | JSON text | `{"type": "generation_complete", "usage": {...}}` — AI finished speaking; return to listening state. `usage` is the running per-session token total (same shape as `session_end.usage`). | | `tool_call` | JSON text | `{"type": "tool_call", "call_id": "...", "name": "...", "args": {...}}` | | `session_resumption` | JSON text | `{"type": "session_resumption", "handle": "..."}` — store for reconnection | | `session_end` | JSON text | `{"type": "session_end", "duration_seconds": float, "model": "...", "usage": {...}}` — sent right before the WebSocket closes; record for usage tracking | | `go_away` | JSON text | `{"type": "go_away", "time_left": "..."}` — Gemini signaling imminent disconnect | | `error` | JSON text | `{"type": "error", "code": "...", "message": "...", "details": {...}}` | The `error.details` object is best-effort diagnostic context — present when Truememory can attribute the failure to an upstream cause: | Field | Type | When present | |--|--|--| | `model` | string | Always on upstream errors | | `location` | string | Always on upstream errors — the Vertex AI region Truememory called | | `upstream_code` | int | Set when the failure originated from Gemini (HTTP status) | | `upstream_status` | string | Set when the failure originated from Gemini (e.g. `PERMISSION_DENIED`, `RESOURCE_EXHAUSTED`) | The `code` field maps to: | `code` | Meaning | |--|--| | `VOICE_AUTH_FAILED` | Client token rejected, or upstream 401 from Vertex AI | | `VOICE_MODEL_UNAVAILABLE` | Voice model not accessible at the configured project/region (upstream 403/404) | | `VOICE_RATE_LIMIT_EXCEEDED` | Upstream quota exhausted (HTTP 429) — retry with backoff | | `VOICE_SESSION_LIMIT_EXCEEDED` | Per-user concurrent-session cap hit | | `VOICE_SESSION_EXPIRED` | Session ran past `VOICE_SESSION_TIMEOUT` | | `VOICE_SESSION_FAILED` | Generic — upstream 4xx/5xx with no specific mapping, or non-upstream transport error | The same `usage` object is also attached to `interrupted` and `generation_complete` frames so callers can record token totals even when the WebSocket disconnects before `session_end` arrives (e.g. client-initiated end-of-call). Each frame carries the latest accumulator snapshot — callers should keep the most recent value and treat it as the canonical session total. The `usage` object on `session_end` carries the per-turn-summed token counts from Gemini Live's `usage_metadata`: ```json { "prompt_tokens": 1240, "completion_tokens": 380, "total_tokens": 1620, "cached_content_tokens": 0, "thoughts_tokens": 0 } ``` Per-turn `prompt_tokens` includes the session memory replayed as input on each turn — that's how Vertex AI bills it, so summing matches your invoice rather than over-counting. ### Audio Format | Direction | Format | |-----------|--------| | Client → Truememory | PCM 16-bit signed, 16 kHz, mono | | Truememory → Client | PCM 16-bit signed, 24 kHz, mono | Use an [AudioWorklet](https://developer.mozilla.org/en-US/docs/Web/API/AudioWorklet) to convert browser mic float32 samples to int16 PCM before sending. > **Playback:** Create your `AudioBuffer` at **24 kHz** — not at the `AudioContext`'s native sample rate. Using the wrong rate causes chipmunk-pitched or slow playback. > **Interrupt handling:** When you receive `interrupted`, stop all scheduled `AudioBufferSourceNode`s by calling `.stop()` on each tracked node and reset your playback clock. Do **not** close and recreate the `AudioContext` — creating a new context outside a user gesture may leave it in a suspended state, silently breaking all subsequent audio playback. ### Tool Call Routing Truememory routes tool calls into two categories: **Truememory-native tools** — executed server-side, invisible to the client: - `memory_search` — searches the user's memory graph - `memory_write` — stores a new memory **Client-provided tools** — defined in `tools` at session start, forwarded to the client for execution. When Gemini calls one, Truememory sends a `tool_call` event. The client must reply within 30 seconds with a `tool_result` message or Truememory returns a timeout error to Gemini. ```json // Client receives: {"type": "tool_call", "call_id": "abc123", "name": "get_calendar", "args": {"date": "today"}} // Client replies: {"type": "tool_result", "call_id": "abc123", "result": {"events": [...]}} ``` ### Session Limits | Limit | Value | |-------|-------| | Max session duration | `VOICE_SESSION_TIMEOUT` (default 900s) | | Gemini native limit | 15 min (Truememory enables context window compression to extend) | | Tool call timeout | 30s per call | | Audio send timeout | 5s (slow clients are disconnected) | ### Session Resumption Gemini periodically resets WebSocket connections. Truememory enables seamless reconnection: 1. During a session, Truememory forwards `session_resumption` events with a `handle` 2. Store the latest handle in memory or `sessionStorage` 3. On reconnect, pass `resumption_handle` in the session config 4. Gemini restores conversation context from the handle ### Transcript Sync At session end, Truememory writes buffered transcripts to the thread and runs the memory pipeline. Pass the `thread_id` from `session_ready` to subsequent chat requests to continue in the same context. --- ## Frontend Integration ### React Hook (`useTieVoiceSession`) A reference React hook that handles the full WebSocket lifecycle, audio pipeline, session resumption, and tool call dispatch. It is not published as a package — the import below shows how an app that has copied the hook into its codebase uses it; treat the sections above as the wire contract to implement. ```ts import { useTieVoiceSession } from '@/hooks/use-tie-voice-session'; const { voiceMode, // 'idle' | 'listening' | 'processing' | 'speaking' interimTranscript, error, startVoice, stopVoice, interrupt, } = useTieVoiceSession({ agentId: 'my-app', personaId: activePersona?.id, // from GET /personas/active — Truememory is source of truth for the agent voiceId: companion?.voiceId, // Gemini voice — overrides stored preference if set skillInstructions: buildSkillInstructions(companion?.name, user?.displayName), getAccessToken, onToolCall: async (name, args) => { // Handle client-side tool calls from Gemini if (name === 'get_calendar') return { events: await fetchCalendar(args.date) }; return { error: 'Unknown tool' }; }, }); ``` **Building `skillInstructions`:** ```ts function buildSkillInstructions(companionName?: string, userName?: string): string { const tz = Intl.DateTimeFormat().resolvedOptions().timeZone; const now = new Date().toLocaleString('en-US', { timeZone: tz, dateStyle: 'full', timeStyle: 'short', }); return [ // App context — replace with your app name 'You are currently in the MyApp experience.\nKeep responses concise and conversational — this is a voice session.\nNever read out URLs, markdown, or bullet points — speak naturally.', // Dynamic companion/user context companionName ? `Your name is ${companionName}.` : '', userName ? `You are speaking with ${userName}. Address them by name when natural.` : '', // Always include timezone so the AI gives correct times `Current time: ${now}\nUser timezone: ${tz}`, ].filter(Boolean).join('\n\n'); } ``` **Voice mode state machine:** ``` idle ──startVoice()──▶ listening │ user speaks │ ▼ processing (transcript received, waiting for AI) │ AI responds │ ▼ speaking (binary audio frames arriving) │ generation_complete│ or interrupted ▼ listening │ stopVoice() │ ▼ idle ``` **UI state indicator:** ```tsx function VoiceStateIndicator({ voiceMode }: { voiceMode: string }) { if (voiceMode === 'idle') return null; return (
{voiceMode === 'listening' && 'Listening…'} {voiceMode === 'processing' && 'Thinking…'} {voiceMode === 'speaking' && 'Speaking…'}
); } ``` ### Raw WebSocket (no hook) For apps not using the hook, here is the minimal connection pattern: ```ts async function startVoiceSession(token: string, companionName: string) { const tz = Intl.DateTimeFormat().resolvedOptions().timeZone; const now = new Date().toLocaleString('en-US', { timeZone: tz, dateStyle: 'full', timeStyle: 'short' }); // 1. Set up capture at 16 kHz const captureCtx = new AudioContext({ sampleRate: 16000 }); const stream = await navigator.mediaDevices.getUserMedia({ audio: { channelCount: 1, echoCancellation: true, noiseSuppression: true }, }); const micSource = captureCtx.createMediaStreamSource(stream); await captureCtx.audioWorklet.addModule('/worklets/pcm-processor.js'); const workletNode = new AudioWorkletNode(captureCtx, 'pcm-processor'); micSource.connect(workletNode); // 2. Set up playback const playbackCtx = new AudioContext(); let nextPlayTime = 0; const activeSources: AudioBufferSourceNode[] = []; function playPcm(data: ArrayBuffer) { const int16 = new Int16Array(data); const float32 = new Float32Array(int16.length); for (let i = 0; i < int16.length; i++) float32[i] = int16[i] / 32768; const buffer = playbackCtx.createBuffer(1, float32.length, 24000); buffer.copyToChannel(float32, 0); const source = playbackCtx.createBufferSource(); source.buffer = buffer; source.connect(playbackCtx.destination); const startAt = Math.max(nextPlayTime, playbackCtx.currentTime); source.start(startAt); nextPlayTime = startAt + buffer.duration; activeSources.push(source); source.onended = () => activeSources.splice(activeSources.indexOf(source), 1); } function flushAudio() { activeSources.forEach(s => { try { s.stop(); } catch {} }); activeSources.length = 0; nextPlayTime = 0; } // 3. Open WebSocket const ws = new WebSocket('wss://your-tie-host/v1/voice/agent', ['tie.bearer', token]); ws.binaryType = 'arraybuffer'; ws.onopen = () => { ws.send(JSON.stringify({ agent_id: 'my-app', // voice and language omitted — Truememory uses the user's stored preference. // Pass voiceId explicitly to override: voice: 'Aoede' skill_instructions: `You are currently in the MyApp experience.\nKeep responses concise and conversational.\nCurrent time: ${now}\nUser timezone: ${tz}`, })); }; ws.onmessage = (event) => { if (event.data instanceof ArrayBuffer) { playPcm(event.data); return; } const msg = JSON.parse(event.data); if (msg.type === 'session_ready') { // Start streaming mic audio workletNode.port.onmessage = (e: MessageEvent) => { if (ws.readyState === WebSocket.OPEN) ws.send(e.data); }; } if (msg.type === 'interrupted') flushAudio(); if (msg.type === 'session_resumption') localStorage.setItem('tie-voice-handle', msg.handle); if (msg.type === 'tool_call') { // Execute and reply executeClientTool(msg.name, msg.args).then(result => { ws.send(JSON.stringify({ type: 'tool_result', call_id: msg.call_id, result })); }); } if (msg.type === 'error') console.error('Truememory error:', msg.code, msg.message); }; // 4. Mute: disable track + signal end of audio stream function mute() { stream.getAudioTracks()[0].enabled = false; ws.send(JSON.stringify({ type: 'audio_stream_end' })); } // 5. Disconnect cleanly function disconnect() { ws.send(JSON.stringify({ type: 'control', action: 'disconnect' })); ws.close(); stream.getTracks().forEach(t => t.stop()); captureCtx.close(); playbackCtx.close(); } return { mute, disconnect }; } ``` ### AudioWorklet (pcm-processor.js) Place this file at `/public/worklets/pcm-processor.js`: ```js class PcmProcessor extends AudioWorkletProcessor { process(inputs) { const input = inputs[0]?.[0]; if (!input?.length) return true; const int16 = new Int16Array(input.length); for (let i = 0; i < input.length; i++) { int16[i] = Math.max(-32768, Math.min(32767, Math.round(input[i] * 32767))); } this.port.postMessage(int16.buffer, [int16.buffer]); return true; } } registerProcessor('pcm-processor', PcmProcessor); ``` --- ## Streaming STT (Speech-to-Text only) Stream raw PCM audio and receive real-time transcript events — no LLM, no TTS. Use this when you need live transcription without a full voice conversation: push-to-talk input, ambient listeners, onboarding interviews. ### Endpoint ``` WebSocket /v1/voice/stt ``` Same auth convention as the voice agent — bearer token in the subprotocol: ```js new WebSocket("wss://your-tie-host/v1/voice/stt", ["tie.bearer", ""]) ``` ### Session Lifecycle ```mermaid sequenceDiagram participant Client participant Truememory participant Gemini Client->>Truememory: WebSocket connect (Sec-WebSocket-Protocol: tie.bearer, token) Truememory-->>Client: {"type": "ready"} loop While mic is open Client->>Truememory: Binary frame (PCM 16kHz audio chunk) Truememory->>Gemini: Audio relay Gemini-->>Truememory: inputTranscription event Truememory-->>Client: {"type": "transcript", "text": "...", "isFinal": false} end Truememory-->>Client: {"type": "speech-ended"} (VAD detected end of utterance) Truememory-->>Client: {"type": "session_end", "duration_seconds": 42.5, "model": "..."} ``` ### Audio Format Send raw PCM audio: **16-bit signed, 16 kHz, mono**. Use the same `pcm-processor.js` AudioWorklet as the voice agent. ### Server → Client Events | Event | Payload | When | |-------|---------|------| | `ready` | `{"type": "ready"}` | Gemini session open, audio accepted | | `transcript` | `{"type": "transcript", "text": "...", "isFinal": bool}` | Transcription chunk received | | `speech-ended` | `{"type": "speech-ended", "usage": {...}}` | VAD detected end of utterance. `usage` is the running per-session token total (same shape as `session_end.usage`). | | `session_end` | `{"type": "session_end", "duration_seconds": float, "model": "...", "usage": {...}}` | Sent right before the WebSocket closes — use this to record session usage | | `go_away` | `{"type": "go_away", "time_left": "..."}` | Gemini signaling imminent disconnect | | `error` | `{"type": "error", "code": "...", "message": "..."}` | Auth failure, session limit, or upstream error | The `usage` object on `session_end` carries per-turn-summed token counts (`prompt_tokens`, `completion_tokens`, `total_tokens`, `cached_content_tokens`, `thoughts_tokens`) from Gemini Live's `usage_metadata`. `duration_seconds` is included as a secondary proxy. The frame is best-effort — if the client has already disconnected, it is dropped silently. The same `usage` object is also attached to every `speech-ended` frame so callers can record token totals even when the WebSocket disconnects before `session_end` arrives (e.g. client-initiated stop). Each frame carries the latest accumulator snapshot — callers should keep the most recent value and treat it as the canonical session total. `isFinal` is `true` when Gemini marks the transcription as finished for that chunk. The `speech-ended` event marks the utterance boundary — use it to trigger submission in push-to-talk flows. **VAD tuning:** STT is configured for **dictation**, not conversational turn-taking. The `speech-ended` event fires after **1000 ms of silence** (with low end-of-speech sensitivity and 200 ms prefix padding) so users can pause to think mid-sentence without their utterance being chopped up. The full voice agent (`/v1/voice/agent`) uses tighter VAD (500 ms silence, high sensitivity) for fast back-and-forth. ### Example (push-to-talk) ```ts const ws = new WebSocket('wss://your-tie-host/v1/voice/stt', ['tie.bearer', token]); ws.binaryType = 'arraybuffer'; let transcript = ''; ws.onmessage = (event) => { const msg = JSON.parse(event.data as string); if (msg.type === 'ready') { // Start sending audio chunks from AudioWorklet workletNode.port.onmessage = (e: MessageEvent) => { if (ws.readyState === WebSocket.OPEN) ws.send(e.data); }; } if (msg.type === 'transcript') { transcript = msg.text; // update live preview } if (msg.type === 'speech-ended') { submitTranscript(transcript); // user stopped speaking — submit transcript = ''; } }; ``` ### Session Limits | Limit | Value | |-------|-------| | Max session duration | `VOICE_SESSION_TIMEOUT` (default 900s) | | Max concurrent sessions per user | `VOICE_MAX_CONCURRENT_SESSIONS_PER_USER` (default 3, shared with voice agent) | | Audio send timeout | 5s (slow clients are disconnected) | --- ## Voice Preferences Store a per-persona preferred voice and language server-side so every session picks them up automatically without the client having to pass them each time. Each persona a user owns can have its own voice. ### Get preferences ``` GET /v1/voice/preferences/{persona_id} Authorization: Bearer ``` ```json { "provider": "gemini", "voice": "Kore", "language": "en" } ``` Returns stored preferences for that persona, or defaults (`provider: "gemini"`, `voice: "Kore"`, `language: "en"`) if none have been saved yet. The persona must be a preset or owned by the caller — otherwise returns `404 PERSONA_NOT_FOUND`. ### Update preferences ``` PUT /v1/voice/preferences/{persona_id} Authorization: Bearer Content-Type: application/json ``` ```json { "provider": "gemini", "voice": "Aoede", "language": "en" } ``` | Field | Type | Description | |-------|------|-------------| | `provider` | string | TTS provider. Currently only `"gemini"` is supported | | `voice` | string | Voice name (see [Voices](#voices)) | | `language` | string | BCP-47 language code (e.g. `"en"`, `"es"`, `"ms"`) | Responds with the saved preferences. Preferences apply to WebSocket sessions when `voice` and `language` are omitted (or null) in the session config. Truememory looks up the row keyed by the session's `persona_id` (or the user's active persona if `persona_id` is omitted). Pass `voice` / `language` explicitly in the session config to override for a single session. --- ## Voices For the full list of available Gemini voices and their character descriptions, see [Audio — Gemini Voices](/ai-gateway/audio/#gemini-voices). # AI Gateway API > Chat completions, memory, personas, skills and the rest of the AI gateway. For sign-in, tokens and accounts, see the [Auth API](/auth/). Source: https://truememory-docs.mvstg.com/api/ · Markdown: https://truememory-docs.mvstg.com/api/index.md Chat completions, memory, personas, skills and the rest of the AI gateway. For sign-in, tokens and accounts, see the [Auth API](/auth/). Version: 1.0.0 ## Servers - `https://truememory-api.mvstg.com` ## Sections - [Chat](/api/tags/chat/) - [History & Feedback](/api/tags/history-feedback/) - [Memory](/api/tags/memory/) - [Personas](/api/tags/personas/) - [Skills](/api/tags/skills/) - [Knowledge Packs](/api/tags/knowledge-packs/) - [Brains](/api/tags/brains/) - [System](/api/tags/system/) - [Voice](/api/tags/voice/) - [Minds](/api/tags/minds/) - [Notifications](/api/tags/notifications/) - [Users](/api/tags/users/) # Generate speech from text > Generate audio from text. Drop-in replacement for OpenAI /v1/audio/speech. Routes to Gemini when the model is a Gemini TTS variant (e.g. ``google/gemini-2.5-flash-tts``); any other model goes through the OpenAI proxy. Accepts tri-mode auth (user token / service+on-behalf-of / service-only) — the service-key path is required for backend callers like BlinkLife that proxy TTS previews to TIE on a user's behalf. Source: https://truememory-docs.mvstg.com/api/audio/speech/ · Markdown: https://truememory-docs.mvstg.com/api/audio/speech/index.md Path: AI Gateway API › Chat `POST /v1/audio/speech` Generate audio from text. Drop-in replacement for OpenAI /v1/audio/speech. Routes to Gemini when the model is a Gemini TTS variant (e.g. ``google/gemini-2.5-flash-tts``); any other model goes through the OpenAI proxy. Accepts tri-mode auth (user token / service+on-behalf-of / service-only) — the service-key path is required for backend callers like BlinkLife that proxy TTS previews to TIE on a user's behalf. ## Authentication - `HTTPBearer`, http, header `Authorization` ## Header parameters - `X-On-Behalf-Of` (`create_speech.header.X-On-Behalf-Of`; any of, optional) - any of: `string`, `null` - `X-External-User-Id` (`create_speech.header.X-External-User-Id`; any of, optional) - any of: `string`, `null` - `X-App-Id` (`create_speech.header.X-App-Id`; any of, optional) - any of: `string`, `null` ## Request body _Required._ - `input` (`create_speech.input`; string, required) — Text to convert to speech - minLength 1; maxLength 4000 - `model` (`create_speech.model`; string, optional) — TTS model to use - minLength 1; maxLength 100; default `"gpt-4o-mini-tts"` - `voice` (`create_speech.voice`; string, optional) — Voice to use - minLength 1; maxLength 100; default `"alloy"` - `response_format` (`create_speech.response_format`; string, optional) - one of `"mp3"`, `"opus"`, `"aac"`, `"flac"`, `"wav"`, `"pcm"`; default `"mp3"` - `speed` (`create_speech.speed`; number, optional) - min 0.25; max 4; default `1` - `language` (`create_speech.language`; any of, optional) — Language code (Gemini only, e.g. 'en', 'ms', 'en-US') - any of: `string`, `null` ## Example request ```json { "model": "gpt-4o-mini-tts", "input": "string", "voice": "alloy", "response_format": "mp3", "speed": 1, "language": "string" } ``` ## Code samples ### cURL ```bash curl --request POST \ --url https://truememory-api.mvstg.com/v1/audio/speech \ --header 'Authorization: Bearer ' \ --header 'Content-Type: application/json' \ --data ' { "model": "gpt-4o-mini-tts", "input": "string", "voice": "alloy", "response_format": "mp3", "speed": 1, "language": "string" } ' ``` ### TypeScript ```typescript const url = 'https://truememory-api.mvstg.com/v1/audio/speech'; const options = { method: 'POST', headers: {'Content-Type': 'application/json', Authorization: 'Bearer '}, body: JSON.stringify({ model: 'gpt-4o-mini-tts', input: 'string', voice: 'alloy', response_format: 'mp3', speed: 1, language: 'string' }) }; fetch(url, options) .then(res => res.json()) .then(json => console.log(json)) .catch(err => console.error(err)); ``` ### Python ```python import requests url = "https://truememory-api.mvstg.com/v1/audio/speech" payload = { "model": "gpt-4o-mini-tts", "input": "string", "voice": "alloy", "response_format": "mp3", "speed": 1, "language": "string" } headers = { "Content-Type": "application/json", "Authorization": "Bearer " } response = requests.post(url, json=payload, headers=headers) print(response.text) ``` ## Responses ### 200 The generated audio as raw bytes — this response is a binary audio stream, not JSON. The `Content-Type` reflects the format the provider returned. ### 422 Validation Error #### Example ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string", "input": null, "ctx": {} } ] } ``` - `detail` (`create_speech.response.422.detail`; array, optional) - `loc` (`create_speech.response.422.detail.loc`; array, required) - any of: `string`, `integer` - `msg` (`create_speech.response.422.detail.msg`; string, required) - `type` (`create_speech.response.422.detail.type`; string, required) - `input` (`create_speech.response.422.detail.input`; unknown, optional) - `ctx` (`create_speech.response.422.detail.ctx`; object, optional) # Transcribe audio > Transcribe audio to text. Drop-in replacement for OpenAI /v1/audio/transcriptions. Accepts multipart/form-data with an audio file. Supported models: whisper-1, google/gemini-2.5-flash. Accepts tri-mode auth (user token / service+on-behalf-of / service-only) — the service-key path is required for backend callers like BlinkLife that proxy STT on a user's behalf, mirroring /v1/audio/speech. Source: https://truememory-docs.mvstg.com/api/audio/transcriptions/ · Markdown: https://truememory-docs.mvstg.com/api/audio/transcriptions/index.md Path: AI Gateway API › Chat `POST /v1/audio/transcriptions` Transcribe audio to text. Drop-in replacement for OpenAI /v1/audio/transcriptions. Accepts multipart/form-data with an audio file. Supported models: whisper-1, google/gemini-2.5-flash. Accepts tri-mode auth (user token / service+on-behalf-of / service-only) — the service-key path is required for backend callers like BlinkLife that proxy STT on a user's behalf, mirroring /v1/audio/speech. ## Authentication - `HTTPBearer`, http, header `Authorization` ## Header parameters - `X-On-Behalf-Of` (`create_transcription.header.X-On-Behalf-Of`; any of, optional) - any of: `string`, `null` - `X-External-User-Id` (`create_transcription.header.X-External-User-Id`; any of, optional) - any of: `string`, `null` - `X-App-Id` (`create_transcription.header.X-App-Id`; any of, optional) - any of: `string`, `null` ## Request body _Required._ - `file` (`create_transcription.file`; string, required) - `model` (`create_transcription.model`; string, optional) - default `"whisper-1"` - `language` (`create_transcription.language`; any of, optional) - any of: `string`, `null` - `prompt` (`create_transcription.prompt`; any of, optional) - any of: `string`, `null` - `response_format` (`create_transcription.response_format`; string, optional) - default `"json"` - `temperature` (`create_transcription.temperature`; number, optional) - default `0` ## Example request ```text { "file": "string", "model": "whisper-1", "language": "string", "prompt": "string", "response_format": "json", "temperature": 0 } ``` ## Code samples ### cURL ```bash curl --request POST \ --url https://truememory-api.mvstg.com/v1/audio/transcriptions \ --header 'Authorization: Bearer ' \ --form-string 'file=string' \ --form-string 'model=whisper-1' \ --form-string 'language=string' \ --form-string 'prompt=string' \ --form-string 'response_format=json' \ --form-string 'temperature=0' ``` ### TypeScript ```typescript const formData = new FormData(); formData.append('file', 'string'); formData.append('model', 'whisper-1'); formData.append('language', 'string'); formData.append('prompt', 'string'); formData.append('response_format', 'json'); formData.append('temperature', '0'); const url = 'https://truememory-api.mvstg.com/v1/audio/transcriptions'; const options = {method: 'POST', headers: {Authorization: 'Bearer '}, body: formData}; fetch(url, options) .then(res => res.json()) .then(json => console.log(json)) .catch(err => console.error(err)); ``` ### Python ```python import requests url = "https://truememory-api.mvstg.com/v1/audio/transcriptions" files = [ ("file", (None, "string")), ("model", (None, "whisper-1")), ("language", (None, "string")), ("prompt", (None, "string")), ("response_format", (None, "json")), ("temperature", (None, "0")) ] headers = {"Authorization": "Bearer "} response = requests.post(url, files=files, headers=headers) print(response.text) ``` ## Responses ### 200 The transcription. JSON by default; set `response_format` to `text`, `srt` or `vtt` for those formats instead. #### Example ```json {} ``` Any of: - `object` - `string` ### 422 Validation Error #### Example ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string", "input": null, "ctx": {} } ] } ``` - `detail` (`create_transcription.response.422.detail`; array, optional) - `loc` (`create_transcription.response.422.detail.loc`; array, required) - any of: `string`, `integer` - `msg` (`create_transcription.response.422.detail.msg`; string, required) - `type` (`create_transcription.response.422.detail.type`; string, required) - `input` (`create_transcription.response.422.detail.input`; unknown, optional) - `ctx` (`create_transcription.response.422.detail.ctx`; object, optional) # Create a brain > Create a brain — a named collection that groups knowledge packs so they can be attached to a conversation together. Source: https://truememory-docs.mvstg.com/api/brains/create/ · Markdown: https://truememory-docs.mvstg.com/api/brains/create/index.md Path: AI Gateway API › Brains `POST /brains` Create a brain — a named collection that groups knowledge packs so they can be attached to a conversation together. ## Authentication - `HTTPBearer`, http, header `Authorization` ## Header parameters - `X-On-Behalf-Of` (`create_brain.header.X-On-Behalf-Of`; any of, optional) - any of: `string`, `null` - `X-External-User-Id` (`create_brain.header.X-External-User-Id`; any of, optional) - any of: `string`, `null` - `X-App-Id` (`create_brain.header.X-App-Id`; any of, optional) - any of: `string`, `null` ## Request body _Required._ - `name` (`create_brain.name`; string, required) - `description` (`create_brain.description`; any of, optional) - any of: `string`, `null` - `app_id` (`create_brain.app_id`; any of, optional) - any of: `string`, `null` - `emoji` (`create_brain.emoji`; any of, optional) - any of: `string`, `null` - `usage_hint` (`create_brain.usage_hint`; any of, optional) - any of: `string`, `null` - `visibility` (`create_brain.visibility`; string, optional) - one of `"private"`, `"app"`, `"public"`; default `"private"` - `skill_ids` (`create_brain.skill_ids`; array, optional) - format `uuid` ## Example request ```json { "name": "string", "description": "string", "app_id": "string", "emoji": "string", "usage_hint": "string", "visibility": "private", "skill_ids": [ "497f6eca-6276-4993-bfeb-53cbbbba6f08" ] } ``` ## Code samples ### cURL ```bash curl --request POST \ --url https://truememory-api.mvstg.com/brains \ --header 'Authorization: Bearer ' \ --header 'Content-Type: application/json' \ --data ' { "name": "string", "description": "string", "app_id": "string", "emoji": "string", "usage_hint": "string", "visibility": "private", "skill_ids": [ "497f6eca-6276-4993-bfeb-53cbbbba6f08" ] } ' ``` ### TypeScript ```typescript const url = 'https://truememory-api.mvstg.com/brains'; const options = { method: 'POST', headers: {'Content-Type': 'application/json', Authorization: 'Bearer '}, body: JSON.stringify({ name: 'string', description: 'string', app_id: 'string', emoji: 'string', usage_hint: 'string', visibility: 'private', skill_ids: ['497f6eca-6276-4993-bfeb-53cbbbba6f08'] }) }; fetch(url, options) .then(res => res.json()) .then(json => console.log(json)) .catch(err => console.error(err)); ``` ### Python ```python import requests url = "https://truememory-api.mvstg.com/brains" payload = { "name": "string", "description": "string", "app_id": "string", "emoji": "string", "usage_hint": "string", "visibility": "private", "skill_ids": ["497f6eca-6276-4993-bfeb-53cbbbba6f08"] } headers = { "Content-Type": "application/json", "Authorization": "Bearer " } response = requests.post(url, json=payload, headers=headers) print(response.text) ``` ## Responses ### 201 Successful Response #### Example ```json { "id": "string", "user_id": "string", "app_id": "string", "name": "string", "description": "string", "emoji": "string", "usage_hint": "string", "visibility": "private", "skills": [ { "id": "string", "key": "string", "name": "string" } ], "document_count": 0, "created_at": "2019-08-24T14:15:22Z", "updated_at": "2019-08-24T14:15:22Z", "skill_ids": [ "string" ] } ``` - `id` (`create_brain.response.201.id`; string, required) - `user_id` (`create_brain.response.201.user_id`; any of, required) - any of: `string`, `null` - `app_id` (`create_brain.response.201.app_id`; any of, required) - any of: `string`, `null` - `name` (`create_brain.response.201.name`; string, required) - `description` (`create_brain.response.201.description`; any of, required) - any of: `string`, `null` - `created_at` (`create_brain.response.201.created_at`; string, required) - format `date-time` - `updated_at` (`create_brain.response.201.updated_at`; string, required) - format `date-time` - `skill_ids` (`create_brain.response.201.skill_ids`; array, required) — Ids of ``skills``, for clients that only need to seed a multi-select. Derived rather than stored so it cannot disagree with ``skills``. - `emoji` (`create_brain.response.201.emoji`; any of, optional) - any of: `string`, `null` - `usage_hint` (`create_brain.response.201.usage_hint`; any of, optional) - any of: `string`, `null` - `visibility` (`create_brain.response.201.visibility`; string, optional) - one of `"private"`, `"app"`, `"public"`; default `"private"` - `skills` (`create_brain.response.201.skills`; array, optional) - `id` (`create_brain.response.201.skills.id`; string, required) - `key` (`create_brain.response.201.skills.key`; string, required) - `name` (`create_brain.response.201.skills.name`; string, required) - `document_count` (`create_brain.response.201.document_count`; integer, optional) - default `0` ### 422 Validation Error #### Example ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string", "input": null, "ctx": {} } ] } ``` - `detail` (`create_brain.response.422.detail`; array, optional) - `loc` (`create_brain.response.422.detail.loc`; array, required) - any of: `string`, `integer` - `msg` (`create_brain.response.422.detail.msg`; string, required) - `type` (`create_brain.response.422.detail.type`; string, required) - `input` (`create_brain.response.422.detail.input`; unknown, optional) - `ctx` (`create_brain.response.422.detail.ctx`; object, optional) # Delete a brain > Delete a brain. The knowledge packs inside it are not deleted — they only lose this grouping. Returns 204 with no body. Source: https://truememory-docs.mvstg.com/api/brains/delete/ · Markdown: https://truememory-docs.mvstg.com/api/brains/delete/index.md Path: AI Gateway API › Brains `DELETE /brains/{brain_id}` Delete a brain. The knowledge packs inside it are not deleted — they only lose this grouping. Returns 204 with no body. ## Authentication - `HTTPBearer`, http, header `Authorization` ## Path parameters - `brain_id` (`delete_brain.path.brain_id`; string, required) - format `uuid` ## Header parameters - `X-On-Behalf-Of` (`delete_brain.header.X-On-Behalf-Of`; any of, optional) - any of: `string`, `null` - `X-External-User-Id` (`delete_brain.header.X-External-User-Id`; any of, optional) - any of: `string`, `null` - `X-App-Id` (`delete_brain.header.X-App-Id`; any of, optional) - any of: `string`, `null` ## Code samples ### cURL ```bash curl --request DELETE \ --url 'https://truememory-api.mvstg.com/brains/' \ --header 'Authorization: Bearer ' ``` ### TypeScript ```typescript const url = 'https://truememory-api.mvstg.com/brains/'; const options = {method: 'DELETE', headers: {Authorization: 'Bearer '}}; fetch(url, options) .then(res => res.json()) .then(json => console.log(json)) .catch(err => console.error(err)); ``` ### Python ```python import requests url = "https://truememory-api.mvstg.com/brains/" headers = {"Authorization": "Bearer "} response = requests.delete(url, headers=headers) print(response.text) ``` ## Responses ### 204 Successful Response ### 422 Validation Error #### Example ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string", "input": null, "ctx": {} } ] } ``` - `detail` (`delete_brain.response.422.detail`; array, optional) - `loc` (`delete_brain.response.422.detail.loc`; array, required) - any of: `string`, `integer` - `msg` (`delete_brain.response.422.detail.msg`; string, required) - `type` (`delete_brain.response.422.detail.type`; string, required) - `input` (`delete_brain.response.422.detail.input`; unknown, optional) - `ctx` (`delete_brain.response.422.detail.ctx`; object, optional) # Get a brain > Fetch one brain by id, including its skills and metadata. Returns 404 if you have no access to it. Source: https://truememory-docs.mvstg.com/api/brains/get/ · Markdown: https://truememory-docs.mvstg.com/api/brains/get/index.md Path: AI Gateway API › Brains `GET /brains/{brain_id}` Fetch one brain by id, including its skills and metadata. Returns 404 if you have no access to it. ## Authentication - `HTTPBearer`, http, header `Authorization` ## Path parameters - `brain_id` (`get_brain.path.brain_id`; string, required) - format `uuid` ## Header parameters - `X-App-Id` (`get_brain.header.X-App-Id`; any of, optional) - any of: `string`, `null` - `X-On-Behalf-Of` (`get_brain.header.X-On-Behalf-Of`; any of, optional) - any of: `string`, `null` - `X-External-User-Id` (`get_brain.header.X-External-User-Id`; any of, optional) - any of: `string`, `null` ## Code samples ### cURL ```bash curl --request GET \ --url 'https://truememory-api.mvstg.com/brains/' \ --header 'Authorization: Bearer ' ``` ### TypeScript ```typescript const url = 'https://truememory-api.mvstg.com/brains/'; const options = {method: 'GET', headers: {Authorization: 'Bearer '}}; fetch(url, options) .then(res => res.json()) .then(json => console.log(json)) .catch(err => console.error(err)); ``` ### Python ```python import requests url = "https://truememory-api.mvstg.com/brains/" headers = {"Authorization": "Bearer "} response = requests.get(url, headers=headers) print(response.text) ``` ## Responses ### 200 Successful Response #### Example ```json { "id": "string", "user_id": "string", "app_id": "string", "name": "string", "description": "string", "emoji": "string", "usage_hint": "string", "visibility": "private", "skills": [ { "id": "string", "key": "string", "name": "string" } ], "document_count": 0, "created_at": "2019-08-24T14:15:22Z", "updated_at": "2019-08-24T14:15:22Z", "skill_ids": [ "string" ] } ``` - `id` (`get_brain.response.200.id`; string, required) - `user_id` (`get_brain.response.200.user_id`; any of, required) - any of: `string`, `null` - `app_id` (`get_brain.response.200.app_id`; any of, required) - any of: `string`, `null` - `name` (`get_brain.response.200.name`; string, required) - `description` (`get_brain.response.200.description`; any of, required) - any of: `string`, `null` - `created_at` (`get_brain.response.200.created_at`; string, required) - format `date-time` - `updated_at` (`get_brain.response.200.updated_at`; string, required) - format `date-time` - `skill_ids` (`get_brain.response.200.skill_ids`; array, required) — Ids of ``skills``, for clients that only need to seed a multi-select. Derived rather than stored so it cannot disagree with ``skills``. - `emoji` (`get_brain.response.200.emoji`; any of, optional) - any of: `string`, `null` - `usage_hint` (`get_brain.response.200.usage_hint`; any of, optional) - any of: `string`, `null` - `visibility` (`get_brain.response.200.visibility`; string, optional) - one of `"private"`, `"app"`, `"public"`; default `"private"` - `skills` (`get_brain.response.200.skills`; array, optional) - `id` (`get_brain.response.200.skills.id`; string, required) - `key` (`get_brain.response.200.skills.key`; string, required) - `name` (`get_brain.response.200.skills.name`; string, required) - `document_count` (`get_brain.response.200.document_count`; integer, optional) - default `0` ### 422 Validation Error #### Example ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string", "input": null, "ctx": {} } ] } ``` - `detail` (`get_brain.response.422.detail`; array, optional) - `loc` (`get_brain.response.422.detail.loc`; array, required) - any of: `string`, `integer` - `msg` (`get_brain.response.422.detail.msg`; string, required) - `type` (`get_brain.response.422.detail.type`; string, required) - `input` (`get_brain.response.422.detail.input`; unknown, optional) - `ctx` (`get_brain.response.422.detail.ctx`; object, optional) # Add a knowledge pack to a brain > Add a pack you own to a brain. Returns the pack as it now sits in the brain. Source: https://truememory-docs.mvstg.com/api/brains/knowledge-packs/create/ · Markdown: https://truememory-docs.mvstg.com/api/brains/knowledge-packs/create/index.md Path: AI Gateway API › Brains `POST /brains/{brain_id}/knowledge-packs` Add a pack you own to a brain. Returns the pack as it now sits in the brain. ## Authentication - `HTTPBearer`, http, header `Authorization` ## Path parameters - `brain_id` (`add_knowledge_pack_to_brain.path.brain_id`; string, required) - format `uuid` ## Header parameters - `X-On-Behalf-Of` (`add_knowledge_pack_to_brain.header.X-On-Behalf-Of`; any of, optional) - any of: `string`, `null` - `X-External-User-Id` (`add_knowledge_pack_to_brain.header.X-External-User-Id`; any of, optional) - any of: `string`, `null` - `X-App-Id` (`add_knowledge_pack_to_brain.header.X-App-Id`; any of, optional) - any of: `string`, `null` ## Request body _Required._ - `pack_id` (`add_knowledge_pack_to_brain.pack_id`; string, required) - format `uuid` ## Example request ```json { "pack_id": "57cc3d41-f96b-4516-a620-a3167d9dc6bf" } ``` ## Code samples ### cURL ```bash curl --request POST \ --url 'https://truememory-api.mvstg.com/brains//knowledge-packs' \ --header 'Authorization: Bearer ' \ --header 'Content-Type: application/json' \ --data ' { "pack_id": "57cc3d41-f96b-4516-a620-a3167d9dc6bf" } ' ``` ### TypeScript ```typescript const url = 'https://truememory-api.mvstg.com/brains//knowledge-packs'; const options = { method: 'POST', headers: {'Content-Type': 'application/json', Authorization: 'Bearer '}, body: JSON.stringify({pack_id: '57cc3d41-f96b-4516-a620-a3167d9dc6bf'}) }; fetch(url, options) .then(res => res.json()) .then(json => console.log(json)) .catch(err => console.error(err)); ``` ### Python ```python import requests url = "https://truememory-api.mvstg.com/brains//knowledge-packs" payload = { "pack_id": "57cc3d41-f96b-4516-a620-a3167d9dc6bf" } headers = { "Content-Type": "application/json", "Authorization": "Bearer " } response = requests.post(url, json=payload, headers=headers) print(response.text) ``` ## Responses ### 201 Successful Response #### Example ```json { "id": "string", "user_id": "string", "app_id": "string", "skill_id": "string", "filename": "string", "content_type": "string", "status": "pending", "visibility": "private", "created_at": "2019-08-24T14:15:22Z" } ``` - `id` (`add_knowledge_pack_to_brain.response.201.id`; string, required) - `user_id` (`add_knowledge_pack_to_brain.response.201.user_id`; any of, required) - any of: `string`, `null` - `app_id` (`add_knowledge_pack_to_brain.response.201.app_id`; any of, required) - any of: `string`, `null` - `skill_id` (`add_knowledge_pack_to_brain.response.201.skill_id`; any of, required) - any of: `string`, `null` - `filename` (`add_knowledge_pack_to_brain.response.201.filename`; string, required) - `content_type` (`add_knowledge_pack_to_brain.response.201.content_type`; string, required) - `status` (`add_knowledge_pack_to_brain.response.201.status`; string, required) - one of `"pending"`, `"processing"`, `"ready"`, `"error"` - `created_at` (`add_knowledge_pack_to_brain.response.201.created_at`; string, required) - format `date-time` - `visibility` (`add_knowledge_pack_to_brain.response.201.visibility`; string, optional) - one of `"private"`, `"app"`, `"public"`; default `"private"` ### 422 Validation Error #### Example ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string", "input": null, "ctx": {} } ] } ``` - `detail` (`add_knowledge_pack_to_brain.response.422.detail`; array, optional) - `loc` (`add_knowledge_pack_to_brain.response.422.detail.loc`; array, required) - any of: `string`, `integer` - `msg` (`add_knowledge_pack_to_brain.response.422.detail.msg`; string, required) - `type` (`add_knowledge_pack_to_brain.response.422.detail.type`; string, required) - `input` (`add_knowledge_pack_to_brain.response.422.detail.input`; unknown, optional) - `ctx` (`add_knowledge_pack_to_brain.response.422.detail.ctx`; object, optional) # Remove a knowledge pack from a brain > Remove a pack from a brain. The pack itself is untouched and stays available on its own. Returns 204 with no body. Source: https://truememory-docs.mvstg.com/api/brains/knowledge-packs/delete/ · Markdown: https://truememory-docs.mvstg.com/api/brains/knowledge-packs/delete/index.md Path: AI Gateway API › Brains `DELETE /brains/{brain_id}/knowledge-packs/{pack_id}` Remove a pack from a brain. The pack itself is untouched and stays available on its own. Returns 204 with no body. ## Authentication - `HTTPBearer`, http, header `Authorization` ## Path parameters - `brain_id` (`remove_knowledge_pack_from_brain.path.brain_id`; string, required) - format `uuid` - `pack_id` (`remove_knowledge_pack_from_brain.path.pack_id`; string, required) - format `uuid` ## Header parameters - `X-On-Behalf-Of` (`remove_knowledge_pack_from_brain.header.X-On-Behalf-Of`; any of, optional) - any of: `string`, `null` - `X-External-User-Id` (`remove_knowledge_pack_from_brain.header.X-External-User-Id`; any of, optional) - any of: `string`, `null` - `X-App-Id` (`remove_knowledge_pack_from_brain.header.X-App-Id`; any of, optional) - any of: `string`, `null` ## Code samples ### cURL ```bash curl --request DELETE \ --url 'https://truememory-api.mvstg.com/brains//knowledge-packs/' \ --header 'Authorization: Bearer ' ``` ### TypeScript ```typescript const url = 'https://truememory-api.mvstg.com/brains//knowledge-packs/'; const options = {method: 'DELETE', headers: {Authorization: 'Bearer '}}; fetch(url, options) .then(res => res.json()) .then(json => console.log(json)) .catch(err => console.error(err)); ``` ### Python ```python import requests url = "https://truememory-api.mvstg.com/brains//knowledge-packs/" headers = {"Authorization": "Bearer "} response = requests.delete(url, headers=headers) print(response.text) ``` ## Responses ### 204 Successful Response ### 422 Validation Error #### Example ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string", "input": null, "ctx": {} } ] } ``` - `detail` (`remove_knowledge_pack_from_brain.response.422.detail`; array, optional) - `loc` (`remove_knowledge_pack_from_brain.response.422.detail.loc`; array, required) - any of: `string`, `integer` - `msg` (`remove_knowledge_pack_from_brain.response.422.detail.msg`; string, required) - `type` (`remove_knowledge_pack_from_brain.response.422.detail.type`; string, required) - `input` (`remove_knowledge_pack_from_brain.response.422.detail.input`; unknown, optional) - `ctx` (`remove_knowledge_pack_from_brain.response.422.detail.ctx`; object, optional) # List a brain's knowledge packs > List the packs grouped into this brain, in the order they were added. Source: https://truememory-docs.mvstg.com/api/brains/knowledge-packs/list/ · Markdown: https://truememory-docs.mvstg.com/api/brains/knowledge-packs/list/index.md Path: AI Gateway API › Brains `GET /brains/{brain_id}/knowledge-packs` List the packs grouped into this brain, in the order they were added. ## Authentication - `HTTPBearer`, http, header `Authorization` ## Path parameters - `brain_id` (`list_knowledge_packs_in_brain.path.brain_id`; string, required) - format `uuid` ## Header parameters - `X-App-Id` (`list_knowledge_packs_in_brain.header.X-App-Id`; any of, optional) - any of: `string`, `null` - `X-On-Behalf-Of` (`list_knowledge_packs_in_brain.header.X-On-Behalf-Of`; any of, optional) - any of: `string`, `null` - `X-External-User-Id` (`list_knowledge_packs_in_brain.header.X-External-User-Id`; any of, optional) - any of: `string`, `null` ## Code samples ### cURL ```bash curl --request GET \ --url 'https://truememory-api.mvstg.com/brains//knowledge-packs' \ --header 'Authorization: Bearer ' ``` ### TypeScript ```typescript const url = 'https://truememory-api.mvstg.com/brains//knowledge-packs'; const options = {method: 'GET', headers: {Authorization: 'Bearer '}}; fetch(url, options) .then(res => res.json()) .then(json => console.log(json)) .catch(err => console.error(err)); ``` ### Python ```python import requests url = "https://truememory-api.mvstg.com/brains//knowledge-packs" headers = {"Authorization": "Bearer "} response = requests.get(url, headers=headers) print(response.text) ``` ## Responses ### 200 Successful Response #### Example ```json { "packs": [ { "id": "string", "user_id": "string", "app_id": "string", "skill_id": "string", "filename": "string", "content_type": "string", "status": "pending", "visibility": "private", "created_at": "2019-08-24T14:15:22Z" } ], "total": 0 } ``` - `packs` (`list_knowledge_packs_in_brain.response.200.packs`; array, required) - `id` (`list_knowledge_packs_in_brain.response.200.packs.id`; string, required) - `user_id` (`list_knowledge_packs_in_brain.response.200.packs.user_id`; any of, required) - any of: `string`, `null` - `app_id` (`list_knowledge_packs_in_brain.response.200.packs.app_id`; any of, required) - any of: `string`, `null` - `skill_id` (`list_knowledge_packs_in_brain.response.200.packs.skill_id`; any of, required) - any of: `string`, `null` - `filename` (`list_knowledge_packs_in_brain.response.200.packs.filename`; string, required) - `content_type` (`list_knowledge_packs_in_brain.response.200.packs.content_type`; string, required) - `status` (`list_knowledge_packs_in_brain.response.200.packs.status`; string, required) - one of `"pending"`, `"processing"`, `"ready"`, `"error"` - `created_at` (`list_knowledge_packs_in_brain.response.200.packs.created_at`; string, required) - format `date-time` - `visibility` (`list_knowledge_packs_in_brain.response.200.packs.visibility`; string, optional) - one of `"private"`, `"app"`, `"public"`; default `"private"` - `total` (`list_knowledge_packs_in_brain.response.200.total`; integer, required) — Total rows matching the filters before LIMIT/OFFSET. ### 422 Validation Error #### Example ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string", "input": null, "ctx": {} } ] } ``` - `detail` (`list_knowledge_packs_in_brain.response.422.detail`; array, optional) - `loc` (`list_knowledge_packs_in_brain.response.422.detail.loc`; array, required) - any of: `string`, `integer` - `msg` (`list_knowledge_packs_in_brain.response.422.detail.msg`; string, required) - `type` (`list_knowledge_packs_in_brain.response.422.detail.type`; string, required) - `input` (`list_knowledge_packs_in_brain.response.422.detail.input`; unknown, optional) - `ctx` (`list_knowledge_packs_in_brain.response.422.detail.ctx`; object, optional) # List your brains > List the brains you own, plus any shared with you. Source: https://truememory-docs.mvstg.com/api/brains/list/ · Markdown: https://truememory-docs.mvstg.com/api/brains/list/index.md Path: AI Gateway API › Brains `GET /brains` List the brains you own, plus any shared with you. ## Authentication - `HTTPBearer`, http, header `Authorization` ## Query parameters - `app_id` (`list_brains.query.app_id`; any of, optional) - any of: `string`, `null` - `limit` (`list_brains.query.limit`; integer, optional) - max 200; default `50` - `offset` (`list_brains.query.offset`; integer, optional) - default `0` ## Header parameters - `X-App-Id` (`list_brains.header.X-App-Id`; any of, optional) - any of: `string`, `null` - `X-On-Behalf-Of` (`list_brains.header.X-On-Behalf-Of`; any of, optional) - any of: `string`, `null` - `X-External-User-Id` (`list_brains.header.X-External-User-Id`; any of, optional) - any of: `string`, `null` ## Code samples ### cURL ```bash curl --request GET \ --url https://truememory-api.mvstg.com/brains \ --header 'Authorization: Bearer ' ``` ### TypeScript ```typescript const url = 'https://truememory-api.mvstg.com/brains'; const options = {method: 'GET', headers: {Authorization: 'Bearer '}}; fetch(url, options) .then(res => res.json()) .then(json => console.log(json)) .catch(err => console.error(err)); ``` ### Python ```python import requests url = "https://truememory-api.mvstg.com/brains" headers = {"Authorization": "Bearer "} response = requests.get(url, headers=headers) print(response.text) ``` ## Responses ### 200 Successful Response #### Example ```json { "brains": [ { "id": "string", "user_id": "string", "app_id": "string", "name": "string", "description": "string", "emoji": "string", "usage_hint": "string", "visibility": "private", "skills": [ { "id": "string", "key": "string", "name": "string" } ], "document_count": 0, "created_at": "2019-08-24T14:15:22Z", "updated_at": "2019-08-24T14:15:22Z", "skill_ids": [ "string" ] } ], "total": 0 } ``` - `brains` (`list_brains.response.200.brains`; array, required) - `id` (`list_brains.response.200.brains.id`; string, required) - `user_id` (`list_brains.response.200.brains.user_id`; any of, required) - any of: `string`, `null` - `app_id` (`list_brains.response.200.brains.app_id`; any of, required) - any of: `string`, `null` - `name` (`list_brains.response.200.brains.name`; string, required) - `description` (`list_brains.response.200.brains.description`; any of, required) - any of: `string`, `null` - `created_at` (`list_brains.response.200.brains.created_at`; string, required) - format `date-time` - `updated_at` (`list_brains.response.200.brains.updated_at`; string, required) - format `date-time` - `skill_ids` (`list_brains.response.200.brains.skill_ids`; array, required) — Ids of ``skills``, for clients that only need to seed a multi-select. Derived rather than stored so it cannot disagree with ``skills``. - `emoji` (`list_brains.response.200.brains.emoji`; any of, optional) - any of: `string`, `null` - `usage_hint` (`list_brains.response.200.brains.usage_hint`; any of, optional) - any of: `string`, `null` - `visibility` (`list_brains.response.200.brains.visibility`; string, optional) - one of `"private"`, `"app"`, `"public"`; default `"private"` - `skills` (`list_brains.response.200.brains.skills`; array, optional) - `id` (`list_brains.response.200.brains.skills.id`; string, required) - `key` (`list_brains.response.200.brains.skills.key`; string, required) - `name` (`list_brains.response.200.brains.skills.name`; string, required) - `document_count` (`list_brains.response.200.brains.document_count`; integer, optional) - default `0` - `total` (`list_brains.response.200.total`; integer, required) — Total rows matching the filters before LIMIT/OFFSET. ### 422 Validation Error #### Example ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string", "input": null, "ctx": {} } ] } ``` - `detail` (`list_brains.response.422.detail`; array, optional) - `loc` (`list_brains.response.422.detail.loc`; array, required) - any of: `string`, `integer` - `msg` (`list_brains.response.422.detail.msg`; string, required) - `type` (`list_brains.response.422.detail.type`; string, required) - `input` (`list_brains.response.422.detail.input`; unknown, optional) - `ctx` (`list_brains.response.422.detail.ctx`; object, optional) # Update a brain > Update a brain's name, description, emoji, usage hint, visibility or skills. Omitted fields are left as they are. Source: https://truememory-docs.mvstg.com/api/brains/update/ · Markdown: https://truememory-docs.mvstg.com/api/brains/update/index.md Path: AI Gateway API › Brains `PATCH /brains/{brain_id}` Update a brain's name, description, emoji, usage hint, visibility or skills. Omitted fields are left as they are. ## Authentication - `HTTPBearer`, http, header `Authorization` ## Path parameters - `brain_id` (`update_brain.path.brain_id`; string, required) - format `uuid` ## Header parameters - `X-On-Behalf-Of` (`update_brain.header.X-On-Behalf-Of`; any of, optional) - any of: `string`, `null` - `X-External-User-Id` (`update_brain.header.X-External-User-Id`; any of, optional) - any of: `string`, `null` - `X-App-Id` (`update_brain.header.X-App-Id`; any of, optional) - any of: `string`, `null` ## Request body _Required._ - `name` (`update_brain.name`; any of, optional) - any of: `string`, `null` - `description` (`update_brain.description`; any of, optional) - any of: `string`, `null` - `emoji` (`update_brain.emoji`; any of, optional) - any of: `string`, `null` - `usage_hint` (`update_brain.usage_hint`; any of, optional) - any of: `string`, `null` - `visibility` (`update_brain.visibility`; any of, optional) - any of: `string`, `null` - `skill_ids` (`update_brain.skill_ids`; any of, optional) - any of: `array`, `null` ## Example request ```json { "name": "string", "description": "string", "emoji": "string", "usage_hint": "string", "visibility": "private", "skill_ids": [ "497f6eca-6276-4993-bfeb-53cbbbba6f08" ] } ``` ## Code samples ### cURL ```bash curl --request PATCH \ --url 'https://truememory-api.mvstg.com/brains/' \ --header 'Authorization: Bearer ' \ --header 'Content-Type: application/json' \ --data ' { "name": "string", "description": "string", "emoji": "string", "usage_hint": "string", "visibility": "private", "skill_ids": [ "497f6eca-6276-4993-bfeb-53cbbbba6f08" ] } ' ``` ### TypeScript ```typescript const url = 'https://truememory-api.mvstg.com/brains/'; const options = { method: 'PATCH', headers: {'Content-Type': 'application/json', Authorization: 'Bearer '}, body: JSON.stringify({ name: 'string', description: 'string', emoji: 'string', usage_hint: 'string', visibility: 'private', skill_ids: ['497f6eca-6276-4993-bfeb-53cbbbba6f08'] }) }; fetch(url, options) .then(res => res.json()) .then(json => console.log(json)) .catch(err => console.error(err)); ``` ### Python ```python import requests url = "https://truememory-api.mvstg.com/brains/" payload = { "name": "string", "description": "string", "emoji": "string", "usage_hint": "string", "visibility": "private", "skill_ids": ["497f6eca-6276-4993-bfeb-53cbbbba6f08"] } headers = { "Content-Type": "application/json", "Authorization": "Bearer " } response = requests.patch(url, json=payload, headers=headers) print(response.text) ``` ## Responses ### 200 Successful Response #### Example ```json { "id": "string", "user_id": "string", "app_id": "string", "name": "string", "description": "string", "emoji": "string", "usage_hint": "string", "visibility": "private", "skills": [ { "id": "string", "key": "string", "name": "string" } ], "document_count": 0, "created_at": "2019-08-24T14:15:22Z", "updated_at": "2019-08-24T14:15:22Z", "skill_ids": [ "string" ] } ``` - `id` (`update_brain.response.200.id`; string, required) - `user_id` (`update_brain.response.200.user_id`; any of, required) - any of: `string`, `null` - `app_id` (`update_brain.response.200.app_id`; any of, required) - any of: `string`, `null` - `name` (`update_brain.response.200.name`; string, required) - `description` (`update_brain.response.200.description`; any of, required) - any of: `string`, `null` - `created_at` (`update_brain.response.200.created_at`; string, required) - format `date-time` - `updated_at` (`update_brain.response.200.updated_at`; string, required) - format `date-time` - `skill_ids` (`update_brain.response.200.skill_ids`; array, required) — Ids of ``skills``, for clients that only need to seed a multi-select. Derived rather than stored so it cannot disagree with ``skills``. - `emoji` (`update_brain.response.200.emoji`; any of, optional) - any of: `string`, `null` - `usage_hint` (`update_brain.response.200.usage_hint`; any of, optional) - any of: `string`, `null` - `visibility` (`update_brain.response.200.visibility`; string, optional) - one of `"private"`, `"app"`, `"public"`; default `"private"` - `skills` (`update_brain.response.200.skills`; array, optional) - `id` (`update_brain.response.200.skills.id`; string, required) - `key` (`update_brain.response.200.skills.key`; string, required) - `name` (`update_brain.response.200.skills.name`; string, required) - `document_count` (`update_brain.response.200.document_count`; integer, optional) - default `0` ### 422 Validation Error #### Example ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string", "input": null, "ctx": {} } ] } ``` - `detail` (`update_brain.response.422.detail`; array, optional) - `loc` (`update_brain.response.422.detail.loc`; array, required) - any of: `string`, `integer` - `msg` (`update_brain.response.422.detail.msg`; string, required) - `type` (`update_brain.response.422.detail.type`; string, required) - `input` (`update_brain.response.422.detail.input`; unknown, optional) - `ctx` (`update_brain.response.422.detail.ctx`; object, optional) # Create a chat completion > Drop-in replacement for OpenAI's `/v1/chat/completions`, with Truememory's persona and memory layers applied to every turn. Supports tools, streaming and all standard parameters. The response comes in one of two shapes, depending on `stream`: - `stream: false` (default) — a single JSON `ChatCompletionResponse`. - `stream: true` — `text/event-stream` of `data:` lines, each an OpenAI-style chunk, terminated by `data: [DONE]`. Source: https://truememory-docs.mvstg.com/api/chat/completions/ · Markdown: https://truememory-docs.mvstg.com/api/chat/completions/index.md Path: AI Gateway API › Chat `POST /v1/chat/completions` Drop-in replacement for OpenAI's `/v1/chat/completions`, with Truememory's persona and memory layers applied to every turn. Supports tools, streaming and all standard parameters. The response comes in one of two shapes, depending on `stream`: - `stream: false` (default) — a single JSON `ChatCompletionResponse`. - `stream: true` — `text/event-stream` of `data:` lines, each an OpenAI-style chunk, terminated by `data: [DONE]`. ## Authentication - `HTTPBearer`, http, header `Authorization` ## Header parameters - `x-agent-id` (`chat_completions.header.x-agent-id`; any of, optional) — TIE agent to use - any of: `string`, `null` - `x-app-id` (`chat_completions.header.x-app-id`; any of, optional) — Application ID for scoping threads - any of: `string`, `null` - `X-On-Behalf-Of` (`chat_completions.header.X-On-Behalf-Of`; any of, optional) - any of: `string`, `null` - `X-External-User-Id` (`chat_completions.header.X-External-User-Id`; any of, optional) - any of: `string`, `null` - `X-App-Id` (`chat_completions.header.X-App-Id`; any of, optional) - any of: `string`, `null` ## Request body _Required._ - `model` (`chat_completions.model`; string, required) — Model to use, e.g. 'anthropic/claude-sonnet-4-6' - `messages` (`chat_completions.messages`; array, required) - `role` (`chat_completions.messages.role`; string, required) - one of `"system"`, `"developer"`, `"user"`, `"assistant"`, `"tool"` - `content` (`chat_completions.messages.content`; any of, optional) - any of: `string`, `array`, `null` - `tool_calls` (`chat_completions.messages.tool_calls`; any of, optional) - any of: `array`, `null` - `tool_call_id` (`chat_completions.messages.tool_call_id`; any of, optional) - any of: `string`, `null` - `name` (`chat_completions.messages.name`; any of, optional) - any of: `string`, `null` - `cache_control` (`chat_completions.messages.cache_control`; any of, optional) — Anthropic prompt-caching marker applied to the whole message. Example: ``{"type": "ephemeral"}``. Per Anthropic semantics, the marker attaches to the last content block, caching everything up to and including that block. Use this for the simple case (string-content messages); for fine-grained control, set ``cache_control`` on individual content parts. Forwarded to the underlying provider via the LangChain message's ``additional_kwargs``. - any of: `object`, `null` - `tools` (`chat_completions.tools`; any of, optional) - any of: `array`, `null` - `stream` (`chat_completions.stream`; boolean, optional) - default `false` - `stream_options` (`chat_completions.stream_options`; any of, optional) - any of: `StreamOptions`, `null` - `temperature` (`chat_completions.temperature`; any of, optional) - any of: `number`, `null` - `max_tokens` (`chat_completions.max_tokens`; any of, optional) - any of: `integer`, `null` - `top_p` (`chat_completions.top_p`; any of, optional) - any of: `number`, `null` - `seed` (`chat_completions.seed`; any of, optional) - any of: `integer`, `null` - `stop` (`chat_completions.stop`; any of, optional) - any of: `array`, `string`, `null` - `max_history_tokens` (`chat_completions.max_history_tokens`; any of, optional) — Cap the replayed thread history to roughly this many tokens, keeping the most recent turns (the window starts on a human turn). Older turns are dropped from the replay — callers that set this should supply their own running summary so older context is preserved. Omit (default) to replay the full history unchanged. - any of: `integer`, `null` - `system_prompt_cache_contract` (`chat_completions.system_prompt_cache_contract`; string, optional) — How to read multi-block system content. "legacy" (default, unchanged behaviour): a block list with no `cache_control` marker anywhere is treated as entirely STABLE — the historical reading, so existing callers that use block lists for structure are unaffected. "explicit": markers are the caller's cacheability declaration — blocks through the last marked one are stable, later blocks are dynamic, and a list with NO marker means nothing is cacheable. Opt in only if you place markers deliberately: under "explicit" an unmarked system prompt is delivered as per-request context inside the user turn, not as a system message. - one of `"legacy"`, `"explicit"`; default `"legacy"` - `prompt_cache_key` (`chat_completions.prompt_cache_key`; any of, optional) — OpenAI cache-routing key (GPT-5.6+), forwarded verbatim to the provider so same-conversation requests land on the same cache shard. Ignored for non-OpenAI models. Send an opaque, privacy-safe value (a hash, never raw user/thread identifiers). - any of: `string`, `null` - `context_management` (`chat_completions.context_management`; any of, optional) — Context-management strategies for this request. At most one entry is supported. Include a `{"type": "compaction", ...}` entry to enable thread compaction: when the upcoming request is over budget (`compact_threshold` when set, else the model's context window minus a buffer), older turns fold into a rolling summary checkpoint before the model call runs; subsequent turns replay the checkpoint plus newer messages instead of the full transcript. The thread's stored history is never modified. Omit to disable. - any of: `array`, `null` - `thread_id` (`chat_completions.thread_id`; any of, optional) — TIE thread ID for state persistence - any of: `string`, `null` - `parent_message_id` (`chat_completions.parent_message_id`; any of, optional) — ID of the last stored message the client received. When set, anything already stored after it is not appended again, so a request that failed mid-run can be re-sent unchanged. `messages` holds the messages after it, optionally preceded by replayed history that ends at it; that history is dropped exactly as it is without this field. Use the `message_id` from the previous response. Ignored without `thread_id`: a turn with no thread has no stored state. - any of: `string`, `null` - `store` (`chat_completions.store`; boolean, optional) — Whether to persist this turn to the user's chat history. Default: true. Set to false for system-internal or ephemeral calls. Memory injection (pre-LLM) and usage tracking are unaffected. - default `true` - `persona_id` (`chat_completions.persona_id`; any of, optional) — Persona to use. Falls back to the user's active persona if omitted. - any of: `string`, `null` - `metadata` (`chat_completions.metadata`; any of, optional) — Up to 16 key-value pairs attached to this request. Persisted with the thread and returned in history. - any of: `map`, `null` - `skill_instructions` (`chat_completions.skill_instructions`; any of, optional) — Caller-composed skill prompt. Injected after persona, before memory context. Never replaces persona or base instructions — purely additive. - any of: `string`, `null` - `skill_selection` (`chat_completions.skill_selection`; string, optional) — `auto` lets Truememory pick skills for this turn by matching the user's message against each visible skill's `use_case` and `detection_patterns`. Only skills the caller could already list are considered, and only ones carrying trigger data. Combines with `skills`: explicit keys always apply, matches are added after them. Defaults to `off` — no existing caller changes behaviour. - one of `"off"`, `"auto"`; default `"off"` - `skills` (`chat_completions.skills`; array, optional) — Skill keys to resolve from the registry and inject, in the order given. Truememory reads each skill's stored `prompt`, so the registry stays authoritative instead of each caller pasting a copy. A key you cannot see is a 404, never a silent drop. Resolved prompts are injected before `skill_instructions`, which still works and can be used alongside them for per-request text. - `skill_files` (`chat_completions.skill_files`; boolean, optional) — Let the model read the files of this turn's skills (from `skills` and `skill_selection`): each such skill's file list follows its prompt, and the model gets a `read_skill_file` tool. Off by default, so a skill injects only its `prompt`, as before. Ignored on a deployment without skill file storage. - default `false` - `memory` (`chat_completions.memory`; any of, optional) — Per-request memory controls. Defaults to read/write enabled for user-scoped requests. - any of: `MemoryOptions`, `null` - `tool_callback` (`chat_completions.tool_callback`; any of, optional) — Callback configuration for tool execution. When set, TIE runs the tool loop and executes caller-defined tools via HTTPS POST to {base_url}/{tool_name}. ``tools`` must also be provided. - any of: `ToolCallback`, `null` - `extra_context` (`chat_completions.extra_context`; any of, optional) — Free-form caller-provided context injected into the system prompt before tool definitions. TIE does not interpret it. - any of: `object`, `null` - `skill_id` (`chat_completions.skill_id`; any of, optional) — Skill ID whose associated knowledge packs are searched and injected into the system prompt. Works alongside skill_instructions — neither requires the other. - any of: `string`, `null` - `knowledge_pack_ids` (`chat_completions.knowledge_pack_ids`; any of, optional) — Explicit knowledge pack IDs to search. Combined with skill_id when both are provided. No document context is injected when both are absent. - any of: `array`, `null` - `brain_id` (`chat_completions.brain_id`; any of, optional) — Brain ID whose knowledge packs are searched and injected into the system prompt. Resolved to knowledge_pack_ids before context injection. Ignored when knowledge_pack_ids is also provided. Singular alias for brain_ids — when both are set, their union is resolved. - any of: `string`, `null` - `brain_ids` (`chat_completions.brain_ids`; any of, optional) — Brain IDs whose knowledge packs are searched and injected into the system prompt. All named brains are resolved to one deduplicated set of knowledge_pack_ids before context injection. Every ID must be readable by the caller — an unknown or unreachable one is a 404, not a silent drop. Ignored when knowledge_pack_ids is also provided. - any of: `array`, `null` - `mind_ids` (`chat_completions.mind_ids`; any of, optional) — Minds whose memory namespaces are blended into per-message retrieval alongside the user's own memory. Each is resolved through the mind read gate, so only minds the caller can actually read take effect; an unknown or unreachable id is a 404, not a silent drop. Namespace facts appear in the injected memory context — conversation ingestion still writes only to the user's own graph. - any of: `array`, `null` - `graph_ids` (`chat_completions.graph_ids`; any of, optional) — Raw shared knowledge-graph namespaces, by slug. Service principals only — a namespace slug carries no ownership, so honouring one from a user token would let any caller read any namespace they can guess. First-party callers name minds instead, via mind_ids. - any of: `array`, `null` ## Example request ```json { "model": "string", "messages": [ { "role": "system", "content": "string", "tool_calls": [ {} ], "tool_call_id": "string", "name": "string", "cache_control": {} } ], "tools": [ { "type": "function", "function": { "name": "list_tasks", "description": "List user tasks filtered by status", "parameters": {} } } ], "stream": false, "stream_options": { "include_usage": false }, "temperature": 0, "max_tokens": 0, "top_p": 0, "seed": 0, "stop": [ "string" ], "max_history_tokens": 0, "system_prompt_cache_contract": "legacy", "prompt_cache_key": "string", "context_management": [ { "type": "compaction", "compact_threshold": 0, "keep_tokens": 0, "model": "string" } ], "thread_id": "string", "parent_message_id": "string", "store": true, "persona_id": "string", "metadata": { "property1": "string", "property2": "string" }, "skill_instructions": "string", "skill_selection": "off", "skills": [ "summarize-thread" ], "skill_files": false, "memory": { "read": true, "write": true }, "tool_callback": { "base_url": "https://api.blinklearn.envision.io/agent-tools", "auth_token": "string", "timeout_ms": 8000 }, "extra_context": {}, "skill_id": "string", "knowledge_pack_ids": [ "string" ], "brain_id": "5ff5069c-3661-4a30-92df-9e098e7ad88f", "brain_ids": [ "497f6eca-6276-4993-bfeb-53cbbbba6f08" ], "mind_ids": [ "497f6eca-6276-4993-bfeb-53cbbbba6f08" ], "graph_ids": [ "string" ] } ``` ## Code samples ### cURL ```bash curl --request POST \ --url https://truememory-api.mvstg.com/v1/chat/completions \ --header 'Authorization: Bearer ' \ --header 'Content-Type: application/json' \ --data ' { "model": "string", "messages": [ { "role": "system", "content": "string", "tool_calls": [ {} ], "tool_call_id": "string", "name": "string", "cache_control": {} } ], "tools": [ { "type": "function", "function": { "name": "list_tasks", "description": "List user tasks filtered by status", "parameters": {} } } ], "stream": false, "stream_options": { "include_usage": false }, "temperature": 0, "max_tokens": 0, "top_p": 0, "seed": 0, "stop": [ "string" ], "max_history_tokens": 0, "system_prompt_cache_contract": "legacy", "prompt_cache_key": "string", "context_management": [ { "type": "compaction", "compact_threshold": 0, "keep_tokens": 0, "model": "string" } ], "thread_id": "string", "parent_message_id": "string", "store": true, "persona_id": "string", "metadata": { "property1": "string", "property2": "string" }, "skill_instructions": "string", "skill_selection": "off", "skills": [ "summarize-thread" ], "skill_files": false, "memory": { "read": true, "write": true }, "tool_callback": { "base_url": "https://api.blinklearn.envision.io/agent-tools", "auth_token": "string", "timeout_ms": 8000 }, "extra_context": {}, "skill_id": "string", "knowledge_pack_ids": [ "string" ], "brain_id": "5ff5069c-3661-4a30-92df-9e098e7ad88f", "brain_ids": [ "497f6eca-6276-4993-bfeb-53cbbbba6f08" ], "mind_ids": [ "497f6eca-6276-4993-bfeb-53cbbbba6f08" ], "graph_ids": [ "string" ] } ' ``` ### TypeScript ```typescript const url = 'https://truememory-api.mvstg.com/v1/chat/completions'; const options = { method: 'POST', headers: {'Content-Type': 'application/json', Authorization: 'Bearer '}, body: JSON.stringify({ model: 'string', messages: [ { role: 'system', content: 'string', tool_calls: [{}], tool_call_id: 'string', name: 'string', cache_control: {} } ], tools: [ { type: 'function', function: { name: 'list_tasks', description: 'List user tasks filtered by status', parameters: {} } } ], stream: false, stream_options: {include_usage: false}, temperature: 0, max_tokens: 0, top_p: 0, seed: 0, stop: ['string'], max_history_tokens: 0, system_prompt_cache_contract: 'legacy', prompt_cache_key: 'string', context_management: [{type: 'compaction', compact_threshold: 0, keep_tokens: 0, model: 'string'}], thread_id: 'string', parent_message_id: 'string', store: true, persona_id: 'string', metadata: {property1: 'string', property2: 'string'}, skill_instructions: 'string', skill_selection: 'off', skills: ['summarize-thread'], skill_files: false, memory: {read: true, write: true}, tool_callback: { base_url: 'https://api.blinklearn.envision.io/agent-tools', auth_token: 'string', timeout_ms: 8000 }, extra_context: {}, skill_id: 'string', knowledge_pack_ids: ['string'], brain_id: '5ff5069c-3661-4a30-92df-9e098e7ad88f', brain_ids: ['497f6eca-6276-4993-bfeb-53cbbbba6f08'], mind_ids: ['497f6eca-6276-4993-bfeb-53cbbbba6f08'], graph_ids: ['string'] }) }; fetch(url, options) .then(res => res.json()) .then(json => console.log(json)) .catch(err => console.error(err)); ``` ### Python ```python import requests url = "https://truememory-api.mvstg.com/v1/chat/completions" payload = { "model": "string", "messages": [ { "role": "system", "content": "string", "tool_calls": [{}], "tool_call_id": "string", "name": "string", "cache_control": {} } ], "tools": [ { "type": "function", "function": { "name": "list_tasks", "description": "List user tasks filtered by status", "parameters": {} } } ], "stream": False, "stream_options": { "include_usage": False }, "temperature": 0, "max_tokens": 0, "top_p": 0, "seed": 0, "stop": ["string"], "max_history_tokens": 0, "system_prompt_cache_contract": "legacy", "prompt_cache_key": "string", "context_management": [ { "type": "compaction", "compact_threshold": 0, "keep_tokens": 0, "model": "string" } ], "thread_id": "string", "parent_message_id": "string", "store": True, "persona_id": "string", "metadata": { "property1": "string", "property2": "string" }, "skill_instructions": "string", "skill_selection": "off", "skills": ["summarize-thread"], "skill_files": False, "memory": { "read": True, "write": True }, "tool_callback": { "base_url": "https://api.blinklearn.envision.io/agent-tools", "auth_token": "string", "timeout_ms": 8000 }, "extra_context": {}, "skill_id": "string", "knowledge_pack_ids": ["string"], "brain_id": "5ff5069c-3661-4a30-92df-9e098e7ad88f", "brain_ids": ["497f6eca-6276-4993-bfeb-53cbbbba6f08"], "mind_ids": ["497f6eca-6276-4993-bfeb-53cbbbba6f08"], "graph_ids": ["string"] } headers = { "Content-Type": "application/json", "Authorization": "Bearer " } response = requests.post(url, json=payload, headers=headers) print(response.text) ``` ## Responses ### 200 A chat completion. JSON when `stream` is false; an SSE stream of chunks when it is true. #### Example ```json { "id": "string", "object": "chat.completion", "created": 0, "model": "string", "choices": [ { "index": 0, "message": { "role": "assistant", "content": "string", "tool_calls": [ {} ] }, "finish_reason": "stop" } ], "usage": {}, "metadata": { "property1": "string", "property2": "string" }, "context_management": [ { "type": "compaction", "status": "completed" } ], "message_id": "string" } ``` - `id` (`chat_completions.response.200.id`; string, required) - `created` (`chat_completions.response.200.created`; integer, required) - `model` (`chat_completions.response.200.model`; string, required) - `choices` (`chat_completions.response.200.choices`; array, required) - `message` (`chat_completions.response.200.choices.message`; object, required) - `role` (`chat_completions.response.200.choices.message.role`; string, optional) - default `"assistant"` - `content` (`chat_completions.response.200.choices.message.content`; any of, optional) - any of: `string`, `null` - `tool_calls` (`chat_completions.response.200.choices.message.tool_calls`; any of, optional) - any of: `array`, `null` - `index` (`chat_completions.response.200.choices.index`; integer, optional) - default `0` - `finish_reason` (`chat_completions.response.200.choices.finish_reason`; any of, optional) - default `"stop"` - any of: `string`, `null` - `object` (`chat_completions.response.200.object`; string, optional) - default `"chat.completion"` - `usage` (`chat_completions.response.200.usage`; object, optional) - `metadata` (`chat_completions.response.200.metadata`; any of, optional) - any of: `map`, `null` - `context_management` (`chat_completions.response.200.context_management`; any of, optional) - any of: `array`, `null` - `message_id` (`chat_completions.response.200.message_id`; any of, optional) — ID of the last message stored on the thread by this turn. Send it as `parent_message_id` on the next request. Streaming responses carry it as a top-level `message_id` on the chunk with a non-error `finish_reason`. Only set for requests with a `thread_id`. - any of: `string`, `null` ### 422 Validation Error #### Example ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string", "input": null, "ctx": {} } ] } ``` - `detail` (`chat_completions.response.422.detail`; array, optional) - `loc` (`chat_completions.response.422.detail.loc`; array, required) - any of: `string`, `integer` - `msg` (`chat_completions.response.422.detail.msg`; string, required) - `type` (`chat_completions.response.422.detail.type`; string, required) - `input` (`chat_completions.response.422.detail.input`; unknown, optional) - `ctx` (`chat_completions.response.422.detail.ctx`; object, optional) # Warm the user's memory cache > Prefetch the user's memory into cache so the next chat turn skips the lookup. **Deprecated** — use ``POST /memories/graph/{user_id}/warm``, which does the same work and is scoped to the user rather than an agent. Source: https://truememory-docs.mvstg.com/api/context/create/ · Markdown: https://truememory-docs.mvstg.com/api/context/create/index.md > **Deprecated.** Path: AI Gateway API › Memory `POST /context/{agent_id}` Prefetch the user's memory into cache so the next chat turn skips the lookup. **Deprecated** — use ``POST /memories/graph/{user_id}/warm``, which does the same work and is scoped to the user rather than an agent. ## Authentication - `HTTPBearer`, http, header `Authorization` ## Path parameters - `agent_id` (`prefetch_context.path.agent_id`; string, required) ## Header parameters - `X-On-Behalf-Of` (`prefetch_context.header.X-On-Behalf-Of`; any of, optional) - any of: `string`, `null` - `X-External-User-Id` (`prefetch_context.header.X-External-User-Id`; any of, optional) - any of: `string`, `null` - `X-App-Id` (`prefetch_context.header.X-App-Id`; any of, optional) - any of: `string`, `null` ## Code samples ### cURL ```bash curl --request POST \ --url 'https://truememory-api.mvstg.com/context/' \ --header 'Authorization: Bearer ' ``` ### TypeScript ```typescript const url = 'https://truememory-api.mvstg.com/context/'; const options = {method: 'POST', headers: {Authorization: 'Bearer '}}; fetch(url, options) .then(res => res.json()) .then(json => console.log(json)) .catch(err => console.error(err)); ``` ### Python ```python import requests url = "https://truememory-api.mvstg.com/context/" headers = {"Authorization": "Bearer "} response = requests.post(url, headers=headers) print(response.text) ``` ## Responses ### 200 Successful Response #### Example ```json { "entities": [ { "name": "string", "summary": "string" } ], "facts": [ "string" ] } ``` - `entities` (`prefetch_context.response.200.entities`; array, required) — Up to 15 entities. - `name` (`prefetch_context.response.200.entities.name`; string, required) - `summary` (`prefetch_context.response.200.entities.summary`; string, required) - `facts` (`prefetch_context.response.200.facts`; array, required) — Up to 15 facts. ### 422 Validation Error #### Example ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string", "input": null, "ctx": {} } ] } ``` - `detail` (`prefetch_context.response.422.detail`; array, optional) - `loc` (`prefetch_context.response.422.detail.loc`; array, required) - any of: `string`, `integer` - `msg` (`prefetch_context.response.422.detail.msg`; string, required) - `type` (`prefetch_context.response.422.detail.type`; string, required) - `input` (`prefetch_context.response.422.detail.input`; unknown, optional) - `ctx` (`prefetch_context.response.422.detail.ctx`; object, optional) # Record run feedback > Record feedback for a run to LangSmith. Requires an authenticated caller. This is a simple wrapper for the LangSmith create_feedback API, so the credentials can be stored and managed in the service rather than the client. See: https://api.smith.langchain.com/redoc#tag/feedback/operation/create_feedback_api_v1_feedback_post Source: https://truememory-docs.mvstg.com/api/feedback/create/ · Markdown: https://truememory-docs.mvstg.com/api/feedback/create/index.md Path: AI Gateway API › History & Feedback `POST /feedback` Record feedback for a run to LangSmith. Requires an authenticated caller. This is a simple wrapper for the LangSmith create_feedback API, so the credentials can be stored and managed in the service rather than the client. See: https://api.smith.langchain.com/redoc#tag/feedback/operation/create_feedback_api_v1_feedback_post ## Authentication - `HTTPBearer`, http, header `Authorization` ## Header parameters - `X-On-Behalf-Of` (`feedback.header.X-On-Behalf-Of`; any of, optional) - any of: `string`, `null` - `X-External-User-Id` (`feedback.header.X-External-User-Id`; any of, optional) - any of: `string`, `null` - `X-App-Id` (`feedback.header.X-App-Id`; any of, optional) - any of: `string`, `null` ## Request body _Required._ - `run_id` (`feedback.run_id`; string, required) — Run ID to record feedback for. - `key` (`feedback.key`; string, required) — Feedback key. - `score` (`feedback.score`; number, required) — Feedback score. - `kwargs` (`feedback.kwargs`; object, optional) — Additional feedback kwargs, passed to LangSmith. - default `{}` ## Example request ```json { "run_id": "847c6285-8fc9-4560-a83f-4e6285809254", "key": "human-feedback-stars", "score": 0.8, "kwargs": { "comment": "In-line human feedback" } } ``` ## Code samples ### cURL ```bash curl --request POST \ --url https://truememory-api.mvstg.com/feedback \ --header 'Authorization: Bearer ' \ --header 'Content-Type: application/json' \ --data ' { "run_id": "847c6285-8fc9-4560-a83f-4e6285809254", "key": "human-feedback-stars", "score": 0.8, "kwargs": { "comment": "In-line human feedback" } } ' ``` ### TypeScript ```typescript const url = 'https://truememory-api.mvstg.com/feedback'; const options = { method: 'POST', headers: {'Content-Type': 'application/json', Authorization: 'Bearer '}, body: JSON.stringify({ run_id: '847c6285-8fc9-4560-a83f-4e6285809254', key: 'human-feedback-stars', score: 0.8, kwargs: {comment: 'In-line human feedback'} }) }; fetch(url, options) .then(res => res.json()) .then(json => console.log(json)) .catch(err => console.error(err)); ``` ### Python ```python import requests url = "https://truememory-api.mvstg.com/feedback" payload = { "run_id": "847c6285-8fc9-4560-a83f-4e6285809254", "key": "human-feedback-stars", "score": 0.8, "kwargs": { "comment": "In-line human feedback" } } headers = { "Content-Type": "application/json", "Authorization": "Bearer " } response = requests.post(url, json=payload, headers=headers) print(response.text) ``` ## Responses ### 200 Successful Response #### Example ```json { "status": "success" } ``` - `status` (`feedback.response.200.status`; string, optional) - default `"success"` ### 422 Validation Error #### Example ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string", "input": null, "ctx": {} } ] } ``` - `detail` (`feedback.response.422.detail`; array, optional) - `loc` (`feedback.response.422.detail.loc`; array, required) - any of: `string`, `integer` - `msg` (`feedback.response.422.detail.msg`; string, required) - `type` (`feedback.response.422.detail.type`; string, required) - `input` (`feedback.response.422.detail.input`; unknown, optional) - `ctx` (`feedback.response.422.detail.ctx`; object, optional) # Get chat history > Get chat history. Each returned message carries a ``created_at`` derived from the LangGraph checkpoint that first contained it — clients use this for stable display timestamps instead of stamping fetch time. Falls back to ``None`` when the originating checkpoint cannot be resolved (very rare; treat as unknown on the client and render a fallback there). Source: https://truememory-docs.mvstg.com/api/history/create/ · Markdown: https://truememory-docs.mvstg.com/api/history/create/index.md Path: AI Gateway API › History & Feedback `POST /history` Get chat history. Each returned message carries a ``created_at`` derived from the LangGraph checkpoint that first contained it — clients use this for stable display timestamps instead of stamping fetch time. Falls back to ``None`` when the originating checkpoint cannot be resolved (very rare; treat as unknown on the client and render a fallback there). ## Authentication - `HTTPBearer`, http, header `Authorization` ## Header parameters - `x-app-id` (`history.header.x-app-id`; any of, optional) — Application ID for scoping threads - any of: `string`, `null` - `X-On-Behalf-Of` (`history.header.X-On-Behalf-Of`; any of, optional) - any of: `string`, `null` - `X-External-User-Id` (`history.header.X-External-User-Id`; any of, optional) - any of: `string`, `null` - `X-App-Id` (`history.header.X-App-Id`; any of, optional) - any of: `string`, `null` ## Request body _Required._ - `thread_id` (`history.thread_id`; string, required) — Thread ID to persist and continue a multi-turn conversation. ## Example request ```json { "thread_id": "847c6285-8fc9-4560-a83f-4e6285809254" } ``` ## Code samples ### cURL ```bash curl --request POST \ --url https://truememory-api.mvstg.com/history \ --header 'Authorization: Bearer ' \ --header 'Content-Type: application/json' \ --data ' { "thread_id": "847c6285-8fc9-4560-a83f-4e6285809254" } ' ``` ### TypeScript ```typescript const url = 'https://truememory-api.mvstg.com/history'; const options = { method: 'POST', headers: {'Content-Type': 'application/json', Authorization: 'Bearer '}, body: JSON.stringify({thread_id: '847c6285-8fc9-4560-a83f-4e6285809254'}) }; fetch(url, options) .then(res => res.json()) .then(json => console.log(json)) .catch(err => console.error(err)); ``` ### Python ```python import requests url = "https://truememory-api.mvstg.com/history" payload = { "thread_id": "847c6285-8fc9-4560-a83f-4e6285809254" } headers = { "Content-Type": "application/json", "Authorization": "Bearer " } response = requests.post(url, json=payload, headers=headers) print(response.text) ``` ## Responses ### 200 Successful Response #### Example ```json { "messages": [ { "type": "human", "content": "Hello, world!", "tool_calls": [], "tool_call_id": "call_Jja7J89XsjrOLA5r!MEOW!SL", "run_id": "847c6285-8fc9-4560-a83f-4e6285809254", "response_metadata": {}, "custom_data": {}, "metadata": { "property1": "string", "property2": "string" }, "created_at": "2026-05-06T12:34:56.789Z" } ] } ``` - `messages` (`history.response.200.messages`; array, required) - `type` (`history.response.200.messages.type`; string, required) — Role of the message. - one of `"human"`, `"ai"`, `"system"`, `"tool"`, `"custom"` - `content` (`history.response.200.messages.content`; string, required) — Content of the message. - `tool_calls` (`history.response.200.messages.tool_calls`; array, optional) — Tool calls in the message. - default `[]` - `id` (`history.response.200.messages.tool_calls.id`; string, required) - `type` (`history.response.200.messages.tool_calls.type`; string, required) - `function` (`history.response.200.messages.tool_calls.function`; object, required) — Function call details (OpenAI chat completions format). - `name` (`history.response.200.messages.tool_calls.function.name`; string, required) - `arguments` (`history.response.200.messages.tool_calls.function.arguments`; string, required) - `tool_call_id` (`history.response.200.messages.tool_call_id`; any of, optional) — Tool call that this message is responding to. - any of: `string`, `null` - `run_id` (`history.response.200.messages.run_id`; any of, optional) — Run ID of the message. - any of: `string`, `null` - `response_metadata` (`history.response.200.messages.response_metadata`; object, optional) — Response metadata. For example: response headers, logprobs, token counts. - default `{}` - `custom_data` (`history.response.200.messages.custom_data`; object, optional) — Custom message data. - default `{}` - `metadata` (`history.response.200.messages.metadata`; any of, optional) — Up to 16 key-value pairs attached to the message. Keys max 64 chars, values max 512 chars. - any of: `map`, `null` - `created_at` (`history.response.200.messages.created_at`; any of, optional) — UTC timestamp of when the message was first persisted to the thread, derived from the LangGraph checkpoint that first contained this message. Null for messages whose origin checkpoint is not available. - any of: `string`, `null` ### 422 Validation Error #### Example ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string", "input": null, "ctx": {} } ] } ``` - `detail` (`history.response.422.detail`; array, optional) - `loc` (`history.response.422.detail.loc`; array, required) - any of: `string`, `integer` - `msg` (`history.response.422.detail.msg`; string, required) - `type` (`history.response.422.detail.type`; string, required) - `input` (`history.response.422.detail.input`; unknown, optional) - `ctx` (`history.response.422.detail.ctx`; object, optional) # Generate an image > Generate images from text. Drop-in replacement for OpenAI /v1/images/generations. Supported models: openai/gpt-image-1.5, openai/dall-e-3, google/gemini-3.1-flash-image, google/gemini-3.1-flash-lite-image, google/gemini-2.5-flash-image, google/gemini-3-pro-image. Source: https://truememory-docs.mvstg.com/api/images/generations/ · Markdown: https://truememory-docs.mvstg.com/api/images/generations/index.md Path: AI Gateway API › Chat `POST /v1/images/generations` Generate images from text. Drop-in replacement for OpenAI /v1/images/generations. Supported models: openai/gpt-image-1.5, openai/dall-e-3, google/gemini-3.1-flash-image, google/gemini-3.1-flash-lite-image, google/gemini-2.5-flash-image, google/gemini-3-pro-image. ## Authentication - `HTTPBearer`, http, header `Authorization` ## Header parameters - `X-On-Behalf-Of` (`create_image.header.X-On-Behalf-Of`; any of, optional) - any of: `string`, `null` - `X-External-User-Id` (`create_image.header.X-External-User-Id`; any of, optional) - any of: `string`, `null` - `X-App-Id` (`create_image.header.X-App-Id`; any of, optional) - any of: `string`, `null` ## Request body _Required._ - `model` (`create_image.model`; string, required) — Model to use, e.g. 'openai/gpt-image-1.5' or 'google/gemini-3.1-flash-image' - `prompt` (`create_image.prompt`; string, required) — Text description of the desired image - `n` (`create_image.n`; integer, optional) - min 1; max 10; default `1` - `size` (`create_image.size`; any of, optional) — Image size, e.g. '1024x1024' - any of: `string`, `null` - `quality` (`create_image.quality`; any of, optional) — Image quality, e.g. 'auto', 'hd' - any of: `string`, `null` - `response_format` (`create_image.response_format`; string, optional) — Response format. Google models always return b64_json. - one of `"url"`, `"b64_json"`; default `"b64_json"` - `style` (`create_image.style`; any of, optional) — Image style (dall-e-3 only) - any of: `string`, `null` ## Example request ```json { "model": "string", "prompt": "string", "n": 1, "size": "string", "quality": "string", "response_format": "url", "style": "string" } ``` ## Code samples ### cURL ```bash curl --request POST \ --url https://truememory-api.mvstg.com/v1/images/generations \ --header 'Authorization: Bearer ' \ --header 'Content-Type: application/json' \ --data ' { "model": "string", "prompt": "string", "n": 1, "size": "string", "quality": "string", "response_format": "url", "style": "string" } ' ``` ### TypeScript ```typescript const url = 'https://truememory-api.mvstg.com/v1/images/generations'; const options = { method: 'POST', headers: {'Content-Type': 'application/json', Authorization: 'Bearer '}, body: JSON.stringify({ model: 'string', prompt: 'string', n: 1, size: 'string', quality: 'string', response_format: 'url', style: 'string' }) }; fetch(url, options) .then(res => res.json()) .then(json => console.log(json)) .catch(err => console.error(err)); ``` ### Python ```python import requests url = "https://truememory-api.mvstg.com/v1/images/generations" payload = { "model": "string", "prompt": "string", "n": 1, "size": "string", "quality": "string", "response_format": "url", "style": "string" } headers = { "Content-Type": "application/json", "Authorization": "Bearer " } response = requests.post(url, json=payload, headers=headers) print(response.text) ``` ## Responses ### 200 Successful Response #### Example ```json { "created": 0, "data": [ { "b64_json": "string", "url": "string", "revised_prompt": "string" } ] } ``` - `created` (`create_image.response.200.created`; integer, required) - `data` (`create_image.response.200.data`; array, required) - `b64_json` (`create_image.response.200.data.b64_json`; any of, optional) - any of: `string`, `null` - `url` (`create_image.response.200.data.url`; any of, optional) - any of: `string`, `null` - `revised_prompt` (`create_image.response.200.data.revised_prompt`; any of, optional) - any of: `string`, `null` ### 422 Validation Error #### Example ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string", "input": null, "ctx": {} } ] } ``` - `detail` (`create_image.response.422.detail`; array, optional) - `loc` (`create_image.response.422.detail.loc`; array, required) - any of: `string`, `integer` - `msg` (`create_image.response.422.detail.msg`; string, required) - `type` (`create_image.response.422.detail.type`; string, required) - `input` (`create_image.response.422.detail.input`; unknown, optional) - `ctx` (`create_image.response.422.detail.ctx`; object, optional) # Get service metadata > List what this deployment can serve: the available agents, the models it accepts, and which agent and model it falls back to when a request names neither. Unauthenticated — use it to discover valid `agent` and `model` values before making a chat request. Source: https://truememory-docs.mvstg.com/api/info/list/ · Markdown: https://truememory-docs.mvstg.com/api/info/list/index.md Path: AI Gateway API › System `GET /info` List what this deployment can serve: the available agents, the models it accepts, and which agent and model it falls back to when a request names neither. Unauthenticated — use it to discover valid `agent` and `model` values before making a chat request. ## Code samples ### cURL ```bash curl --request GET \ --url https://truememory-api.mvstg.com/info ``` ### TypeScript ```typescript const url = 'https://truememory-api.mvstg.com/info'; const options = {method: 'GET'}; fetch(url, options) .then(res => res.json()) .then(json => console.log(json)) .catch(err => console.error(err)); ``` ### Python ```python import requests url = "https://truememory-api.mvstg.com/info" response = requests.get(url) print(response.text) ``` ## Responses ### 200 Successful Response #### Example ```json { "agents": [ { "key": "research-assistant", "description": "A research assistant for generating research papers.", "memory_enabled": false, "memory_scope": "agent" } ], "models": [ "gpt-5-nano" ], "default_agent": "research-assistant", "default_model": "gpt-5-nano" } ``` - `agents` (`info.response.200.agents`; array, required) — List of available agents. - `key` (`info.response.200.agents.key`; string, required) — Agent key. - `description` (`info.response.200.agents.description`; string, required) — Description of the agent. - `memory_enabled` (`info.response.200.agents.memory_enabled`; boolean, optional) — Whether this agent has persistent memory enabled. - default `false` - `memory_scope` (`info.response.200.agents.memory_scope`; any of, optional) — Memory scope: 'agent' (isolated) or 'shared' (cross-agent). None if memory disabled. - any of: `string`, `null` - `models` (`info.response.200.models`; array, required) — List of available LLMs. - any of: `OpenAIModelName`, `OpenAICompatibleName`, `AzureOpenAIModelName`, `DeepseekModelName`, `AnthropicModelName`, `GoogleModelName`, `VertexAIModelName`, `GroqModelName`, `AWSModelName`, `OllamaModelName`, `OpenRouterModelName`, `ZaiModelName`, `FakeModelName` - `default_agent` (`info.response.200.default_agent`; string, required) — Default agent used when none is specified. - `default_model` (`info.response.200.default_model`; any of, required) — Default model used when none is specified. - any of: `OpenAIModelName`, `OpenAICompatibleName`, `AzureOpenAIModelName`, `DeepseekModelName`, `AnthropicModelName`, `GoogleModelName`, `VertexAIModelName`, `GroqModelName`, `AWSModelName`, `OllamaModelName`, `OpenRouterModelName`, `ZaiModelName`, `FakeModelName` # Upload a knowledge pack > Upload a PDF, Markdown or plain-text file to be indexed for retrieval. The response returns immediately with the pack in a pending state — extraction and embedding run in the background, so poll the pack until its status settles before expecting it in retrieval results. Source: https://truememory-docs.mvstg.com/api/knowledge-packs/create/ · Markdown: https://truememory-docs.mvstg.com/api/knowledge-packs/create/index.md Path: AI Gateway API › Knowledge Packs `POST /knowledge-packs` Upload a PDF, Markdown or plain-text file to be indexed for retrieval. The response returns immediately with the pack in a pending state — extraction and embedding run in the background, so poll the pack until its status settles before expecting it in retrieval results. ## Authentication - `HTTPBearer`, http, header `Authorization` ## Query parameters - `skill_id` (`upload_knowledge_pack.query.skill_id`; any of, optional) - any of: `string`, `null` - `app_id` (`upload_knowledge_pack.query.app_id`; any of, optional) - any of: `string`, `null` - `visibility` (`upload_knowledge_pack.query.visibility`; string, optional) - one of `"private"`, `"public"`; default `"private"` ## Header parameters - `X-On-Behalf-Of` (`upload_knowledge_pack.header.X-On-Behalf-Of`; any of, optional) - any of: `string`, `null` - `X-External-User-Id` (`upload_knowledge_pack.header.X-External-User-Id`; any of, optional) - any of: `string`, `null` - `X-App-Id` (`upload_knowledge_pack.header.X-App-Id`; any of, optional) - any of: `string`, `null` ## Request body _Required._ - `file` (`upload_knowledge_pack.file`; string, required) ## Example request ```text { "file": "string" } ``` ## Code samples ### cURL ```bash curl --request POST \ --url https://truememory-api.mvstg.com/knowledge-packs \ --header 'Authorization: Bearer ' \ --form-string 'file=string' ``` ### TypeScript ```typescript const formData = new FormData(); formData.append('file', 'string'); const url = 'https://truememory-api.mvstg.com/knowledge-packs'; const options = {method: 'POST', headers: {Authorization: 'Bearer '}, body: formData}; fetch(url, options) .then(res => res.json()) .then(json => console.log(json)) .catch(err => console.error(err)); ``` ### Python ```python import requests url = "https://truememory-api.mvstg.com/knowledge-packs" files = [ ("file", (None, "string")) ] headers = {"Authorization": "Bearer "} response = requests.post(url, files=files, headers=headers) print(response.text) ``` ## Responses ### 201 Successful Response #### Example ```json { "id": "string", "user_id": "string", "app_id": "string", "skill_id": "string", "filename": "string", "content_type": "string", "status": "pending", "visibility": "private", "created_at": "2019-08-24T14:15:22Z" } ``` - `id` (`upload_knowledge_pack.response.201.id`; string, required) - `user_id` (`upload_knowledge_pack.response.201.user_id`; any of, required) - any of: `string`, `null` - `app_id` (`upload_knowledge_pack.response.201.app_id`; any of, required) - any of: `string`, `null` - `skill_id` (`upload_knowledge_pack.response.201.skill_id`; any of, required) - any of: `string`, `null` - `filename` (`upload_knowledge_pack.response.201.filename`; string, required) - `content_type` (`upload_knowledge_pack.response.201.content_type`; string, required) - `status` (`upload_knowledge_pack.response.201.status`; string, required) - one of `"pending"`, `"processing"`, `"ready"`, `"error"` - `created_at` (`upload_knowledge_pack.response.201.created_at`; string, required) - format `date-time` - `visibility` (`upload_knowledge_pack.response.201.visibility`; string, optional) - one of `"private"`, `"app"`, `"public"`; default `"private"` ### 422 Validation Error #### Example ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string", "input": null, "ctx": {} } ] } ``` - `detail` (`upload_knowledge_pack.response.422.detail`; array, optional) - `loc` (`upload_knowledge_pack.response.422.detail.loc`; array, required) - any of: `string`, `integer` - `msg` (`upload_knowledge_pack.response.422.detail.msg`; string, required) - `type` (`upload_knowledge_pack.response.422.detail.type`; string, required) - `input` (`upload_knowledge_pack.response.422.detail.input`; unknown, optional) - `ctx` (`upload_knowledge_pack.response.422.detail.ctx`; object, optional) # Delete a knowledge pack > Delete a pack you own, along with its indexed content. Returns 204 with no body; deleting a pack you do not own returns 404. Source: https://truememory-docs.mvstg.com/api/knowledge-packs/delete/ · Markdown: https://truememory-docs.mvstg.com/api/knowledge-packs/delete/index.md Path: AI Gateway API › Knowledge Packs `DELETE /knowledge-packs/{pack_id}` Delete a pack you own, along with its indexed content. Returns 204 with no body; deleting a pack you do not own returns 404. ## Authentication - `HTTPBearer`, http, header `Authorization` ## Path parameters - `pack_id` (`delete_knowledge_pack.path.pack_id`; string, required) - pattern `^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$` ## Header parameters - `X-On-Behalf-Of` (`delete_knowledge_pack.header.X-On-Behalf-Of`; any of, optional) - any of: `string`, `null` - `X-External-User-Id` (`delete_knowledge_pack.header.X-External-User-Id`; any of, optional) - any of: `string`, `null` - `X-App-Id` (`delete_knowledge_pack.header.X-App-Id`; any of, optional) - any of: `string`, `null` ## Code samples ### cURL ```bash curl --request DELETE \ --url 'https://truememory-api.mvstg.com/knowledge-packs/' \ --header 'Authorization: Bearer ' ``` ### TypeScript ```typescript const url = 'https://truememory-api.mvstg.com/knowledge-packs/'; const options = {method: 'DELETE', headers: {Authorization: 'Bearer '}}; fetch(url, options) .then(res => res.json()) .then(json => console.log(json)) .catch(err => console.error(err)); ``` ### Python ```python import requests url = "https://truememory-api.mvstg.com/knowledge-packs/" headers = {"Authorization": "Bearer "} response = requests.delete(url, headers=headers) print(response.text) ``` ## Responses ### 204 Successful Response ### 422 Validation Error #### Example ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string", "input": null, "ctx": {} } ] } ``` - `detail` (`delete_knowledge_pack.response.422.detail`; array, optional) - `loc` (`delete_knowledge_pack.response.422.detail.loc`; array, required) - any of: `string`, `integer` - `msg` (`delete_knowledge_pack.response.422.detail.msg`; string, required) - `type` (`delete_knowledge_pack.response.422.detail.type`; string, required) - `input` (`delete_knowledge_pack.response.422.detail.input`; unknown, optional) - `ctx` (`delete_knowledge_pack.response.422.detail.ctx`; object, optional) # List your knowledge packs > List the packs you own, plus any public packs visible to the calling app. Filter by `skill_id` or by ingestion `status`. Source: https://truememory-docs.mvstg.com/api/knowledge-packs/list/ · Markdown: https://truememory-docs.mvstg.com/api/knowledge-packs/list/index.md Path: AI Gateway API › Knowledge Packs `GET /knowledge-packs` List the packs you own, plus any public packs visible to the calling app. Filter by `skill_id` or by ingestion `status`. ## Authentication - `HTTPBearer`, http, header `Authorization` ## Query parameters - `skill_id` (`list_knowledge_packs.query.skill_id`; any of, optional) - any of: `string`, `null` - `status` (`list_knowledge_packs.query.status`; any of, optional) - any of: `string`, `null` - `limit` (`list_knowledge_packs.query.limit`; integer, optional) - max 200; default `50` - `offset` (`list_knowledge_packs.query.offset`; integer, optional) - default `0` ## Header parameters - `X-App-Id` (`list_knowledge_packs.header.X-App-Id`; any of, optional) - any of: `string`, `null` - `X-On-Behalf-Of` (`list_knowledge_packs.header.X-On-Behalf-Of`; any of, optional) - any of: `string`, `null` - `X-External-User-Id` (`list_knowledge_packs.header.X-External-User-Id`; any of, optional) - any of: `string`, `null` ## Code samples ### cURL ```bash curl --request GET \ --url https://truememory-api.mvstg.com/knowledge-packs \ --header 'Authorization: Bearer ' ``` ### TypeScript ```typescript const url = 'https://truememory-api.mvstg.com/knowledge-packs'; const options = {method: 'GET', headers: {Authorization: 'Bearer '}}; fetch(url, options) .then(res => res.json()) .then(json => console.log(json)) .catch(err => console.error(err)); ``` ### Python ```python import requests url = "https://truememory-api.mvstg.com/knowledge-packs" headers = {"Authorization": "Bearer "} response = requests.get(url, headers=headers) print(response.text) ``` ## Responses ### 200 Successful Response #### Example ```json { "packs": [ { "id": "string", "user_id": "string", "app_id": "string", "skill_id": "string", "filename": "string", "content_type": "string", "status": "pending", "visibility": "private", "created_at": "2019-08-24T14:15:22Z" } ], "total": 0 } ``` - `packs` (`list_knowledge_packs.response.200.packs`; array, required) - `id` (`list_knowledge_packs.response.200.packs.id`; string, required) - `user_id` (`list_knowledge_packs.response.200.packs.user_id`; any of, required) - any of: `string`, `null` - `app_id` (`list_knowledge_packs.response.200.packs.app_id`; any of, required) - any of: `string`, `null` - `skill_id` (`list_knowledge_packs.response.200.packs.skill_id`; any of, required) - any of: `string`, `null` - `filename` (`list_knowledge_packs.response.200.packs.filename`; string, required) - `content_type` (`list_knowledge_packs.response.200.packs.content_type`; string, required) - `status` (`list_knowledge_packs.response.200.packs.status`; string, required) - one of `"pending"`, `"processing"`, `"ready"`, `"error"` - `created_at` (`list_knowledge_packs.response.200.packs.created_at`; string, required) - format `date-time` - `visibility` (`list_knowledge_packs.response.200.packs.visibility`; string, optional) - one of `"private"`, `"app"`, `"public"`; default `"private"` - `total` (`list_knowledge_packs.response.200.total`; integer, required) — Total rows matching the filters before LIMIT/OFFSET. ### 422 Validation Error #### Example ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string", "input": null, "ctx": {} } ] } ``` - `detail` (`list_knowledge_packs.response.422.detail`; array, optional) - `loc` (`list_knowledge_packs.response.422.detail.loc`; array, required) - any of: `string`, `integer` - `msg` (`list_knowledge_packs.response.422.detail.msg`; string, required) - `type` (`list_knowledge_packs.response.422.detail.type`; string, required) - `input` (`list_knowledge_packs.response.422.detail.input`; unknown, optional) - `ctx` (`list_knowledge_packs.response.422.detail.ctx`; object, optional) # Retry a failed knowledge pack > Re-run ingestion of a pack you uploaded whose ingest failed. Only a pack in `error` can be retried (409 otherwise); its stored original is ingested again, so nothing is uploaded. A pack you don't own returns 404. Source: https://truememory-docs.mvstg.com/api/knowledge-packs/retry/ · Markdown: https://truememory-docs.mvstg.com/api/knowledge-packs/retry/index.md Path: AI Gateway API › Knowledge Packs `POST /knowledge-packs/{pack_id}/retry` Re-run ingestion of a pack you uploaded whose ingest failed. Only a pack in `error` can be retried (409 otherwise); its stored original is ingested again, so nothing is uploaded. A pack you don't own returns 404. ## Authentication - `HTTPBearer`, http, header `Authorization` ## Path parameters - `pack_id` (`retry_knowledge_pack_ingest.path.pack_id`; string, required) - pattern `^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$` ## Header parameters - `X-On-Behalf-Of` (`retry_knowledge_pack_ingest.header.X-On-Behalf-Of`; any of, optional) - any of: `string`, `null` - `X-External-User-Id` (`retry_knowledge_pack_ingest.header.X-External-User-Id`; any of, optional) - any of: `string`, `null` - `X-App-Id` (`retry_knowledge_pack_ingest.header.X-App-Id`; any of, optional) - any of: `string`, `null` ## Code samples ### cURL ```bash curl --request POST \ --url 'https://truememory-api.mvstg.com/knowledge-packs//retry' \ --header 'Authorization: Bearer ' ``` ### TypeScript ```typescript const url = 'https://truememory-api.mvstg.com/knowledge-packs//retry'; const options = {method: 'POST', headers: {Authorization: 'Bearer '}}; fetch(url, options) .then(res => res.json()) .then(json => console.log(json)) .catch(err => console.error(err)); ``` ### Python ```python import requests url = "https://truememory-api.mvstg.com/knowledge-packs//retry" headers = {"Authorization": "Bearer "} response = requests.post(url, headers=headers) print(response.text) ``` ## Responses ### 200 Successful Response #### Example ```json { "id": "string", "user_id": "string", "app_id": "string", "skill_id": "string", "filename": "string", "content_type": "string", "status": "pending", "visibility": "private", "created_at": "2019-08-24T14:15:22Z" } ``` - `id` (`retry_knowledge_pack_ingest.response.200.id`; string, required) - `user_id` (`retry_knowledge_pack_ingest.response.200.user_id`; any of, required) - any of: `string`, `null` - `app_id` (`retry_knowledge_pack_ingest.response.200.app_id`; any of, required) - any of: `string`, `null` - `skill_id` (`retry_knowledge_pack_ingest.response.200.skill_id`; any of, required) - any of: `string`, `null` - `filename` (`retry_knowledge_pack_ingest.response.200.filename`; string, required) - `content_type` (`retry_knowledge_pack_ingest.response.200.content_type`; string, required) - `status` (`retry_knowledge_pack_ingest.response.200.status`; string, required) - one of `"pending"`, `"processing"`, `"ready"`, `"error"` - `created_at` (`retry_knowledge_pack_ingest.response.200.created_at`; string, required) - format `date-time` - `visibility` (`retry_knowledge_pack_ingest.response.200.visibility`; string, optional) - one of `"private"`, `"app"`, `"public"`; default `"private"` ### 422 Validation Error #### Example ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string", "input": null, "ctx": {} } ] } ``` - `detail` (`retry_knowledge_pack_ingest.response.422.detail`; array, optional) - `loc` (`retry_knowledge_pack_ingest.response.422.detail.loc`; array, required) - any of: `string`, `integer` - `msg` (`retry_knowledge_pack_ingest.response.422.detail.msg`; string, required) - `type` (`retry_knowledge_pack_ingest.response.422.detail.type`; string, required) - `input` (`retry_knowledge_pack_ingest.response.422.detail.input`; unknown, optional) - `ctx` (`retry_knowledge_pack_ingest.response.422.detail.ctx`; object, optional) # Clear all memories for a user+agent pair or a shared namespace > Permanently delete all memory data for one scope. User-initiated deletion is a HARD delete: edges, entity nodes, and episodic content are removed from both the graph database and the search overlay — nothing is retained. (System-side fact invalidation remains a soft ``expired_at``; that history powers contradiction resolution and is not user data retention.) The user path also invalidates the context cache for the pair. ``graph_id`` empties a shared namespace instead, service tokens only; the response ``scope`` field names what was cleared. Source: https://truememory-docs.mvstg.com/api/memories/clear/ · Markdown: https://truememory-docs.mvstg.com/api/memories/clear/index.md Path: AI Gateway API › Memory `POST /memories/{agent_id}/clear` Permanently delete all memory data for one scope. User-initiated deletion is a HARD delete: edges, entity nodes, and episodic content are removed from both the graph database and the search overlay — nothing is retained. (System-side fact invalidation remains a soft ``expired_at``; that history powers contradiction resolution and is not user data retention.) The user path also invalidates the context cache for the pair. ``graph_id`` empties a shared namespace instead, service tokens only; the response ``scope`` field names what was cleared. ## Authentication - `HTTPBearer`, http, header `Authorization` ## Path parameters - `agent_id` (`clear_all_memories.path.agent_id`; string, required) ## Query parameters - `graph_id` (`clear_all_memories.query.graph_id`; any of, optional) — Optional. Clear a shared namespace instead of the user's own memory. Requires a service token; the agent_id path segment is ignored. - any of: `string`, `null` ## Header parameters - `X-On-Behalf-Of` (`clear_all_memories.header.X-On-Behalf-Of`; any of, optional) - any of: `string`, `null` - `X-External-User-Id` (`clear_all_memories.header.X-External-User-Id`; any of, optional) - any of: `string`, `null` - `X-App-Id` (`clear_all_memories.header.X-App-Id`; any of, optional) - any of: `string`, `null` ## Code samples ### cURL ```bash curl --request POST \ --url 'https://truememory-api.mvstg.com/memories//clear' \ --header 'Authorization: Bearer ' ``` ### TypeScript ```typescript const url = 'https://truememory-api.mvstg.com/memories//clear'; const options = {method: 'POST', headers: {Authorization: 'Bearer '}}; fetch(url, options) .then(res => res.json()) .then(json => console.log(json)) .catch(err => console.error(err)); ``` ### Python ```python import requests url = "https://truememory-api.mvstg.com/memories//clear" headers = {"Authorization": "Bearer "} response = requests.post(url, headers=headers) print(response.text) ``` ## Responses ### 200 Successful Response #### Example ```json { "status": "cleared", "scope": "user", "graph_id": "string", "deleted_count": 0, "expired_count": 0 } ``` - `scope` (`clear_all_memories.response.200.scope`; string, required) - one of `"user"`, `"graph"` - `deleted_count` (`clear_all_memories.response.200.deleted_count`; integer, required) - `expired_count` (`clear_all_memories.response.200.expired_count`; integer, required) — Same as deleted_count; kept for older clients. - `status` (`clear_all_memories.response.200.status`; string, optional) - default `"cleared"` - `graph_id` (`clear_all_memories.response.200.graph_id`; any of, optional) — Present only when scope is graph. - any of: `string`, `null` ### 422 Validation Error #### Example ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string", "input": null, "ctx": {} } ] } ``` - `detail` (`clear_all_memories.response.422.detail`; array, optional) - `loc` (`clear_all_memories.response.422.detail.loc`; array, required) - any of: `string`, `integer` - `msg` (`clear_all_memories.response.422.detail.msg`; string, required) - `type` (`clear_all_memories.response.422.detail.type`; string, required) - `input` (`clear_all_memories.response.422.detail.input`; unknown, optional) - `ctx` (`clear_all_memories.response.422.detail.ctx`; object, optional) # Delete a memory (deprecated — use /edges/{edge_id}) > Permanently delete a memory edge by UUID (hard delete, both stores). **Deprecated** — the path doesn't say what it deletes now that node deletion exists alongside it. Use ``DELETE /memories/{agent_id}/edges/ {edge_id}`` (same behavior) or ``.../nodes/{node_id}``. This route keeps serving unchanged until existing callers migrate. The lookup is scoped by the calling user's id, not by ``agent_id`` — so callers don't need to know which of the user's agents originally produced the memory. The ``agent_id`` path parameter is preserved for URL backwards-compatibility but is not used to constrain the search. Edge UUIDs are globally unique; the user-id prefix match still prevents cross-user deletion. With ``graph_id``, the edge is deleted from that shared namespace instead — service tokens only. Returns 404 if the edge isn't in the requested scope (or doesn't exist). Source: https://truememory-docs.mvstg.com/api/memories/delete/ · Markdown: https://truememory-docs.mvstg.com/api/memories/delete/index.md > **Deprecated.** Path: AI Gateway API › Memory `DELETE /memories/{agent_id}/{memory_id}` Permanently delete a memory edge by UUID (hard delete, both stores). **Deprecated** — the path doesn't say what it deletes now that node deletion exists alongside it. Use ``DELETE /memories/{agent_id}/edges/ {edge_id}`` (same behavior) or ``.../nodes/{node_id}``. This route keeps serving unchanged until existing callers migrate. The lookup is scoped by the calling user's id, not by ``agent_id`` — so callers don't need to know which of the user's agents originally produced the memory. The ``agent_id`` path parameter is preserved for URL backwards-compatibility but is not used to constrain the search. Edge UUIDs are globally unique; the user-id prefix match still prevents cross-user deletion. With ``graph_id``, the edge is deleted from that shared namespace instead — service tokens only. Returns 404 if the edge isn't in the requested scope (or doesn't exist). ## Authentication - `HTTPBearer`, http, header `Authorization` ## Path parameters - `agent_id` (`delete_memory.path.agent_id`; string, required) - `memory_id` (`delete_memory.path.memory_id`; string, required) ## Query parameters - `graph_id` (`delete_memory.query.graph_id`; any of, optional) — Optional. Delete from a shared namespace instead of the user's own memory. Requires a service token; the agent_id path segment is ignored. - any of: `string`, `null` ## Header parameters - `X-On-Behalf-Of` (`delete_memory.header.X-On-Behalf-Of`; any of, optional) - any of: `string`, `null` - `X-External-User-Id` (`delete_memory.header.X-External-User-Id`; any of, optional) - any of: `string`, `null` - `X-App-Id` (`delete_memory.header.X-App-Id`; any of, optional) - any of: `string`, `null` ## Code samples ### cURL ```bash curl --request DELETE \ --url 'https://truememory-api.mvstg.com/memories//' \ --header 'Authorization: Bearer ' ``` ### TypeScript ```typescript const url = 'https://truememory-api.mvstg.com/memories//'; const options = {method: 'DELETE', headers: {Authorization: 'Bearer '}}; fetch(url, options) .then(res => res.json()) .then(json => console.log(json)) .catch(err => console.error(err)); ``` ### Python ```python import requests url = "https://truememory-api.mvstg.com/memories//" headers = {"Authorization": "Bearer "} response = requests.delete(url, headers=headers) print(response.text) ``` ## Responses ### 200 Successful Response #### Example ```json { "status": "deleted", "memory_id": "string" } ``` - `memory_id` (`delete_memory.response.200.memory_id`; string, required) - `status` (`delete_memory.response.200.status`; string, optional) - default `"deleted"` ### 422 Validation Error #### Example ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string", "input": null, "ctx": {} } ] } ``` - `detail` (`delete_memory.response.422.detail`; array, optional) - `loc` (`delete_memory.response.422.detail.loc`; array, required) - any of: `string`, `integer` - `msg` (`delete_memory.response.422.detail.msg`; string, required) - `type` (`delete_memory.response.422.detail.type`; string, required) - `input` (`delete_memory.response.422.detail.input`; unknown, optional) - `ctx` (`delete_memory.response.422.detail.ctx`; object, optional) # Delete an edge (one fact) > Permanently delete one edge by UUID — both stores. An edge carries one fact, so this is the single-fact delete (Zep's vocabulary: the deletable object is the edge; the fact is the text stored on it). The canonical, explicitly-named replacement for ``DELETE /memories/{agent_id}/{memory_id}``: same scoping, same cascade. The response names the field ``edge_id`` to match the route. Source: https://truememory-docs.mvstg.com/api/memories/edges/delete/ · Markdown: https://truememory-docs.mvstg.com/api/memories/edges/delete/index.md Path: AI Gateway API › Memory `DELETE /memories/{agent_id}/edges/{edge_id}` Permanently delete one edge by UUID — both stores. An edge carries one fact, so this is the single-fact delete (Zep's vocabulary: the deletable object is the edge; the fact is the text stored on it). The canonical, explicitly-named replacement for ``DELETE /memories/{agent_id}/{memory_id}``: same scoping, same cascade. The response names the field ``edge_id`` to match the route. ## Authentication - `HTTPBearer`, http, header `Authorization` ## Path parameters - `agent_id` (`delete_edge.path.agent_id`; string, required) - `edge_id` (`delete_edge.path.edge_id`; string, required) ## Query parameters - `graph_id` (`delete_edge.query.graph_id`; any of, optional) — Optional. Delete from a shared namespace instead of the user's own memory. Requires a service token; the agent_id path segment is ignored. - any of: `string`, `null` ## Header parameters - `X-On-Behalf-Of` (`delete_edge.header.X-On-Behalf-Of`; any of, optional) - any of: `string`, `null` - `X-External-User-Id` (`delete_edge.header.X-External-User-Id`; any of, optional) - any of: `string`, `null` - `X-App-Id` (`delete_edge.header.X-App-Id`; any of, optional) - any of: `string`, `null` ## Code samples ### cURL ```bash curl --request DELETE \ --url 'https://truememory-api.mvstg.com/memories//edges/' \ --header 'Authorization: Bearer ' ``` ### TypeScript ```typescript const url = 'https://truememory-api.mvstg.com/memories//edges/'; const options = {method: 'DELETE', headers: {Authorization: 'Bearer '}}; fetch(url, options) .then(res => res.json()) .then(json => console.log(json)) .catch(err => console.error(err)); ``` ### Python ```python import requests url = "https://truememory-api.mvstg.com/memories//edges/" headers = {"Authorization": "Bearer "} response = requests.delete(url, headers=headers) print(response.text) ``` ## Responses ### 200 Successful Response #### Example ```json { "status": "deleted", "edge_id": "string" } ``` - `edge_id` (`delete_edge.response.200.edge_id`; string, required) - `status` (`delete_edge.response.200.status`; string, optional) - default `"deleted"` ### 422 Validation Error #### Example ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string", "input": null, "ctx": {} } ] } ``` - `detail` (`delete_edge.response.422.detail`; array, optional) - `loc` (`delete_edge.response.422.detail.loc`; array, required) - any of: `string`, `integer` - `msg` (`delete_edge.response.422.detail.msg`; string, required) - `type` (`delete_edge.response.422.detail.type`; string, required) - `input` (`delete_edge.response.422.detail.input`; unknown, optional) - `ctx` (`delete_edge.response.422.detail.ctx`; object, optional) # Export the user's full memory graph > Return the user's full knowledge graph as a re-importable JSON envelope. Spans every agent scope for the user in one pass. Intended for user migration / GDPR-style data portability. Re-import side (if built later) would UNWIND the arrays back into Neo4j with the group_id remapped to the destination user. Source: https://truememory-docs.mvstg.com/api/memories/export/ · Markdown: https://truememory-docs.mvstg.com/api/memories/export/index.md Path: AI Gateway API › Memory `GET /memories/export` Return the user's full knowledge graph as a re-importable JSON envelope. Spans every agent scope for the user in one pass. Intended for user migration / GDPR-style data portability. Re-import side (if built later) would UNWIND the arrays back into Neo4j with the group_id remapped to the destination user. ## Authentication - `HTTPBearer`, http, header `Authorization` ## Query parameters - `include_episodes` (`export_memories.query.include_episodes`; boolean, optional) — Include raw :Episodic nodes (conversation chunks). Large + PII-heavy. - default `false` - `include_expired` (`export_memories.query.include_expired`; boolean, optional) — Include soft-deleted edges (expired_at set). - default `false` - `include_embeddings` (`export_memories.query.include_embeddings`; boolean, optional) — Include vector embeddings. Regeneratable; inflates payload ~10x. - default `false` ## Header parameters - `X-On-Behalf-Of` (`export_memories.header.X-On-Behalf-Of`; any of, optional) - any of: `string`, `null` - `X-External-User-Id` (`export_memories.header.X-External-User-Id`; any of, optional) - any of: `string`, `null` - `X-App-Id` (`export_memories.header.X-App-Id`; any of, optional) - any of: `string`, `null` ## Code samples ### cURL ```bash curl --request GET \ --url https://truememory-api.mvstg.com/memories/export \ --header 'Authorization: Bearer ' ``` ### TypeScript ```typescript const url = 'https://truememory-api.mvstg.com/memories/export'; const options = {method: 'GET', headers: {Authorization: 'Bearer '}}; fetch(url, options) .then(res => res.json()) .then(json => console.log(json)) .catch(err => console.error(err)); ``` ### Python ```python import requests url = "https://truememory-api.mvstg.com/memories/export" headers = {"Authorization": "Bearer "} response = requests.get(url, headers=headers) print(response.text) ``` ## Responses ### 200 Successful Response #### Example ```json { "schema_version": "string", "exported_at": "string", "source_user_id": "string", "counts": { "entity_nodes": 0, "entity_edges": 0, "episodes": 0 }, "entity_nodes": [ { "uuid": "string", "labels": [ "string" ], "properties": {} } ], "entity_edges": [ { "uuid": "string", "type": "string", "source_uuid": "string", "target_uuid": "string", "properties": {} } ], "episodes": [ { "uuid": "string", "properties": {} } ] } ``` - `schema_version` (`export_memories.response.200.schema_version`; string, required) - `exported_at` (`export_memories.response.200.exported_at`; any of, required) - any of: `string`, `null` - `source_user_id` (`export_memories.response.200.source_user_id`; string, required) - `counts` (`export_memories.response.200.counts`; object, required) - `entity_nodes` (`export_memories.response.200.counts.entity_nodes`; integer, required) - `entity_edges` (`export_memories.response.200.counts.entity_edges`; integer, required) - `episodes` (`export_memories.response.200.counts.episodes`; integer, required) - `entity_nodes` (`export_memories.response.200.entity_nodes`; array, required) - `uuid` (`export_memories.response.200.entity_nodes.uuid`; string, required) - `labels` (`export_memories.response.200.entity_nodes.labels`; array, required) - `properties` (`export_memories.response.200.entity_nodes.properties`; object, required) - `entity_edges` (`export_memories.response.200.entity_edges`; array, required) - `uuid` (`export_memories.response.200.entity_edges.uuid`; string, required) - `type` (`export_memories.response.200.entity_edges.type`; string, required) — Edge type; RELATES_TO for every exported fact today. - `source_uuid` (`export_memories.response.200.entity_edges.source_uuid`; string, required) - `target_uuid` (`export_memories.response.200.entity_edges.target_uuid`; string, required) - `properties` (`export_memories.response.200.entity_edges.properties`; object, required) - `episodes` (`export_memories.response.200.episodes`; array, required) — Empty unless include_episodes=true. - `uuid` (`export_memories.response.200.episodes.uuid`; string, required) - `properties` (`export_memories.response.200.episodes.properties`; object, required) ### 422 Validation Error #### Example ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string", "input": null, "ctx": {} } ] } ``` - `detail` (`export_memories.response.422.detail`; array, optional) - `loc` (`export_memories.response.422.detail.loc`; array, required) - any of: `string`, `integer` - `msg` (`export_memories.response.422.detail.msg`; string, required) - `type` (`export_memories.response.422.detail.type`; string, required) - `input` (`export_memories.response.422.detail.input`; unknown, optional) - `ctx` (`export_memories.response.422.detail.ctx`; object, optional) # List memories > List memories for the user + agent pair via relevance-ranked search. **Deprecated** — use ``GET /memories/timeline`` instead. This endpoint: - is relevance-ranked against a fixed prompt, not time-ordered; - is hard-capped at 50 results with no pagination; - costs an LLM embed + vector search + reranker pass per call. The new ``/memories/timeline`` endpoint returns flat, time-ordered, cursor-paginated edges with selectable ``sort`` (``created_at`` or ``valid_at``) — the right shape for user-facing memory browsers. Will be removed in a future release. New callers must use the timeline endpoint. Source: https://truememory-docs.mvstg.com/api/memories/get/ · Markdown: https://truememory-docs.mvstg.com/api/memories/get/index.md > **Deprecated.** Path: AI Gateway API › Memory `GET /memories/{agent_id}` List memories for the user + agent pair via relevance-ranked search. **Deprecated** — use ``GET /memories/timeline`` instead. This endpoint: - is relevance-ranked against a fixed prompt, not time-ordered; - is hard-capped at 50 results with no pagination; - costs an LLM embed + vector search + reranker pass per call. The new ``/memories/timeline`` endpoint returns flat, time-ordered, cursor-paginated edges with selectable ``sort`` (``created_at`` or ``valid_at``) — the right shape for user-facing memory browsers. Will be removed in a future release. New callers must use the timeline endpoint. ## Authentication - `HTTPBearer`, http, header `Authorization` ## Path parameters - `agent_id` (`list_memories.path.agent_id`; string, required) ## Header parameters - `X-On-Behalf-Of` (`list_memories.header.X-On-Behalf-Of`; any of, optional) - any of: `string`, `null` - `X-External-User-Id` (`list_memories.header.X-External-User-Id`; any of, optional) - any of: `string`, `null` - `X-App-Id` (`list_memories.header.X-App-Id`; any of, optional) - any of: `string`, `null` ## Code samples ### cURL ```bash curl --request GET \ --url 'https://truememory-api.mvstg.com/memories/' \ --header 'Authorization: Bearer ' ``` ### TypeScript ```typescript const url = 'https://truememory-api.mvstg.com/memories/'; const options = {method: 'GET', headers: {Authorization: 'Bearer '}}; fetch(url, options) .then(res => res.json()) .then(json => console.log(json)) .catch(err => console.error(err)); ``` ### Python ```python import requests url = "https://truememory-api.mvstg.com/memories/" headers = {"Authorization": "Bearer "} response = requests.get(url, headers=headers) print(response.text) ``` ## Responses ### 200 Successful Response #### Example ```json [ { "id": "string", "title": "string", "name": "string", "content": "string", "memory_type": "long_term", "source": "edge" } ] ``` - `id` (`list_memories.response.200.id`; string, required) - `title` (`list_memories.response.200.title`; string, required) - `name` (`list_memories.response.200.name`; string, required) - `content` (`list_memories.response.200.content`; string, required) - `memory_type` (`list_memories.response.200.memory_type`; string, required) - `source` (`list_memories.response.200.source`; string, required) ### 422 Validation Error #### Example ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string", "input": null, "ctx": {} } ] } ``` - `detail` (`list_memories.response.422.detail`; array, optional) - `loc` (`list_memories.response.422.detail.loc`; array, required) - any of: `string`, `integer` - `msg` (`list_memories.response.422.detail.msg`; string, required) - `type` (`list_memories.response.422.detail.type`; string, required) - `input` (`list_memories.response.422.detail.input`; unknown, optional) - `ctx` (`list_memories.response.422.detail.ctx`; object, optional) # Bulk-add episodes to memory graph (maintenance lane) > Ingest a batch of episodic payloads into one knowledge-graph scope. The bulk counterpart to ``POST /memories/graph`` for backfills and other operator-driven batches. The batch executes on the maintenance worker lane, so it never occupies realtime capacity, while still passing through the written scope's FIFO so it cannot race realtime graph writes. Episodes ingest in order with per-episode idempotency, so a retried batch resumes after the last committed episode. Always asynchronous — poll ``GET /memories/graph/status/{job_id}``. Source: https://truememory-docs.mvstg.com/api/memories/graph/bulk/ · Markdown: https://truememory-docs.mvstg.com/api/memories/graph/bulk/index.md Path: AI Gateway API › Memory `POST /memories/graph/bulk` Ingest a batch of episodic payloads into one knowledge-graph scope. The bulk counterpart to ``POST /memories/graph`` for backfills and other operator-driven batches. The batch executes on the maintenance worker lane, so it never occupies realtime capacity, while still passing through the written scope's FIFO so it cannot race realtime graph writes. Episodes ingest in order with per-episode idempotency, so a retried batch resumes after the last committed episode. Always asynchronous — poll ``GET /memories/graph/status/{job_id}``. ## Authentication - `HTTPBearer`, http, header `Authorization` ## Header parameters - `X-On-Behalf-Of` (`add_memory_data_bulk.header.X-On-Behalf-Of`; any of, optional) - any of: `string`, `null` - `X-External-User-Id` (`add_memory_data_bulk.header.X-External-User-Id`; any of, optional) - any of: `string`, `null` - `X-App-Id` (`add_memory_data_bulk.header.X-App-Id`; any of, optional) - any of: `string`, `null` ## Request body _Required._ - `episodes` (`add_memory_data_bulk.episodes`; array, required) - `data` (`add_memory_data_bulk.episodes.data`; string, required) - maxLength 50000 - `type` (`add_memory_data_bulk.episodes.type`; string, required) - one of `"message"`, `"json"`, `"text"`, `"document"`, `"note"`, `"transcribe"` - `source_description` (`add_memory_data_bulk.episodes.source_description`; any of, optional) - any of: `string`, `null` - `created_at` (`add_memory_data_bulk.episodes.created_at`; any of, optional) - any of: `string`, `null` - `agent_id` (`add_memory_data_bulk.agent_id`; any of, optional) — Target the calling user's memory, scoped by this agent. Provide exactly one of agent_id or graph_id. - any of: `string`, `null` - `graph_id` (`add_memory_data_bulk.graph_id`; any of, optional) — Target a shared namespace instead of user memory. Requires a service token. - any of: `string`, `null` - `mode` (`add_memory_data_bulk.mode`; string, optional) — append: sequential ingestion with full edge invalidation against the existing graph. seed: one bulk extraction pass over the batch — the fast path for migrating into a fresh or replaced graph. - one of `"append"`, `"seed"`; default `"append"` - `replace` (`add_memory_data_bulk.replace`; boolean, optional) — Expire the scope's existing memory edges before ingesting (start from fresh). User scopes (agent_id) only. - default `false` ## Example request ```json { "episodes": [ { "data": "string", "type": "message", "source_description": "string", "created_at": "2019-08-24T14:15:22Z" } ], "agent_id": "string", "graph_id": "string", "mode": "append", "replace": false } ``` ## Code samples ### cURL ```bash curl --request POST \ --url https://truememory-api.mvstg.com/memories/graph/bulk \ --header 'Authorization: Bearer ' \ --header 'Content-Type: application/json' \ --data ' { "episodes": [ { "data": "string", "type": "message", "source_description": "string", "created_at": "2019-08-24T14:15:22Z" } ], "agent_id": "string", "graph_id": "string", "mode": "append", "replace": false } ' ``` ### TypeScript ```typescript const url = 'https://truememory-api.mvstg.com/memories/graph/bulk'; const options = { method: 'POST', headers: {'Content-Type': 'application/json', Authorization: 'Bearer '}, body: JSON.stringify({ episodes: [ { data: 'string', type: 'message', source_description: 'string', created_at: '2019-08-24T14:15:22Z' } ], agent_id: 'string', graph_id: 'string', mode: 'append', replace: false }) }; fetch(url, options) .then(res => res.json()) .then(json => console.log(json)) .catch(err => console.error(err)); ``` ### Python ```python import requests url = "https://truememory-api.mvstg.com/memories/graph/bulk" payload = { "episodes": [ { "data": "string", "type": "message", "source_description": "string", "created_at": "2019-08-24T14:15:22Z" } ], "agent_id": "string", "graph_id": "string", "mode": "append", "replace": False } headers = { "Content-Type": "application/json", "Authorization": "Bearer " } response = requests.post(url, json=payload, headers=headers) print(response.text) ``` ## Responses ### 202 Successful Response #### Example ```json { "status": "processing", "job_id": "string", "episodes": 0 } ``` - `job_id` (`add_memory_data_bulk.response.202.job_id`; string, required) — Poll GET /memories/graph/status/{job_id} with this. - `episodes` (`add_memory_data_bulk.response.202.episodes`; integer, required) — Number of episodes queued. - `status` (`add_memory_data_bulk.response.202.status`; string, optional) - default `"processing"` ### 422 Validation Error #### Example ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string", "input": null, "ctx": {} } ] } ``` - `detail` (`add_memory_data_bulk.response.422.detail`; array, optional) - `loc` (`add_memory_data_bulk.response.422.detail.loc`; array, required) - any of: `string`, `integer` - `msg` (`add_memory_data_bulk.response.422.detail.msg`; string, required) - `type` (`add_memory_data_bulk.response.422.detail.type`; string, required) - `input` (`add_memory_data_bulk.response.422.detail.input`; unknown, optional) - `ctx` (`add_memory_data_bulk.response.422.detail.ctx`; object, optional) # Add data to the memory graph > Add arbitrary data (text, JSON, or message) to a knowledge graph. Target exactly one scope: `agent_id` writes to the calling user's memory (shared agents write to the shared graph, agent-scoped agents to their own); `graph_id` writes to a shared namespace and requires a service token. Processing is asynchronous — entity extraction and relationship building happen in the background. Returns 202 immediately. Source: https://truememory-docs.mvstg.com/api/memories/graph/create/ · Markdown: https://truememory-docs.mvstg.com/api/memories/graph/create/index.md Path: AI Gateway API › Memory `POST /memories/graph` Add arbitrary data (text, JSON, or message) to a knowledge graph. Target exactly one scope: `agent_id` writes to the calling user's memory (shared agents write to the shared graph, agent-scoped agents to their own); `graph_id` writes to a shared namespace and requires a service token. Processing is asynchronous — entity extraction and relationship building happen in the background. Returns 202 immediately. ## Authentication - `HTTPBearer`, http, header `Authorization` ## Header parameters - `X-On-Behalf-Of` (`add_memory_data.header.X-On-Behalf-Of`; any of, optional) - any of: `string`, `null` - `X-External-User-Id` (`add_memory_data.header.X-External-User-Id`; any of, optional) - any of: `string`, `null` - `X-App-Id` (`add_memory_data.header.X-App-Id`; any of, optional) - any of: `string`, `null` ## Request body _Required._ - `data` (`add_memory_data.data`; string, required) - maxLength 50000 - `type` (`add_memory_data.type`; string, required) — Content shape; selects the extraction rules and nothing else. In a user scope, `message` is one user turn and is stored like a chat exchange (see `assistant_message`). `document` is a note or document the user brought in whole (`note` is accepted as its older name). - one of `"message"`, `"json"`, `"text"`, `"document"`, `"note"`, `"transcribe"` - `agent_id` (`add_memory_data.agent_id`; any of, optional) — Target the calling user's memory, scoped by this agent. Provide exactly one of agent_id or graph_id. - any of: `string`, `null` - `graph_id` (`add_memory_data.graph_id`; any of, optional) — Target a shared namespace instead of user memory. Requires a service token. - any of: `string`, `null` - `source_description` (`add_memory_data.source_description`; any of, optional) - any of: `string`, `null` - `created_at` (`add_memory_data.created_at`; any of, optional) - any of: `string`, `null` - `assistant_message` (`add_memory_data.assistant_message`; any of, optional) — For a user-scope `type=message` only: the assistant turn that answered `data`. Stored with it as one exchange so extraction reads the turn in context. - any of: `string`, `null` - `wait` (`add_memory_data.wait`; boolean, optional) — When true, wait for processing to complete before responding. - default `false` ## Example request ```json { "data": "string", "type": "message", "agent_id": "string", "graph_id": "string", "source_description": "string", "created_at": "2019-08-24T14:15:22Z", "assistant_message": "string", "wait": false } ``` ## Code samples ### cURL ```bash curl --request POST \ --url https://truememory-api.mvstg.com/memories/graph \ --header 'Authorization: Bearer ' \ --header 'Content-Type: application/json' \ --data ' { "data": "string", "type": "message", "agent_id": "string", "graph_id": "string", "source_description": "string", "created_at": "2019-08-24T14:15:22Z", "assistant_message": "string", "wait": false } ' ``` ### TypeScript ```typescript const url = 'https://truememory-api.mvstg.com/memories/graph'; const options = { method: 'POST', headers: {'Content-Type': 'application/json', Authorization: 'Bearer '}, body: JSON.stringify({ data: 'string', type: 'message', agent_id: 'string', graph_id: 'string', source_description: 'string', created_at: '2019-08-24T14:15:22Z', assistant_message: 'string', wait: false }) }; fetch(url, options) .then(res => res.json()) .then(json => console.log(json)) .catch(err => console.error(err)); ``` ### Python ```python import requests url = "https://truememory-api.mvstg.com/memories/graph" payload = { "data": "string", "type": "message", "agent_id": "string", "graph_id": "string", "source_description": "string", "created_at": "2019-08-24T14:15:22Z", "assistant_message": "string", "wait": False } headers = { "Content-Type": "application/json", "Authorization": "Bearer " } response = requests.post(url, json=payload, headers=headers) print(response.text) ``` ## Responses ### 200 Ingestion finished inline (`wait=true`). #### Example ```json { "status": "completed" } ``` - `status` (`add_memory_data.response.200.status`; string, optional) - default `"completed"` ### 202 Ingestion was queued; poll the job for completion. #### Example ```json { "status": "processing", "job_id": "string" } ``` - `job_id` (`add_memory_data.response.202.job_id`; string, required) — Poll GET /memories/graph/status/{job_id} with this. - `status` (`add_memory_data.response.202.status`; string, optional) - default `"processing"` ### 422 Validation Error #### Example ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string", "input": null, "ctx": {} } ] } ``` - `detail` (`add_memory_data.response.422.detail`; array, optional) - `loc` (`add_memory_data.response.422.detail.loc`; array, required) - any of: `string`, `integer` - `msg` (`add_memory_data.response.422.detail.msg`; string, required) - `type` (`add_memory_data.response.422.detail.type`; string, required) - `input` (`add_memory_data.response.422.detail.input`; unknown, optional) - `ctx` (`add_memory_data.response.422.detail.ctx`; object, optional) # Get memory graph for visualization > One graph for visualization: entity nodes plus their ``RELATES_TO`` facts. Every entity with a live fact in scope appears, whatever produced it — a chat turn, an agent-tool write, or an API ingest of any ``type``. ``source_description`` is a provenance label and never decides what is shown, and no entity type is hidden. Episodes are not nodes here; they stay stored verbatim and come back through ``GET /memories/export?include_episodes=true``. ``cursor`` paginates edges. Namespace views (``graph_id``) require a service token. Source: https://truememory-docs.mvstg.com/api/memories/graph/list/ · Markdown: https://truememory-docs.mvstg.com/api/memories/graph/list/index.md Path: AI Gateway API › Memory `GET /memories/graph` One graph for visualization: entity nodes plus their ``RELATES_TO`` facts. Every entity with a live fact in scope appears, whatever produced it — a chat turn, an agent-tool write, or an API ingest of any ``type``. ``source_description`` is a provenance label and never decides what is shown, and no entity type is hidden. Episodes are not nodes here; they stay stored verbatim and come back through ``GET /memories/export?include_episodes=true``. ``cursor`` paginates edges. Namespace views (``graph_id``) require a service token. ## Authentication - `HTTPBearer`, http, header `Authorization` ## Query parameters - `agent_id` (`get_memory_graph.query.agent_id`; any of, optional) - any of: `string`, `null` - `graph_id` (`get_memory_graph.query.graph_id`; any of, optional) — Optional. Render a shared namespace instead of the user's own graph. Requires a service token; mutually exclusive with agent_id; no subject user needed. - any of: `string`, `null` - `limit` (`get_memory_graph.query.limit`; integer, optional) - min 1; max 500; default `200` - `cursor` (`get_memory_graph.query.cursor`; any of, optional) — Optional. Opaque keyset cursor over edges: pass back the previous page's next_cursor verbatim. Format is '|' — the id half is what keeps edges sharing a timestamp from being skipped, so do not truncate it. Malformed values return 400 VALIDATION_ERROR. - any of: `string`, `null` - `visibility` (`get_memory_graph.query.visibility`; any of, optional) — Optional. Narrow the graph to nodes with this visibility; an edge survives only when both its endpoints do. Omit for the full graph. - any of: `string`, `null` ## Header parameters - `X-On-Behalf-Of` (`get_memory_graph.header.X-On-Behalf-Of`; any of, optional) - any of: `string`, `null` - `X-External-User-Id` (`get_memory_graph.header.X-External-User-Id`; any of, optional) - any of: `string`, `null` - `X-App-Id` (`get_memory_graph.header.X-App-Id`; any of, optional) - any of: `string`, `null` ## Code samples ### cURL ```bash curl --request GET \ --url https://truememory-api.mvstg.com/memories/graph \ --header 'Authorization: Bearer ' ``` ### TypeScript ```typescript const url = 'https://truememory-api.mvstg.com/memories/graph'; const options = {method: 'GET', headers: {Authorization: 'Bearer '}}; fetch(url, options) .then(res => res.json()) .then(json => console.log(json)) .catch(err => console.error(err)); ``` ### Python ```python import requests url = "https://truememory-api.mvstg.com/memories/graph" headers = {"Authorization": "Bearer "} response = requests.get(url, headers=headers) print(response.text) ``` ## Responses ### 200 Successful Response #### Example ```json { "nodes": [ { "id": "string", "name": "string", "summary": "string", "summary_raw": "string", "labels": [ "string" ], "visibility": "private", "attributes": {}, "created_at": "string" } ], "edges": [ { "id": "string", "source": "string", "target": "string", "label": "string", "fact": "string", "created_at": "string", "valid_at": "string" } ], "next_cursor": "string" } ``` - `nodes` (`get_memory_graph.response.200.nodes`; array, required) - `id` (`get_memory_graph.response.200.nodes.id`; string, required) - `name` (`get_memory_graph.response.200.nodes.name`; string, required) - `summary` (`get_memory_graph.response.200.nodes.summary`; string, required) — Humanized summary when available, else the raw one. - `summary_raw` (`get_memory_graph.response.200.nodes.summary_raw`; string, required) - `labels` (`get_memory_graph.response.200.nodes.labels`; array, required) — Entity types; never empty. - `visibility` (`get_memory_graph.response.200.nodes.visibility`; string, required) - one of `"private"`, `"public"` - `attributes` (`get_memory_graph.response.200.nodes.attributes`; object, required) — Every other property the entity carries; keys vary by type. - `created_at` (`get_memory_graph.response.200.nodes.created_at`; any of, required) - any of: `string`, `null` - `edges` (`get_memory_graph.response.200.edges`; array, required) - `id` (`get_memory_graph.response.200.edges.id`; string, required) - `source` (`get_memory_graph.response.200.edges.source`; string, required) — Node id the fact starts from. - `target` (`get_memory_graph.response.200.edges.target`; string, required) — Node id the fact points to. - `label` (`get_memory_graph.response.200.edges.label`; string, required) - `fact` (`get_memory_graph.response.200.edges.fact`; string, required) - `created_at` (`get_memory_graph.response.200.edges.created_at`; any of, required) - any of: `string`, `null` - `valid_at` (`get_memory_graph.response.200.edges.valid_at`; any of, required) - any of: `string`, `null` - `next_cursor` (`get_memory_graph.response.200.next_cursor`; any of, required) — Pass back as `cursor` for the next page of edges; null on the last page. - any of: `string`, `null` ### 422 Validation Error #### Example ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string", "input": null, "ctx": {} } ] } ``` - `detail` (`get_memory_graph.response.422.detail`; array, optional) - `loc` (`get_memory_graph.response.422.detail.loc`; array, required) - any of: `string`, `integer` - `msg` (`get_memory_graph.response.422.detail.msg`; string, required) - `type` (`get_memory_graph.response.422.detail.type`; string, required) - `input` (`get_memory_graph.response.422.detail.input`; unknown, optional) - `ctx` (`get_memory_graph.response.422.detail.ctx`; object, optional) # Get graph-ingest job status > Report the status of an async graph-ingest job (from POST /memories/graph with ``wait=false``). Lets callers poll until ingestion completes before searching — the async counterpart to ``wait=true``. ``done`` is true once the job reaches a terminal state; ``succeeded`` distinguishes success from failure. Source: https://truememory-docs.mvstg.com/api/memories/graph/status/get/ · Markdown: https://truememory-docs.mvstg.com/api/memories/graph/status/get/index.md Path: AI Gateway API › Memory `GET /memories/graph/status/{job_id}` Report the status of an async graph-ingest job (from POST /memories/graph with ``wait=false``). Lets callers poll until ingestion completes before searching — the async counterpart to ``wait=true``. ``done`` is true once the job reaches a terminal state; ``succeeded`` distinguishes success from failure. ## Authentication - `HTTPBearer`, http, header `Authorization` ## Path parameters - `job_id` (`get_graph_ingest_status.path.job_id`; string, required) ## Header parameters - `X-On-Behalf-Of` (`get_graph_ingest_status.header.X-On-Behalf-Of`; any of, optional) - any of: `string`, `null` - `X-External-User-Id` (`get_graph_ingest_status.header.X-External-User-Id`; any of, optional) - any of: `string`, `null` - `X-App-Id` (`get_graph_ingest_status.header.X-App-Id`; any of, optional) - any of: `string`, `null` ## Code samples ### cURL ```bash curl --request GET \ --url 'https://truememory-api.mvstg.com/memories/graph/status/' \ --header 'Authorization: Bearer ' ``` ### TypeScript ```typescript const url = 'https://truememory-api.mvstg.com/memories/graph/status/'; const options = {method: 'GET', headers: {Authorization: 'Bearer '}}; fetch(url, options) .then(res => res.json()) .then(json => console.log(json)) .catch(err => console.error(err)); ``` ### Python ```python import requests url = "https://truememory-api.mvstg.com/memories/graph/status/" headers = {"Authorization": "Bearer "} response = requests.get(url, headers=headers) print(response.text) ``` ## Responses ### 200 Successful Response #### Example ```json { "job_id": "string", "status": "string", "done": true, "succeeded": true } ``` - `job_id` (`get_graph_ingest_status.response.200.job_id`; string, required) - `status` (`get_graph_ingest_status.response.200.status`; string, required) — Raw job state: todo, doing, succeeded, failed, cancelled or aborted. - `done` (`get_graph_ingest_status.response.200.done`; boolean, required) — True once the job has reached a terminal state. - `succeeded` (`get_graph_ingest_status.response.200.succeeded`; boolean, required) — True only when the job finished successfully. ### 422 Validation Error #### Example ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string", "input": null, "ctx": {} } ] } ``` - `detail` (`get_graph_ingest_status.response.422.detail`; array, optional) - `loc` (`get_graph_ingest_status.response.422.detail.loc`; array, required) - any of: `string`, `integer` - `msg` (`get_graph_ingest_status.response.422.detail.msg`; string, required) - `type` (`get_graph_ingest_status.response.422.detail.type`; string, required) - `input` (`get_graph_ingest_status.response.422.detail.input`; unknown, optional) - `ctx` (`get_graph_ingest_status.response.422.detail.ctx`; object, optional) # Warm the user's memory cache > Warm the user's memory cache before the first chat request. TIE uses a multi-tier retrieval architecture. The fastest retrieval happens from a hot in-memory cache. After several minutes of inactivity the cache expires and the next request falls back to a slower database query while a background job repopulates the cache. Call this endpoint when the user logs in or opens your app to proactively load their knowledge graph into cache so the first chat request is fast. A user token may only warm its own cache; naming another user's id returns 403. Service principals may name any user, as they do elsewhere. Source: https://truememory-docs.mvstg.com/api/memories/graph/warm/ · Markdown: https://truememory-docs.mvstg.com/api/memories/graph/warm/index.md Path: AI Gateway API › Memory `POST /memories/graph/{user_id}/warm` Warm the user's memory cache before the first chat request. TIE uses a multi-tier retrieval architecture. The fastest retrieval happens from a hot in-memory cache. After several minutes of inactivity the cache expires and the next request falls back to a slower database query while a background job repopulates the cache. Call this endpoint when the user logs in or opens your app to proactively load their knowledge graph into cache so the first chat request is fast. A user token may only warm its own cache; naming another user's id returns 403. Service principals may name any user, as they do elsewhere. ## Authentication - `HTTPBearer`, http, header `Authorization` ## Path parameters - `user_id` (`warm_user_cache.path.user_id`; string, required) ## Query parameters - `agent_id` (`warm_user_cache.query.agent_id`; any of, optional) - any of: `string`, `null` ## Header parameters - `X-On-Behalf-Of` (`warm_user_cache.header.X-On-Behalf-Of`; any of, optional) - any of: `string`, `null` - `X-External-User-Id` (`warm_user_cache.header.X-External-User-Id`; any of, optional) - any of: `string`, `null` - `X-App-Id` (`warm_user_cache.header.X-App-Id`; any of, optional) - any of: `string`, `null` ## Code samples ### cURL ```bash curl --request POST \ --url 'https://truememory-api.mvstg.com/memories/graph//warm' \ --header 'Authorization: Bearer ' ``` ### TypeScript ```typescript const url = 'https://truememory-api.mvstg.com/memories/graph//warm'; const options = {method: 'POST', headers: {Authorization: 'Bearer '}}; fetch(url, options) .then(res => res.json()) .then(json => console.log(json)) .catch(err => console.error(err)); ``` ### Python ```python import requests url = "https://truememory-api.mvstg.com/memories/graph//warm" headers = {"Authorization": "Bearer "} response = requests.post(url, headers=headers) print(response.text) ``` ## Responses ### 200 Successful Response #### Example ```json { "entities": [ { "name": "string", "summary": "string" } ], "facts": [ "string" ] } ``` - `entities` (`warm_user_cache.response.200.entities`; array, required) — Up to 15 entities. - `name` (`warm_user_cache.response.200.entities.name`; string, required) - `summary` (`warm_user_cache.response.200.entities.summary`; string, required) - `facts` (`warm_user_cache.response.200.facts`; array, required) — Up to 15 facts. ### 422 Validation Error #### Example ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string", "input": null, "ctx": {} } ] } ``` - `detail` (`warm_user_cache.response.422.detail`; array, optional) - `loc` (`warm_user_cache.response.422.detail.loc`; array, required) - any of: `string`, `integer` - `msg` (`warm_user_cache.response.422.detail.msg`; string, required) - `type` (`warm_user_cache.response.422.detail.type`; string, required) - `input` (`warm_user_cache.response.422.detail.input`; unknown, optional) - `ctx` (`warm_user_cache.response.422.detail.ctx`; object, optional) # Import a previously exported memory envelope > Import an exported envelope into the caller's own memory graph. The envelope's ``source_user_id`` is remapped to the authenticated user. MERGE semantics: records with UUIDs already present in the target graph are skipped. Embedding regeneration runs asynchronously; poll ``GET /memories/import/{import_id}`` to track progress. Source: https://truememory-docs.mvstg.com/api/memories/import/create/ · Markdown: https://truememory-docs.mvstg.com/api/memories/import/create/index.md Path: AI Gateway API › Memory `POST /memories/import` Import an exported envelope into the caller's own memory graph. The envelope's ``source_user_id`` is remapped to the authenticated user. MERGE semantics: records with UUIDs already present in the target graph are skipped. Embedding regeneration runs asynchronously; poll ``GET /memories/import/{import_id}`` to track progress. ## Authentication - `HTTPBearer`, http, header `Authorization` ## Header parameters - `X-On-Behalf-Of` (`import_memories.header.X-On-Behalf-Of`; any of, optional) - any of: `string`, `null` - `X-External-User-Id` (`import_memories.header.X-External-User-Id`; any of, optional) - any of: `string`, `null` - `X-App-Id` (`import_memories.header.X-App-Id`; any of, optional) - any of: `string`, `null` ## Request body _Required._ - `schema_version` (`import_memories.schema_version`; string, required) - `source_user_id` (`import_memories.source_user_id`; string, required) - `exported_at` (`import_memories.exported_at`; any of, optional) - any of: `string`, `null` - `counts` (`import_memories.counts`; any of, optional) - any of: `map`, `null` - `entity_nodes` (`import_memories.entity_nodes`; array, optional) - `entity_edges` (`import_memories.entity_edges`; array, optional) - `episodes` (`import_memories.episodes`; array, optional) ## Example request ```json { "schema_version": "string", "source_user_id": "string", "exported_at": "string", "counts": { "property1": 0, "property2": 0 }, "entity_nodes": [ {} ], "entity_edges": [ {} ], "episodes": [ {} ] } ``` ## Code samples ### cURL ```bash curl --request POST \ --url https://truememory-api.mvstg.com/memories/import \ --header 'Authorization: Bearer ' \ --header 'Content-Type: application/json' \ --data ' { "schema_version": "string", "source_user_id": "string", "exported_at": "string", "counts": { "property1": 0, "property2": 0 }, "entity_nodes": [ {} ], "entity_edges": [ {} ], "episodes": [ {} ] } ' ``` ### TypeScript ```typescript const url = 'https://truememory-api.mvstg.com/memories/import'; const options = { method: 'POST', headers: {'Content-Type': 'application/json', Authorization: 'Bearer '}, body: JSON.stringify({ schema_version: 'string', source_user_id: 'string', exported_at: 'string', counts: {property1: 0, property2: 0}, entity_nodes: [{}], entity_edges: [{}], episodes: [{}] }) }; fetch(url, options) .then(res => res.json()) .then(json => console.log(json)) .catch(err => console.error(err)); ``` ### Python ```python import requests url = "https://truememory-api.mvstg.com/memories/import" payload = { "schema_version": "string", "source_user_id": "string", "exported_at": "string", "counts": { "property1": 0, "property2": 0 }, "entity_nodes": [{}], "entity_edges": [{}], "episodes": [{}] } headers = { "Content-Type": "application/json", "Authorization": "Bearer " } response = requests.post(url, json=payload, headers=headers) print(response.text) ``` ## Responses ### 202 Successful Response #### Example ```json { "status": "processing", "import_id": "string", "nodes_written": 0, "edges_written": 0, "episodes_written": 0, "edges_skipped": [ "string" ] } ``` - `import_id` (`import_memories.response.202.import_id`; string, required) — Poll GET /memories/import/{import_id} with this. - `nodes_written` (`import_memories.response.202.nodes_written`; integer, required) - `edges_written` (`import_memories.response.202.edges_written`; integer, required) - `episodes_written` (`import_memories.response.202.episodes_written`; integer, required) - `edges_skipped` (`import_memories.response.202.edges_skipped`; array, required) — Ids of edges that were not imported. - `status` (`import_memories.response.202.status`; string, optional) - default `"processing"` ### 422 Validation Error #### Example ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string", "input": null, "ctx": {} } ] } ``` - `detail` (`import_memories.response.422.detail`; array, optional) - `loc` (`import_memories.response.422.detail.loc`; array, required) - any of: `string`, `integer` - `msg` (`import_memories.response.422.detail.msg`; string, required) - `type` (`import_memories.response.422.detail.type`; string, required) - `input` (`import_memories.response.422.detail.input`; unknown, optional) - `ctx` (`import_memories.response.422.detail.ctx`; object, optional) # Check status of an async memory import > Poll import status. 404 if the id is unknown or belongs to another user. Source: https://truememory-docs.mvstg.com/api/memories/import/get/ · Markdown: https://truememory-docs.mvstg.com/api/memories/import/get/index.md Path: AI Gateway API › Memory `GET /memories/import/{import_id}` Poll import status. 404 if the id is unknown or belongs to another user. ## Authentication - `HTTPBearer`, http, header `Authorization` ## Path parameters - `import_id` (`get_import_status.path.import_id`; string, required) ## Header parameters - `X-On-Behalf-Of` (`get_import_status.header.X-On-Behalf-Of`; any of, optional) - any of: `string`, `null` - `X-External-User-Id` (`get_import_status.header.X-External-User-Id`; any of, optional) - any of: `string`, `null` - `X-App-Id` (`get_import_status.header.X-App-Id`; any of, optional) - any of: `string`, `null` ## Code samples ### cURL ```bash curl --request GET \ --url 'https://truememory-api.mvstg.com/memories/import/' \ --header 'Authorization: Bearer ' ``` ### TypeScript ```typescript const url = 'https://truememory-api.mvstg.com/memories/import/'; const options = {method: 'GET', headers: {Authorization: 'Bearer '}}; fetch(url, options) .then(res => res.json()) .then(json => console.log(json)) .catch(err => console.error(err)); ``` ### Python ```python import requests url = "https://truememory-api.mvstg.com/memories/import/" headers = {"Authorization": "Bearer "} response = requests.get(url, headers=headers) print(response.text) ``` ## Responses ### 200 Successful Response #### Example ```json { "import_id": "string", "status": "processing", "nodes_written": 0, "edges_written": 0, "episodes_written": 0, "nodes_embedded": 0, "edges_embedded": 0, "edges_skipped_count": 0, "started_at": "string", "finished_at": "string", "error": "string" } ``` - `import_id` (`get_import_status.response.200.import_id`; string, required) - `status` (`get_import_status.response.200.status`; string, required) - one of `"processing"`, `"ready"`, `"failed"` - `nodes_written` (`get_import_status.response.200.nodes_written`; integer, required) - `edges_written` (`get_import_status.response.200.edges_written`; integer, required) - `episodes_written` (`get_import_status.response.200.episodes_written`; integer, required) - `nodes_embedded` (`get_import_status.response.200.nodes_embedded`; integer, required) - `edges_embedded` (`get_import_status.response.200.edges_embedded`; integer, required) - `edges_skipped_count` (`get_import_status.response.200.edges_skipped_count`; integer, required) - `started_at` (`get_import_status.response.200.started_at`; string, required) - `finished_at` (`get_import_status.response.200.finished_at`; any of, required) - any of: `string`, `null` - `error` (`get_import_status.response.200.error`; any of, required) - any of: `string`, `null` ### 422 Validation Error #### Example ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string", "input": null, "ctx": {} } ] } ``` - `detail` (`get_import_status.response.422.detail`; array, optional) - `loc` (`get_import_status.response.422.detail.loc`; array, required) - any of: `string`, `integer` - `msg` (`get_import_status.response.422.detail.msg`; string, required) - `type` (`get_import_status.response.422.detail.type`; string, required) - `input` (`get_import_status.response.422.detail.input`; unknown, optional) - `ctx` (`get_import_status.response.422.detail.ctx`; object, optional) # Merge two memories > Collapse two entity-node memories into one. Live edges incident to ``duplicate_id`` are repointed at ``primary_id``; the duplicate is soft-marked via ``merged_into``; an ``IS_DUPLICATE_OF`` audit edge is written for traceability. Both nodes must already live in the requested user+agent scope. Source: https://truememory-docs.mvstg.com/api/memories/merge/ · Markdown: https://truememory-docs.mvstg.com/api/memories/merge/index.md Path: AI Gateway API › Memory `POST /memories/{agent_id}/merge` Collapse two entity-node memories into one. Live edges incident to ``duplicate_id`` are repointed at ``primary_id``; the duplicate is soft-marked via ``merged_into``; an ``IS_DUPLICATE_OF`` audit edge is written for traceability. Both nodes must already live in the requested user+agent scope. ## Authentication - `HTTPBearer`, http, header `Authorization` ## Path parameters - `agent_id` (`merge_memories.path.agent_id`; string, required) ## Header parameters - `X-On-Behalf-Of` (`merge_memories.header.X-On-Behalf-Of`; any of, optional) - any of: `string`, `null` - `X-External-User-Id` (`merge_memories.header.X-External-User-Id`; any of, optional) - any of: `string`, `null` - `X-App-Id` (`merge_memories.header.X-App-Id`; any of, optional) - any of: `string`, `null` ## Request body _Required._ - `primary_id` (`merge_memories.primary_id`; string, required) — UUID of the entity node to keep. - `duplicate_id` (`merge_memories.duplicate_id`; string, required) — UUID of the entity node to fold into primary. - `graph_id` (`merge_memories.graph_id`; any of, optional) — Optional. Merge within a shared namespace instead of the user's own memory. Requires a service token; the agent_id path segment is ignored. - any of: `string`, `null` ## Example request ```json { "primary_id": "string", "duplicate_id": "string", "graph_id": "string" } ``` ## Code samples ### cURL ```bash curl --request POST \ --url 'https://truememory-api.mvstg.com/memories//merge' \ --header 'Authorization: Bearer ' \ --header 'Content-Type: application/json' \ --data ' { "primary_id": "string", "duplicate_id": "string", "graph_id": "string" } ' ``` ### TypeScript ```typescript const url = 'https://truememory-api.mvstg.com/memories//merge'; const options = { method: 'POST', headers: {'Content-Type': 'application/json', Authorization: 'Bearer '}, body: JSON.stringify({primary_id: 'string', duplicate_id: 'string', graph_id: 'string'}) }; fetch(url, options) .then(res => res.json()) .then(json => console.log(json)) .catch(err => console.error(err)); ``` ### Python ```python import requests url = "https://truememory-api.mvstg.com/memories//merge" payload = { "primary_id": "string", "duplicate_id": "string", "graph_id": "string" } headers = { "Content-Type": "application/json", "Authorization": "Bearer " } response = requests.post(url, json=payload, headers=headers) print(response.text) ``` ## Responses ### 200 Successful Response #### Example ```json { "primary_id": "string", "duplicate_id": "string", "rewired_outgoing": 0, "rewired_incoming": 0, "audit_edge_id": "string", "merged_at": "string", "enriched": true, "enriched_summary": "string", "enriched_labels": [ "string" ] } ``` - `primary_id` (`merge_memories.response.200.primary_id`; string, required) - `duplicate_id` (`merge_memories.response.200.duplicate_id`; string, required) - `rewired_outgoing` (`merge_memories.response.200.rewired_outgoing`; integer, required) - `rewired_incoming` (`merge_memories.response.200.rewired_incoming`; integer, required) - `audit_edge_id` (`merge_memories.response.200.audit_edge_id`; any of, required) - any of: `string`, `null` - `merged_at` (`merge_memories.response.200.merged_at`; any of, required) - any of: `string`, `null` - `enriched` (`merge_memories.response.200.enriched`; boolean, required) - `enriched_summary` (`merge_memories.response.200.enriched_summary`; any of, required) - any of: `string`, `null` - `enriched_labels` (`merge_memories.response.200.enriched_labels`; any of, required) — Null when enrichment failed. - any of: `array`, `null` ### 422 Validation Error #### Example ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string", "input": null, "ctx": {} } ] } ``` - `detail` (`merge_memories.response.422.detail`; array, optional) - `loc` (`merge_memories.response.422.detail.loc`; array, required) - any of: `string`, `integer` - `msg` (`merge_memories.response.422.detail.msg`; string, required) - `type` (`merge_memories.response.422.detail.type`; string, required) - `input` (`merge_memories.response.422.detail.input`; unknown, optional) - `ctx` (`merge_memories.response.422.detail.ctx`; object, optional) # Delete a memory node > Permanently delete an entity node and every fact connected to it. The card-level "forget this topic" delete: the node, its fact edges in both directions, and their search-overlay rows are removed from both stores. Episodic source content is not touched — per-record erasure stays a separate concern (``clear`` erases everything for the pair). Scoping follows the fact delete: the calling user's id prefix, not ``agent_id`` (kept for URL shape); ``graph_id`` targets a shared namespace instead, service tokens only. Responses: 404 when the node isn't in the requested scope; 422 ``MEMORY_NODE_PROTECTED`` for structural nodes (the user's own User node, Topic and Value taxonomy nodes), which are never deletable. Source: https://truememory-docs.mvstg.com/api/memories/nodes/delete/ · Markdown: https://truememory-docs.mvstg.com/api/memories/nodes/delete/index.md Path: AI Gateway API › Memory `DELETE /memories/{agent_id}/nodes/{node_id}` Permanently delete an entity node and every fact connected to it. The card-level "forget this topic" delete: the node, its fact edges in both directions, and their search-overlay rows are removed from both stores. Episodic source content is not touched — per-record erasure stays a separate concern (``clear`` erases everything for the pair). Scoping follows the fact delete: the calling user's id prefix, not ``agent_id`` (kept for URL shape); ``graph_id`` targets a shared namespace instead, service tokens only. Responses: 404 when the node isn't in the requested scope; 422 ``MEMORY_NODE_PROTECTED`` for structural nodes (the user's own User node, Topic and Value taxonomy nodes), which are never deletable. ## Authentication - `HTTPBearer`, http, header `Authorization` ## Path parameters - `agent_id` (`delete_node.path.agent_id`; string, required) - `node_id` (`delete_node.path.node_id`; string, required) ## Query parameters - `graph_id` (`delete_node.query.graph_id`; any of, optional) — Optional. Delete from a shared namespace instead of the user's own memory. Requires a service token; the agent_id path segment is ignored. - any of: `string`, `null` ## Header parameters - `X-On-Behalf-Of` (`delete_node.header.X-On-Behalf-Of`; any of, optional) - any of: `string`, `null` - `X-External-User-Id` (`delete_node.header.X-External-User-Id`; any of, optional) - any of: `string`, `null` - `X-App-Id` (`delete_node.header.X-App-Id`; any of, optional) - any of: `string`, `null` ## Code samples ### cURL ```bash curl --request DELETE \ --url 'https://truememory-api.mvstg.com/memories//nodes/' \ --header 'Authorization: Bearer ' ``` ### TypeScript ```typescript const url = 'https://truememory-api.mvstg.com/memories//nodes/'; const options = {method: 'DELETE', headers: {Authorization: 'Bearer '}}; fetch(url, options) .then(res => res.json()) .then(json => console.log(json)) .catch(err => console.error(err)); ``` ### Python ```python import requests url = "https://truememory-api.mvstg.com/memories//nodes/" headers = {"Authorization": "Bearer "} response = requests.delete(url, headers=headers) print(response.text) ``` ## Responses ### 200 Successful Response #### Example ```json { "status": "deleted", "node_id": "string", "facts_deleted": 0 } ``` - `node_id` (`delete_node.response.200.node_id`; string, required) - `facts_deleted` (`delete_node.response.200.facts_deleted`; integer, required) - `status` (`delete_node.response.200.status`; string, optional) - default `"deleted"` ### 422 Validation Error #### Example ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string", "input": null, "ctx": {} } ] } ``` - `detail` (`delete_node.response.422.detail`; array, optional) - `loc` (`delete_node.response.422.detail.loc`; array, required) - any of: `string`, `integer` - `msg` (`delete_node.response.422.detail.msg`; string, required) - `type` (`delete_node.response.422.detail.type`; string, required) - `input` (`delete_node.response.422.detail.input`; unknown, optional) - `ctx` (`delete_node.response.422.detail.ctx`; object, optional) # Set a memory node's visibility > Mark one entity node public or private. Nodes are private until this is called — extraction never sets visibility, so nothing becomes public by inference. Marking a node public does **not** share it: it keeps its group and is still served to its owner alone. Cross-user sharing is what shared namespaces (``graph_id``) are for. The ``agent_id`` path segment is accepted for URL symmetry with the other ``/memories/{agent_id}/...`` routes but does not narrow the scope — the node is matched across the caller's agents, because the memory browser that lists nodes is itself cross-agent. Another user's node is never reachable. Responses: 404 when the node isn't in the requested scope; 422 ``MEMORY_NODE_PROTECTED`` for structural nodes (the user's own User node, Topic and Value taxonomy nodes) — the same set ``DELETE .../nodes/{id}`` refuses. Marking the User node public would make every fact hanging off it satisfy a ``visibility=public`` query at once. Source: https://truememory-docs.mvstg.com/api/memories/nodes/visibility/ · Markdown: https://truememory-docs.mvstg.com/api/memories/nodes/visibility/index.md Path: AI Gateway API › Memory `PATCH /memories/{agent_id}/nodes/{node_id}/visibility` Mark one entity node public or private. Nodes are private until this is called — extraction never sets visibility, so nothing becomes public by inference. Marking a node public does **not** share it: it keeps its group and is still served to its owner alone. Cross-user sharing is what shared namespaces (``graph_id``) are for. The ``agent_id`` path segment is accepted for URL symmetry with the other ``/memories/{agent_id}/...`` routes but does not narrow the scope — the node is matched across the caller's agents, because the memory browser that lists nodes is itself cross-agent. Another user's node is never reachable. Responses: 404 when the node isn't in the requested scope; 422 ``MEMORY_NODE_PROTECTED`` for structural nodes (the user's own User node, Topic and Value taxonomy nodes) — the same set ``DELETE .../nodes/{id}`` refuses. Marking the User node public would make every fact hanging off it satisfy a ``visibility=public`` query at once. ## Authentication - `HTTPBearer`, http, header `Authorization` ## Path parameters - `agent_id` (`set_memory_node_visibility.path.agent_id`; string, required) - `node_id` (`set_memory_node_visibility.path.node_id`; string, required) ## Header parameters - `X-On-Behalf-Of` (`set_memory_node_visibility.header.X-On-Behalf-Of`; any of, optional) - any of: `string`, `null` - `X-External-User-Id` (`set_memory_node_visibility.header.X-External-User-Id`; any of, optional) - any of: `string`, `null` - `X-App-Id` (`set_memory_node_visibility.header.X-App-Id`; any of, optional) - any of: `string`, `null` ## Request body _Required._ - `visibility` (`set_memory_node_visibility.visibility`; string, required) — 'public' un-privates the node; 'private' puts it back. - one of `"private"`, `"public"` - `graph_id` (`set_memory_node_visibility.graph_id`; any of, optional) — Optional. Target a node in a shared namespace instead of the user's own memory. Requires a service token; the agent_id path segment is ignored. - any of: `string`, `null` ## Example request ```json { "visibility": "private", "graph_id": "string" } ``` ## Code samples ### cURL ```bash curl --request PATCH \ --url 'https://truememory-api.mvstg.com/memories//nodes//visibility' \ --header 'Authorization: Bearer ' \ --header 'Content-Type: application/json' \ --data ' { "visibility": "private", "graph_id": "string" } ' ``` ### TypeScript ```typescript const url = 'https://truememory-api.mvstg.com/memories//nodes//visibility'; const options = { method: 'PATCH', headers: {'Content-Type': 'application/json', Authorization: 'Bearer '}, body: JSON.stringify({visibility: 'private', graph_id: 'string'}) }; fetch(url, options) .then(res => res.json()) .then(json => console.log(json)) .catch(err => console.error(err)); ``` ### Python ```python import requests url = "https://truememory-api.mvstg.com/memories//nodes//visibility" payload = { "visibility": "private", "graph_id": "string" } headers = { "Content-Type": "application/json", "Authorization": "Bearer " } response = requests.patch(url, json=payload, headers=headers) print(response.text) ``` ## Responses ### 200 Successful Response #### Example ```json { "id": "string", "name": "string", "labels": [ "string" ], "group_id": "string", "visibility": "private" } ``` - `id` (`set_memory_node_visibility.response.200.id`; string, required) - `name` (`set_memory_node_visibility.response.200.name`; any of, required) - any of: `string`, `null` - `labels` (`set_memory_node_visibility.response.200.labels`; array, required) - `group_id` (`set_memory_node_visibility.response.200.group_id`; any of, required) - any of: `string`, `null` - `visibility` (`set_memory_node_visibility.response.200.visibility`; string, required) - one of `"private"`, `"public"` ### 422 Validation Error #### Example ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string", "input": null, "ctx": {} } ] } ``` - `detail` (`set_memory_node_visibility.response.422.detail`; array, optional) - `loc` (`set_memory_node_visibility.response.422.detail.loc`; array, required) - any of: `string`, `integer` - `msg` (`set_memory_node_visibility.response.422.detail.msg`; string, required) - `type` (`set_memory_node_visibility.response.422.detail.type`; string, required) - `input` (`set_memory_node_visibility.response.422.detail.input`; unknown, optional) - `ctx` (`set_memory_node_visibility.response.422.detail.ctx`; object, optional) # Retrieve memory context for a message > Retrieve the full memory context that would be injected during a chat turn. Runs the same pipeline as POST /v1/chat/completions — session context via cache/fallback, low-information gating, and per-message graph search. Returns raw data, rendered prompts, and cache diagnostics. Source: https://truememory-docs.mvstg.com/api/memories/retrieve/ · Markdown: https://truememory-docs.mvstg.com/api/memories/retrieve/index.md Path: AI Gateway API › Memory `POST /memories/retrieve` Retrieve the full memory context that would be injected during a chat turn. Runs the same pipeline as POST /v1/chat/completions — session context via cache/fallback, low-information gating, and per-message graph search. Returns raw data, rendered prompts, and cache diagnostics. ## Authentication - `HTTPBearer`, http, header `Authorization` ## Header parameters - `X-On-Behalf-Of` (`retrieve_memory.header.X-On-Behalf-Of`; any of, optional) - any of: `string`, `null` - `X-External-User-Id` (`retrieve_memory.header.X-External-User-Id`; any of, optional) - any of: `string`, `null` - `X-App-Id` (`retrieve_memory.header.X-App-Id`; any of, optional) - any of: `string`, `null` ## Request body _Required._ - `query` (`retrieve_memory.query`; string, required) — The user message to retrieve memories for - minLength 1; maxLength 5000 - `agent_id` (`retrieve_memory.agent_id`; string, required) - `display_name` (`retrieve_memory.display_name`; any of, optional) — User display name rendered into the context prompt (chat parity) - any of: `string`, `null` - `email` (`retrieve_memory.email`; any of, optional) — User email rendered into the context prompt (chat parity) - any of: `string`, `null` - `graph_ids` (`retrieve_memory.graph_ids`; any of, optional) — Shared knowledge graphs to blend into the per-message retrieval alongside the user's own memory. - any of: `array`, `null` ## Example request ```json { "query": "string", "agent_id": "string", "display_name": "string", "email": "string", "graph_ids": [ "string" ] } ``` ## Code samples ### cURL ```bash curl --request POST \ --url https://truememory-api.mvstg.com/memories/retrieve \ --header 'Authorization: Bearer ' \ --header 'Content-Type: application/json' \ --data ' { "query": "string", "agent_id": "string", "display_name": "string", "email": "string", "graph_ids": [ "string" ] } ' ``` ### TypeScript ```typescript const url = 'https://truememory-api.mvstg.com/memories/retrieve'; const options = { method: 'POST', headers: {'Content-Type': 'application/json', Authorization: 'Bearer '}, body: JSON.stringify({ query: 'string', agent_id: 'string', display_name: 'string', email: 'string', graph_ids: ['string'] }) }; fetch(url, options) .then(res => res.json()) .then(json => console.log(json)) .catch(err => console.error(err)); ``` ### Python ```python import requests url = "https://truememory-api.mvstg.com/memories/retrieve" payload = { "query": "string", "agent_id": "string", "display_name": "string", "email": "string", "graph_ids": ["string"] } headers = { "Content-Type": "application/json", "Authorization": "Bearer " } response = requests.post(url, json=payload, headers=headers) print(response.text) ``` ## Responses ### 200 Successful Response #### Example ```json { "query": "string", "user_id": "string", "agent_id": "string", "elapsed_seconds": 0, "prompt": "string", "context": { "data": { "entities": [ { "name": "string", "summary": "string" } ], "facts": [ "string" ] }, "prompt": "string", "elapsed_seconds": 0, "cache_hit": true, "cache_age_seconds": 0, "source": "string", "prefetch_in_flight": true, "cache_size": 0 }, "retrieval": { "low_info_skipped": true, "results": [ { "id": "string", "name": "string", "content": "string", "created_at": "string", "valid_at": "string", "source_name": "string", "target_name": "string", "source_descriptions": [ "string" ], "scope": "user", "graph_id": "string" } ], "prompt": "string", "error": "string", "elapsed_seconds": 0, "result_count": 0 } } ``` - `query` (`retrieve_memory.response.200.query`; string, required) - `user_id` (`retrieve_memory.response.200.user_id`; string, required) - `agent_id` (`retrieve_memory.response.200.agent_id`; string, required) - `elapsed_seconds` (`retrieve_memory.response.200.elapsed_seconds`; number, required) - `prompt` (`retrieve_memory.response.200.prompt`; any of, required) - any of: `string`, `null` - `context` (`retrieve_memory.response.200.context`; object, required) - `data` (`retrieve_memory.response.200.context.data`; object, required) - `entities` (`retrieve_memory.response.200.context.data.entities`; array, required) - `name` (`retrieve_memory.response.200.context.data.entities.name`; string, required) - `summary` (`retrieve_memory.response.200.context.data.entities.summary`; any of, required) - any of: `string`, `null` - `facts` (`retrieve_memory.response.200.context.data.facts`; array, required) - `prompt` (`retrieve_memory.response.200.context.prompt`; any of, required) - any of: `string`, `null` - `elapsed_seconds` (`retrieve_memory.response.200.context.elapsed_seconds`; number, required) - `cache_hit` (`retrieve_memory.response.200.context.cache_hit`; boolean, required) - `cache_age_seconds` (`retrieve_memory.response.200.context.cache_age_seconds`; any of, required) - any of: `number`, `null` - `source` (`retrieve_memory.response.200.context.source`; string, required) — Where the context came from, e.g. cypher_fallback. - `prefetch_in_flight` (`retrieve_memory.response.200.context.prefetch_in_flight`; boolean, required) - `cache_size` (`retrieve_memory.response.200.context.cache_size`; integer, required) - `retrieval` (`retrieve_memory.response.200.retrieval`; object, required) - `low_info_skipped` (`retrieve_memory.response.200.retrieval.low_info_skipped`; boolean, required) - `results` (`retrieve_memory.response.200.retrieval.results`; array, required) - `id` (`retrieve_memory.response.200.retrieval.results.id`; string, required) - `name` (`retrieve_memory.response.200.retrieval.results.name`; string, required) - `content` (`retrieve_memory.response.200.retrieval.results.content`; string, required) - `created_at` (`retrieve_memory.response.200.retrieval.results.created_at`; any of, required) - any of: `string`, `null` - `valid_at` (`retrieve_memory.response.200.retrieval.results.valid_at`; any of, required) - any of: `string`, `null` - `source_name` (`retrieve_memory.response.200.retrieval.results.source_name`; any of, required) - any of: `string`, `null` - `target_name` (`retrieve_memory.response.200.retrieval.results.target_name`; any of, required) - any of: `string`, `null` - `source_descriptions` (`retrieve_memory.response.200.retrieval.results.source_descriptions`; array, required) - `scope` (`retrieve_memory.response.200.retrieval.results.scope`; string, required) - one of `"user"`, `"graph"` - `graph_id` (`retrieve_memory.response.200.retrieval.results.graph_id`; any of, required) - any of: `string`, `null` - `prompt` (`retrieve_memory.response.200.retrieval.prompt`; any of, required) - any of: `string`, `null` - `error` (`retrieve_memory.response.200.retrieval.error`; any of, required) - any of: `string`, `null` - `elapsed_seconds` (`retrieve_memory.response.200.retrieval.elapsed_seconds`; number, required) - `result_count` (`retrieve_memory.response.200.retrieval.result_count`; integer, required) ### 422 Validation Error #### Example ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string", "input": null, "ctx": {} } ] } ``` - `detail` (`retrieve_memory.response.422.detail`; array, optional) - `loc` (`retrieve_memory.response.422.detail.loc`; array, required) - any of: `string`, `integer` - `msg` (`retrieve_memory.response.422.detail.msg`; string, required) - `type` (`retrieve_memory.response.422.detail.type`; string, required) - `input` (`retrieve_memory.response.422.detail.input`; unknown, optional) - `ctx` (`retrieve_memory.response.422.detail.ctx`; object, optional) # Search memory graph > Search the user's knowledge graph for relevant facts and entities. Uses hybrid retrieval (BM25 + cosine + graph traversal + cross-encoder reranking) to find the most relevant memory edges. Source: https://truememory-docs.mvstg.com/api/memories/search/ · Markdown: https://truememory-docs.mvstg.com/api/memories/search/index.md Path: AI Gateway API › Memory `POST /memories/search` Search the user's knowledge graph for relevant facts and entities. Uses hybrid retrieval (BM25 + cosine + graph traversal + cross-encoder reranking) to find the most relevant memory edges. ## Authentication - `HTTPBearer`, http, header `Authorization` ## Header parameters - `X-On-Behalf-Of` (`search_memories.header.X-On-Behalf-Of`; any of, optional) - any of: `string`, `null` - `X-External-User-Id` (`search_memories.header.X-External-User-Id`; any of, optional) - any of: `string`, `null` - `X-App-Id` (`search_memories.header.X-App-Id`; any of, optional) - any of: `string`, `null` ## Request body _Required._ - `query` (`search_memories.query`; string, required) - minLength 1; maxLength 5000 - `agent_id` (`search_memories.agent_id`; string, required) - `max_results` (`search_memories.max_results`; integer, optional) - min 1; max 50; default `10` - `created_after` (`search_memories.created_after`; any of, optional) — Only return memories created after this datetime - any of: `string`, `null` - `created_before` (`search_memories.created_before`; any of, optional) — Only return memories created before this datetime - any of: `string`, `null` - `graph_ids` (`search_memories.graph_ids`; any of, optional) — Shared knowledge graphs to blend into the search alongside the user's own memory. Results from these come back tagged scope="graph" with their graph_id. - any of: `array`, `null` - `visibility` (`search_memories.visibility`; any of, optional) — Optional. Keep only facts whose both endpoint nodes carry this visibility — 'public' therefore returns facts that expose no private node, which is what makes them safe to surface outside the owner's private view. Omit for no visibility filtering. Every result reports source_visibility and target_visibility regardless, so you can filter on your own terms instead. - any of: `string`, `null` ## Example request ```json { "query": "string", "agent_id": "string", "max_results": 10, "created_after": "2019-08-24T14:15:22Z", "created_before": "2019-08-24T14:15:22Z", "graph_ids": [ "string" ], "visibility": "private" } ``` ## Code samples ### cURL ```bash curl --request POST \ --url https://truememory-api.mvstg.com/memories/search \ --header 'Authorization: Bearer ' \ --header 'Content-Type: application/json' \ --data ' { "query": "string", "agent_id": "string", "max_results": 10, "created_after": "2019-08-24T14:15:22Z", "created_before": "2019-08-24T14:15:22Z", "graph_ids": [ "string" ], "visibility": "private" } ' ``` ### TypeScript ```typescript const url = 'https://truememory-api.mvstg.com/memories/search'; const options = { method: 'POST', headers: {'Content-Type': 'application/json', Authorization: 'Bearer '}, body: JSON.stringify({ query: 'string', agent_id: 'string', max_results: 10, created_after: '2019-08-24T14:15:22Z', created_before: '2019-08-24T14:15:22Z', graph_ids: ['string'], visibility: 'private' }) }; fetch(url, options) .then(res => res.json()) .then(json => console.log(json)) .catch(err => console.error(err)); ``` ### Python ```python import requests url = "https://truememory-api.mvstg.com/memories/search" payload = { "query": "string", "agent_id": "string", "max_results": 10, "created_after": "2019-08-24T14:15:22Z", "created_before": "2019-08-24T14:15:22Z", "graph_ids": ["string"], "visibility": "private" } headers = { "Content-Type": "application/json", "Authorization": "Bearer " } response = requests.post(url, json=payload, headers=headers) print(response.text) ``` ## Responses ### 200 Successful Response #### Example ```json [ { "id": "string", "name": "string", "content": "string", "created_at": "string", "valid_at": "string", "source_name": "string", "target_name": "string", "source_labels": [ "string" ], "target_labels": [ "string" ], "source_descriptions": [ "string" ], "source_visibility": "private", "target_visibility": "private", "scope": "user", "graph_id": "string" } ] ``` - `id` (`search_memories.response.200.id`; string, required) - `name` (`search_memories.response.200.name`; string, required) - `content` (`search_memories.response.200.content`; string, required) - `created_at` (`search_memories.response.200.created_at`; any of, required) - any of: `string`, `null` - `valid_at` (`search_memories.response.200.valid_at`; any of, required) - any of: `string`, `null` - `source_name` (`search_memories.response.200.source_name`; any of, required) - any of: `string`, `null` - `target_name` (`search_memories.response.200.target_name`; any of, required) - any of: `string`, `null` - `source_labels` (`search_memories.response.200.source_labels`; array, required) - `target_labels` (`search_memories.response.200.target_labels`; array, required) - `source_descriptions` (`search_memories.response.200.source_descriptions`; array, required) - `source_visibility` (`search_memories.response.200.source_visibility`; string, required) - one of `"private"`, `"public"` - `target_visibility` (`search_memories.response.200.target_visibility`; string, required) - one of `"private"`, `"public"` - `scope` (`search_memories.response.200.scope`; string, required) - one of `"user"`, `"graph"` - `graph_id` (`search_memories.response.200.graph_id`; any of, required) — Set only when scope is graph. - any of: `string`, `null` ### 422 Validation Error #### Example ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string", "input": null, "ctx": {} } ] } ``` - `detail` (`search_memories.response.422.detail`; array, optional) - `loc` (`search_memories.response.422.detail.loc`; array, required) - any of: `string`, `integer` - `msg` (`search_memories.response.422.detail.msg`; string, required) - `type` (`search_memories.response.422.detail.type`; string, required) - `input` (`search_memories.response.422.detail.input`; unknown, optional) - `ctx` (`search_memories.response.422.detail.ctx`; object, optional) # Get the memory timeline > Recently active entities first — one card per entity, not per fact. Each item is **one entity** with its humanized summary as the body and its connected edge facts inlined (for an "N facts" disclosure). Designed for user-facing memory browsers (Memories tab, infinite scroll). Prefer this over ``/memories/{agent_id}`` (which is relevance-ranked, capped at 50, and not paginated). Query params: - ``agent_id``: optional, scope to one agent. Cross-agent when omitted. - ``limit``: 1-200, default 20. - ``cursor``: ISO datetime from the previous page's ``next_cursor``. - ``sort``: - ``last_updated`` (default) — entities ordered by ``max(edge.created_at)``; new facts make an entity pop to the top. - ``created`` — entities ordered by their own creation time. Namespace feeds (``graph_id``) require a service token. Source: https://truememory-docs.mvstg.com/api/memories/timeline/ · Markdown: https://truememory-docs.mvstg.com/api/memories/timeline/index.md Path: AI Gateway API › Memory `GET /memories/timeline` Recently active entities first — one card per entity, not per fact. Each item is **one entity** with its humanized summary as the body and its connected edge facts inlined (for an "N facts" disclosure). Designed for user-facing memory browsers (Memories tab, infinite scroll). Prefer this over ``/memories/{agent_id}`` (which is relevance-ranked, capped at 50, and not paginated). Query params: - ``agent_id``: optional, scope to one agent. Cross-agent when omitted. - ``limit``: 1-200, default 20. - ``cursor``: ISO datetime from the previous page's ``next_cursor``. - ``sort``: - ``last_updated`` (default) — entities ordered by ``max(edge.created_at)``; new facts make an entity pop to the top. - ``created`` — entities ordered by their own creation time. Namespace feeds (``graph_id``) require a service token. ## Authentication - `HTTPBearer`, http, header `Authorization` ## Query parameters - `agent_id` (`get_memory_timeline.query.agent_id`; any of, optional) - any of: `string`, `null` - `graph_id` (`get_memory_timeline.query.graph_id`; any of, optional) — Optional. Feed from a shared namespace instead of the user's own graph. Requires a service token; mutually exclusive with agent_id; no subject user needed. - any of: `string`, `null` - `limit` (`get_memory_timeline.query.limit`; integer, optional) - min 1; max 500; default `20` - `cursor` (`get_memory_timeline.query.cursor`; any of, optional) — Optional. Opaque keyset cursor: pass back the previous page's next_cursor verbatim to get the next page. Format is '|' — the id half is what keeps entities sharing a timestamp from being skipped, so do not truncate it. Malformed values return 400 VALIDATION_ERROR. There is no offset/page parameter — this is the only pagination mechanism. - any of: `string`, `null` - `sort` (`get_memory_timeline.query.sort`; string, optional) - pattern `^(last_updated|created)$`; default `"last_updated"` - `visibility` (`get_memory_timeline.query.visibility`; any of, optional) — Optional. Narrow the feed to entities with this visibility. Omit for the whole feed. Every item reports its own 'visibility' either way. - any of: `string`, `null` ## Header parameters - `X-On-Behalf-Of` (`get_memory_timeline.header.X-On-Behalf-Of`; any of, optional) - any of: `string`, `null` - `X-External-User-Id` (`get_memory_timeline.header.X-External-User-Id`; any of, optional) - any of: `string`, `null` - `X-App-Id` (`get_memory_timeline.header.X-App-Id`; any of, optional) - any of: `string`, `null` ## Code samples ### cURL ```bash curl --request GET \ --url https://truememory-api.mvstg.com/memories/timeline \ --header 'Authorization: Bearer ' ``` ### TypeScript ```typescript const url = 'https://truememory-api.mvstg.com/memories/timeline'; const options = {method: 'GET', headers: {Authorization: 'Bearer '}}; fetch(url, options) .then(res => res.json()) .then(json => console.log(json)) .catch(err => console.error(err)); ``` ### Python ```python import requests url = "https://truememory-api.mvstg.com/memories/timeline" headers = {"Authorization": "Bearer "} response = requests.get(url, headers=headers) print(response.text) ``` ## Responses ### 200 Successful Response #### Example ```json { "items": [ { "id": "string", "name": "string", "labels": [ "string" ], "visibility": "private", "summary": "string", "summary_raw": "string", "first_seen_at": "string", "last_updated_at": "string", "fact_count": 0, "facts": [ { "id": "string", "label": "string", "fact": "string", "created_at": "string", "valid_at": "string", "direction": "outgoing", "other": { "id": "string", "name": "string", "labels": [ "string" ] } } ] } ], "next_cursor": "string", "sort": "last_updated" } ``` - `items` (`get_memory_timeline.response.200.items`; array, required) - `id` (`get_memory_timeline.response.200.items.id`; string, required) - `name` (`get_memory_timeline.response.200.items.name`; string, required) - `labels` (`get_memory_timeline.response.200.items.labels`; array, required) — Entity types; never empty. - `visibility` (`get_memory_timeline.response.200.items.visibility`; string, required) - one of `"private"`, `"public"` - `summary` (`get_memory_timeline.response.200.items.summary`; string, required) - `summary_raw` (`get_memory_timeline.response.200.items.summary_raw`; string, required) - `first_seen_at` (`get_memory_timeline.response.200.items.first_seen_at`; any of, required) - any of: `string`, `null` - `last_updated_at` (`get_memory_timeline.response.200.items.last_updated_at`; any of, required) - any of: `string`, `null` - `fact_count` (`get_memory_timeline.response.200.items.fact_count`; integer, required) - `facts` (`get_memory_timeline.response.200.items.facts`; array, required) - `id` (`get_memory_timeline.response.200.items.facts.id`; any of, required) - any of: `string`, `null` - `label` (`get_memory_timeline.response.200.items.facts.label`; string, required) - `fact` (`get_memory_timeline.response.200.items.facts.fact`; string, required) - `created_at` (`get_memory_timeline.response.200.items.facts.created_at`; any of, required) - any of: `string`, `null` - `valid_at` (`get_memory_timeline.response.200.items.facts.valid_at`; any of, required) - any of: `string`, `null` - `direction` (`get_memory_timeline.response.200.items.facts.direction`; string, required) - one of `"outgoing"`, `"incoming"` - `other` (`get_memory_timeline.response.200.items.facts.other`; object, required) — The entity at the other end of the fact. - `id` (`get_memory_timeline.response.200.items.facts.other.id`; any of, required) - any of: `string`, `null` - `name` (`get_memory_timeline.response.200.items.facts.other.name`; string, required) - `labels` (`get_memory_timeline.response.200.items.facts.other.labels`; array, required) - `next_cursor` (`get_memory_timeline.response.200.next_cursor`; any of, required) — Null on the last page. - any of: `string`, `null` - `sort` (`get_memory_timeline.response.200.sort`; string, required) - one of `"last_updated"`, `"created"` ### 422 Validation Error #### Example ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string", "input": null, "ctx": {} } ] } ``` - `detail` (`get_memory_timeline.response.422.detail`; array, optional) - `loc` (`get_memory_timeline.response.422.detail.loc`; array, required) - any of: `string`, `integer` - `msg` (`get_memory_timeline.response.422.detail.msg`; string, required) - `type` (`get_memory_timeline.response.422.detail.type`; string, required) - `input` (`get_memory_timeline.response.422.detail.input`; unknown, optional) - `ctx` (`get_memory_timeline.response.422.detail.ctx`; object, optional) # Upload a mind banner > Upload or replace the mind's banner. Accepts PNG, JPEG, WebP or SVG up to 5 MB. Replacing removes the previous file. Returns the updated mind. Gated as a write on the mind: its owner, or someone holding an admin grant. A viewer the mind was shared with cannot reskin it for everyone else. Source: https://truememory-docs.mvstg.com/api/minds/banner/ · Markdown: https://truememory-docs.mvstg.com/api/minds/banner/index.md Path: AI Gateway API › Minds `PUT /minds/{mind_id}/banner` Upload or replace the mind's banner. Accepts PNG, JPEG, WebP or SVG up to 5 MB. Replacing removes the previous file. Returns the updated mind. Gated as a write on the mind: its owner, or someone holding an admin grant. A viewer the mind was shared with cannot reskin it for everyone else. ## Authentication - `HTTPBearer`, http, header `Authorization` ## Path parameters - `mind_id` (`upload_mind_banner.path.mind_id`; string, required) - format `uuid` ## Header parameters - `X-App-Id` (`upload_mind_banner.header.X-App-Id`; any of, optional) - any of: `string`, `null` - `X-On-Behalf-Of` (`upload_mind_banner.header.X-On-Behalf-Of`; any of, optional) - any of: `string`, `null` - `X-External-User-Id` (`upload_mind_banner.header.X-External-User-Id`; any of, optional) - any of: `string`, `null` ## Request body _Required._ - `file` (`upload_mind_banner.file`; string, required) ## Example request ```text { "file": "string" } ``` ## Code samples ### cURL ```bash curl --request PUT \ --url 'https://truememory-api.mvstg.com/minds//banner' \ --header 'Authorization: Bearer ' \ --form-string 'file=string' ``` ### TypeScript ```typescript const formData = new FormData(); formData.append('file', 'string'); const url = 'https://truememory-api.mvstg.com/minds//banner'; const options = {method: 'PUT', headers: {Authorization: 'Bearer '}, body: formData}; fetch(url, options) .then(res => res.json()) .then(json => console.log(json)) .catch(err => console.error(err)); ``` ### Python ```python import requests url = "https://truememory-api.mvstg.com/minds//banner" files = [ ("file", (None, "string")) ] headers = {"Authorization": "Bearer "} response = requests.put(url, files=files, headers=headers) print(response.text) ``` ## Responses ### 200 Successful Response #### Example ```json { "id": "string", "owner_type": "user", "owner_id": "string", "app_id": "string", "slug": "string", "name": "string", "description": "string", "emoji": "string", "usage_hint": "string", "visibility": "private", "banner_image_url": "string", "skills": [ { "id": "string", "key": "string", "name": "string" } ], "brain_count": 0, "created_at": "2019-08-24T14:15:22Z", "updated_at": "2019-08-24T14:15:22Z", "skill_ids": [ "string" ] } ``` - `id` (`upload_mind_banner.response.200.id`; string, required) - `owner_type` (`upload_mind_banner.response.200.owner_type`; string, required) - one of `"user"`, `"org"` - `owner_id` (`upload_mind_banner.response.200.owner_id`; string, required) - `slug` (`upload_mind_banner.response.200.slug`; string, required) - `name` (`upload_mind_banner.response.200.name`; string, required) - `created_at` (`upload_mind_banner.response.200.created_at`; string, required) - format `date-time` - `updated_at` (`upload_mind_banner.response.200.updated_at`; string, required) - format `date-time` - `skill_ids` (`upload_mind_banner.response.200.skill_ids`; array, required) — Ids of ``skills``, for clients seeding a multi-select. - `app_id` (`upload_mind_banner.response.200.app_id`; any of, optional) - any of: `string`, `null` - `description` (`upload_mind_banner.response.200.description`; any of, optional) - any of: `string`, `null` - `emoji` (`upload_mind_banner.response.200.emoji`; any of, optional) - any of: `string`, `null` - `usage_hint` (`upload_mind_banner.response.200.usage_hint`; any of, optional) - any of: `string`, `null` - `visibility` (`upload_mind_banner.response.200.visibility`; string, optional) - one of `"private"`, `"app"`, `"public"`; default `"private"` - `banner_image_url` (`upload_mind_banner.response.200.banner_image_url`; any of, optional) - any of: `string`, `null` - `skills` (`upload_mind_banner.response.200.skills`; array, optional) - `id` (`upload_mind_banner.response.200.skills.id`; string, required) - `key` (`upload_mind_banner.response.200.skills.key`; string, required) - `name` (`upload_mind_banner.response.200.skills.name`; string, required) - `brain_count` (`upload_mind_banner.response.200.brain_count`; integer, optional) - default `0` ### 422 Validation Error #### Example ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string", "input": null, "ctx": {} } ] } ``` - `detail` (`upload_mind_banner.response.422.detail`; array, optional) - `loc` (`upload_mind_banner.response.422.detail.loc`; array, required) - any of: `string`, `integer` - `msg` (`upload_mind_banner.response.422.detail.msg`; string, required) - `type` (`upload_mind_banner.response.422.detail.type`; string, required) - `input` (`upload_mind_banner.response.422.detail.input`; unknown, optional) - `ctx` (`upload_mind_banner.response.422.detail.ctx`; object, optional) # Take a brain out of a mind > Gated on the mind, not the brain — a mind admin who does not own a brain must still be able to take it out, or a collection could be filled with items its manager cannot remove. Source: https://truememory-docs.mvstg.com/api/minds/brains/delete/ · Markdown: https://truememory-docs.mvstg.com/api/minds/brains/delete/index.md Path: AI Gateway API › Minds `DELETE /minds/{mind_id}/brains/{brain_id}` Gated on the mind, not the brain — a mind admin who does not own a brain must still be able to take it out, or a collection could be filled with items its manager cannot remove. ## Authentication - `HTTPBearer`, http, header `Authorization` ## Path parameters - `mind_id` (`remove_mind_brain.path.mind_id`; string, required) - format `uuid` - `brain_id` (`remove_mind_brain.path.brain_id`; string, required) - format `uuid` ## Header parameters - `X-App-Id` (`remove_mind_brain.header.X-App-Id`; any of, optional) - any of: `string`, `null` - `X-On-Behalf-Of` (`remove_mind_brain.header.X-On-Behalf-Of`; any of, optional) - any of: `string`, `null` - `X-External-User-Id` (`remove_mind_brain.header.X-External-User-Id`; any of, optional) - any of: `string`, `null` ## Code samples ### cURL ```bash curl --request DELETE \ --url 'https://truememory-api.mvstg.com/minds//brains/' \ --header 'Authorization: Bearer ' ``` ### TypeScript ```typescript const url = 'https://truememory-api.mvstg.com/minds//brains/'; const options = {method: 'DELETE', headers: {Authorization: 'Bearer '}}; fetch(url, options) .then(res => res.json()) .then(json => console.log(json)) .catch(err => console.error(err)); ``` ### Python ```python import requests url = "https://truememory-api.mvstg.com/minds//brains/" headers = {"Authorization": "Bearer "} response = requests.delete(url, headers=headers) print(response.text) ``` ## Responses ### 204 Successful Response ### 422 Validation Error #### Example ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string", "input": null, "ctx": {} } ] } ``` - `detail` (`remove_mind_brain.response.422.detail`; array, optional) - `loc` (`remove_mind_brain.response.422.detail.loc`; array, required) - any of: `string`, `integer` - `msg` (`remove_mind_brain.response.422.detail.msg`; string, required) - `type` (`remove_mind_brain.response.422.detail.type`; string, required) - `input` (`remove_mind_brain.response.422.detail.input`; unknown, optional) - `ctx` (`remove_mind_brain.response.422.detail.ctx`; object, optional) # List the brains in a mind Source: https://truememory-docs.mvstg.com/api/minds/brains/list/ · Markdown: https://truememory-docs.mvstg.com/api/minds/brains/list/index.md Path: AI Gateway API › Minds `GET /minds/{mind_id}/brains` ## Authentication - `HTTPBearer`, http, header `Authorization` ## Path parameters - `mind_id` (`list_mind_brains.path.mind_id`; string, required) - format `uuid` ## Header parameters - `X-App-Id` (`list_mind_brains.header.X-App-Id`; any of, optional) - any of: `string`, `null` - `X-On-Behalf-Of` (`list_mind_brains.header.X-On-Behalf-Of`; any of, optional) - any of: `string`, `null` - `X-External-User-Id` (`list_mind_brains.header.X-External-User-Id`; any of, optional) - any of: `string`, `null` ## Code samples ### cURL ```bash curl --request GET \ --url 'https://truememory-api.mvstg.com/minds//brains' \ --header 'Authorization: Bearer ' ``` ### TypeScript ```typescript const url = 'https://truememory-api.mvstg.com/minds//brains'; const options = {method: 'GET', headers: {Authorization: 'Bearer '}}; fetch(url, options) .then(res => res.json()) .then(json => console.log(json)) .catch(err => console.error(err)); ``` ### Python ```python import requests url = "https://truememory-api.mvstg.com/minds//brains" headers = {"Authorization": "Bearer "} response = requests.get(url, headers=headers) print(response.text) ``` ## Responses ### 200 Successful Response #### Example ```json [ { "id": "string", "name": "string", "emoji": "string", "document_count": 0 } ] ``` - `id` (`list_mind_brains.response.200.id`; string, required) - `name` (`list_mind_brains.response.200.name`; string, required) - `emoji` (`list_mind_brains.response.200.emoji`; any of, optional) - any of: `string`, `null` - `document_count` (`list_mind_brains.response.200.document_count`; integer, optional) - default `0` ### 422 Validation Error #### Example ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string", "input": null, "ctx": {} } ] } ``` - `detail` (`list_mind_brains.response.422.detail`; array, optional) - `loc` (`list_mind_brains.response.422.detail.loc`; array, required) - any of: `string`, `integer` - `msg` (`list_mind_brains.response.422.detail.msg`; string, required) - `type` (`list_mind_brains.response.422.detail.type`; string, required) - `input` (`list_mind_brains.response.422.detail.input`; unknown, optional) - `ctx` (`list_mind_brains.response.422.detail.ctx`; object, optional) # Put a brain into a mind > Add a brain you **own** to a mind you may reshape. Ownership, not readability: a brain merely shared with you must not be loanable into a mind you then share onward. A brain lives in at most one mind, so adding it to a second moves it. Source: https://truememory-docs.mvstg.com/api/minds/brains/update/ · Markdown: https://truememory-docs.mvstg.com/api/minds/brains/update/index.md Path: AI Gateway API › Minds `PUT /minds/{mind_id}/brains/{brain_id}` Add a brain you **own** to a mind you may reshape. Ownership, not readability: a brain merely shared with you must not be loanable into a mind you then share onward. A brain lives in at most one mind, so adding it to a second moves it. ## Authentication - `HTTPBearer`, http, header `Authorization` ## Path parameters - `mind_id` (`add_mind_brain.path.mind_id`; string, required) - format `uuid` - `brain_id` (`add_mind_brain.path.brain_id`; string, required) - format `uuid` ## Header parameters - `X-App-Id` (`add_mind_brain.header.X-App-Id`; any of, optional) - any of: `string`, `null` - `X-On-Behalf-Of` (`add_mind_brain.header.X-On-Behalf-Of`; any of, optional) - any of: `string`, `null` - `X-External-User-Id` (`add_mind_brain.header.X-External-User-Id`; any of, optional) - any of: `string`, `null` ## Code samples ### cURL ```bash curl --request PUT \ --url 'https://truememory-api.mvstg.com/minds//brains/' \ --header 'Authorization: Bearer ' ``` ### TypeScript ```typescript const url = 'https://truememory-api.mvstg.com/minds//brains/'; const options = {method: 'PUT', headers: {Authorization: 'Bearer '}}; fetch(url, options) .then(res => res.json()) .then(json => console.log(json)) .catch(err => console.error(err)); ``` ### Python ```python import requests url = "https://truememory-api.mvstg.com/minds//brains/" headers = {"Authorization": "Bearer "} response = requests.put(url, headers=headers) print(response.text) ``` ## Responses ### 204 Successful Response ### 422 Validation Error #### Example ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string", "input": null, "ctx": {} } ] } ``` - `detail` (`add_mind_brain.response.422.detail`; array, optional) - `loc` (`add_mind_brain.response.422.detail.loc`; array, required) - any of: `string`, `integer` - `msg` (`add_mind_brain.response.422.detail.msg`; string, required) - `type` (`add_mind_brain.response.422.detail.type`; string, required) - `input` (`add_mind_brain.response.422.detail.input`; unknown, optional) - `ctx` (`add_mind_brain.response.422.detail.ctx`; object, optional) # Retrieve what a mind knows about a query > Everything this mind holds that bears on the query: matched skills, remembered facts, and document excerpts. **This endpoint does not generate text.** All three arms are existing non-generative retrievers, scoped to the mind. The caller does the reasoning — which is the point, since a model is usually already sitting on the other side of this call. Arms fail independently: a dead embedder costs you skills and excerpts, not the memories you could still have had. Source: https://truememory-docs.mvstg.com/api/minds/consult/ · Markdown: https://truememory-docs.mvstg.com/api/minds/consult/index.md Path: AI Gateway API › Minds `POST /minds/{mind_id}/consult` Everything this mind holds that bears on the query: matched skills, remembered facts, and document excerpts. **This endpoint does not generate text.** All three arms are existing non-generative retrievers, scoped to the mind. The caller does the reasoning — which is the point, since a model is usually already sitting on the other side of this call. Arms fail independently: a dead embedder costs you skills and excerpts, not the memories you could still have had. ## Authentication - `HTTPBearer`, http, header `Authorization` ## Path parameters - `mind_id` (`consult.path.mind_id`; string, required) - format `uuid` ## Header parameters - `X-App-Id` (`consult.header.X-App-Id`; any of, optional) - any of: `string`, `null` - `X-On-Behalf-Of` (`consult.header.X-On-Behalf-Of`; any of, optional) - any of: `string`, `null` - `X-External-User-Id` (`consult.header.X-External-User-Id`; any of, optional) - any of: `string`, `null` ## Request body _Required._ - `query` (`consult.query`; string, required) — What to retrieve. Skills are matched against this by embedding similarity, so keep the asker's own wording. - minLength 1; maxLength 5000 - `agent_id` (`consult.agent_id`; string, optional) — Memory scope for the caller's own facts, blended alongside the mind's. - default `"chatbot"` ## Example request ```json { "query": "string", "agent_id": "chatbot" } ``` ## Code samples ### cURL ```bash curl --request POST \ --url 'https://truememory-api.mvstg.com/minds//consult' \ --header 'Authorization: Bearer ' \ --header 'Content-Type: application/json' \ --data ' { "query": "string", "agent_id": "chatbot" } ' ``` ### TypeScript ```typescript const url = 'https://truememory-api.mvstg.com/minds//consult'; const options = { method: 'POST', headers: {'Content-Type': 'application/json', Authorization: 'Bearer '}, body: JSON.stringify({query: 'string', agent_id: 'chatbot'}) }; fetch(url, options) .then(res => res.json()) .then(json => console.log(json)) .catch(err => console.error(err)); ``` ### Python ```python import requests url = "https://truememory-api.mvstg.com/minds//consult" payload = { "query": "string", "agent_id": "chatbot" } headers = { "Content-Type": "application/json", "Authorization": "Bearer " } response = requests.post(url, json=payload, headers=headers) print(response.text) ``` ## Responses ### 200 Successful Response #### Example ```json { "mind_id": "string", "mind": "string", "query": "string", "skills": [ { "key": "string", "name": "string", "instructions": "string", "similarity": 0, "files": [ { "path": "string", "size": 0 } ] } ], "memories": [ { "fact": "string", "when": "string", "source": "string", "scope": "string" } ], "excerpts": [ { "filename": "string", "content": "string" } ] } ``` - `mind_id` (`consult.response.200.mind_id`; string, required) - `mind` (`consult.response.200.mind`; string, required) - `query` (`consult.response.200.query`; string, required) - `skills` (`consult.response.200.skills`; array, optional) - `key` (`consult.response.200.skills.key`; string, required) - `name` (`consult.response.200.skills.name`; string, required) - `instructions` (`consult.response.200.skills.instructions`; string, required) - `similarity` (`consult.response.200.skills.similarity`; number, required) - `files` (`consult.response.200.skills.files`; array, optional) — The skill's other files. Read one with GET /minds/{mind_id}/skills/{key}/files/{path} when the instructions point to it. - `path` (`consult.response.200.skills.files.path`; string, required) - `size` (`consult.response.200.skills.files.size`; integer, required) - `memories` (`consult.response.200.memories`; array, optional) - `fact` (`consult.response.200.memories.fact`; string, required) - `when` (`consult.response.200.memories.when`; any of, optional) - any of: `string`, `null` - `source` (`consult.response.200.memories.source`; any of, optional) - any of: `string`, `null` - `scope` (`consult.response.200.memories.scope`; any of, optional) - any of: `string`, `null` - `excerpts` (`consult.response.200.excerpts`; array, optional) - `filename` (`consult.response.200.excerpts.filename`; string, required) - `content` (`consult.response.200.excerpts.content`; string, required) ### 422 Validation Error #### Example ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string", "input": null, "ctx": {} } ] } ``` - `detail` (`consult.response.422.detail`; array, optional) - `loc` (`consult.response.422.detail.loc`; array, required) - any of: `string`, `integer` - `msg` (`consult.response.422.detail.msg`; string, required) - `type` (`consult.response.422.detail.type`; string, required) - `input` (`consult.response.422.detail.input`; unknown, optional) - `ctx` (`consult.response.422.detail.ctx`; object, optional) # Create a mind > Create a mind owned by the caller. The mind's memory namespace is created implicitly — ``graph_id`` is a generated column, so it cannot drift from the row that names it. Source: https://truememory-docs.mvstg.com/api/minds/create/ · Markdown: https://truememory-docs.mvstg.com/api/minds/create/index.md Path: AI Gateway API › Minds `POST /minds` Create a mind owned by the caller. The mind's memory namespace is created implicitly — ``graph_id`` is a generated column, so it cannot drift from the row that names it. ## Authentication - `HTTPBearer`, http, header `Authorization` ## Header parameters - `X-App-Id` (`create_mind.header.X-App-Id`; any of, optional) - any of: `string`, `null` - `X-On-Behalf-Of` (`create_mind.header.X-On-Behalf-Of`; any of, optional) - any of: `string`, `null` - `X-External-User-Id` (`create_mind.header.X-External-User-Id`; any of, optional) - any of: `string`, `null` ## Request body _Required._ - `name` (`create_mind.name`; string, required) - minLength 1; maxLength 200 - `slug` (`create_mind.slug`; any of, optional) - any of: `string`, `null` - `description` (`create_mind.description`; any of, optional) - any of: `string`, `null` - `emoji` (`create_mind.emoji`; any of, optional) - any of: `string`, `null` - `usage_hint` (`create_mind.usage_hint`; any of, optional) - any of: `string`, `null` - `visibility` (`create_mind.visibility`; string, optional) - one of `"private"`, `"app"`, `"public"`; default `"private"` - `skill_ids` (`create_mind.skill_ids`; array, optional) - format `uuid` ## Example request ```json { "name": "string", "slug": "string", "description": "string", "emoji": "string", "usage_hint": "string", "visibility": "private", "skill_ids": [ "497f6eca-6276-4993-bfeb-53cbbbba6f08" ] } ``` ## Code samples ### cURL ```bash curl --request POST \ --url https://truememory-api.mvstg.com/minds \ --header 'Authorization: Bearer ' \ --header 'Content-Type: application/json' \ --data ' { "name": "string", "slug": "string", "description": "string", "emoji": "string", "usage_hint": "string", "visibility": "private", "skill_ids": [ "497f6eca-6276-4993-bfeb-53cbbbba6f08" ] } ' ``` ### TypeScript ```typescript const url = 'https://truememory-api.mvstg.com/minds'; const options = { method: 'POST', headers: {'Content-Type': 'application/json', Authorization: 'Bearer '}, body: JSON.stringify({ name: 'string', slug: 'string', description: 'string', emoji: 'string', usage_hint: 'string', visibility: 'private', skill_ids: ['497f6eca-6276-4993-bfeb-53cbbbba6f08'] }) }; fetch(url, options) .then(res => res.json()) .then(json => console.log(json)) .catch(err => console.error(err)); ``` ### Python ```python import requests url = "https://truememory-api.mvstg.com/minds" payload = { "name": "string", "slug": "string", "description": "string", "emoji": "string", "usage_hint": "string", "visibility": "private", "skill_ids": ["497f6eca-6276-4993-bfeb-53cbbbba6f08"] } headers = { "Content-Type": "application/json", "Authorization": "Bearer " } response = requests.post(url, json=payload, headers=headers) print(response.text) ``` ## Responses ### 201 Successful Response #### Example ```json { "id": "string", "owner_type": "user", "owner_id": "string", "app_id": "string", "slug": "string", "name": "string", "description": "string", "emoji": "string", "usage_hint": "string", "visibility": "private", "banner_image_url": "string", "skills": [ { "id": "string", "key": "string", "name": "string" } ], "brain_count": 0, "created_at": "2019-08-24T14:15:22Z", "updated_at": "2019-08-24T14:15:22Z", "skill_ids": [ "string" ] } ``` - `id` (`create_mind.response.201.id`; string, required) - `owner_type` (`create_mind.response.201.owner_type`; string, required) - one of `"user"`, `"org"` - `owner_id` (`create_mind.response.201.owner_id`; string, required) - `slug` (`create_mind.response.201.slug`; string, required) - `name` (`create_mind.response.201.name`; string, required) - `created_at` (`create_mind.response.201.created_at`; string, required) - format `date-time` - `updated_at` (`create_mind.response.201.updated_at`; string, required) - format `date-time` - `skill_ids` (`create_mind.response.201.skill_ids`; array, required) — Ids of ``skills``, for clients seeding a multi-select. - `app_id` (`create_mind.response.201.app_id`; any of, optional) - any of: `string`, `null` - `description` (`create_mind.response.201.description`; any of, optional) - any of: `string`, `null` - `emoji` (`create_mind.response.201.emoji`; any of, optional) - any of: `string`, `null` - `usage_hint` (`create_mind.response.201.usage_hint`; any of, optional) - any of: `string`, `null` - `visibility` (`create_mind.response.201.visibility`; string, optional) - one of `"private"`, `"app"`, `"public"`; default `"private"` - `banner_image_url` (`create_mind.response.201.banner_image_url`; any of, optional) - any of: `string`, `null` - `skills` (`create_mind.response.201.skills`; array, optional) - `id` (`create_mind.response.201.skills.id`; string, required) - `key` (`create_mind.response.201.skills.key`; string, required) - `name` (`create_mind.response.201.skills.name`; string, required) - `brain_count` (`create_mind.response.201.brain_count`; integer, optional) - default `0` ### 422 Validation Error #### Example ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string", "input": null, "ctx": {} } ] } ``` - `detail` (`create_mind.response.422.detail`; array, optional) - `loc` (`create_mind.response.422.detail.loc`; array, required) - any of: `string`, `integer` - `msg` (`create_mind.response.422.detail.msg`; string, required) - `type` (`create_mind.response.422.detail.type`; string, required) - `input` (`create_mind.response.422.detail.input`; unknown, optional) - `ctx` (`create_mind.response.422.detail.ctx`; object, optional) # Delete a mind banner > Remove the mind's banner and delete the stored asset. Returns the updated mind. Source: https://truememory-docs.mvstg.com/api/minds/delete-banner/ · Markdown: https://truememory-docs.mvstg.com/api/minds/delete-banner/index.md Path: AI Gateway API › Minds `DELETE /minds/{mind_id}/banner` Remove the mind's banner and delete the stored asset. Returns the updated mind. ## Authentication - `HTTPBearer`, http, header `Authorization` ## Path parameters - `mind_id` (`delete_mind_banner.path.mind_id`; string, required) - format `uuid` ## Header parameters - `X-App-Id` (`delete_mind_banner.header.X-App-Id`; any of, optional) - any of: `string`, `null` - `X-On-Behalf-Of` (`delete_mind_banner.header.X-On-Behalf-Of`; any of, optional) - any of: `string`, `null` - `X-External-User-Id` (`delete_mind_banner.header.X-External-User-Id`; any of, optional) - any of: `string`, `null` ## Code samples ### cURL ```bash curl --request DELETE \ --url 'https://truememory-api.mvstg.com/minds//banner' \ --header 'Authorization: Bearer ' ``` ### TypeScript ```typescript const url = 'https://truememory-api.mvstg.com/minds//banner'; const options = {method: 'DELETE', headers: {Authorization: 'Bearer '}}; fetch(url, options) .then(res => res.json()) .then(json => console.log(json)) .catch(err => console.error(err)); ``` ### Python ```python import requests url = "https://truememory-api.mvstg.com/minds//banner" headers = {"Authorization": "Bearer "} response = requests.delete(url, headers=headers) print(response.text) ``` ## Responses ### 200 Successful Response #### Example ```json { "id": "string", "owner_type": "user", "owner_id": "string", "app_id": "string", "slug": "string", "name": "string", "description": "string", "emoji": "string", "usage_hint": "string", "visibility": "private", "banner_image_url": "string", "skills": [ { "id": "string", "key": "string", "name": "string" } ], "brain_count": 0, "created_at": "2019-08-24T14:15:22Z", "updated_at": "2019-08-24T14:15:22Z", "skill_ids": [ "string" ] } ``` - `id` (`delete_mind_banner.response.200.id`; string, required) - `owner_type` (`delete_mind_banner.response.200.owner_type`; string, required) - one of `"user"`, `"org"` - `owner_id` (`delete_mind_banner.response.200.owner_id`; string, required) - `slug` (`delete_mind_banner.response.200.slug`; string, required) - `name` (`delete_mind_banner.response.200.name`; string, required) - `created_at` (`delete_mind_banner.response.200.created_at`; string, required) - format `date-time` - `updated_at` (`delete_mind_banner.response.200.updated_at`; string, required) - format `date-time` - `skill_ids` (`delete_mind_banner.response.200.skill_ids`; array, required) — Ids of ``skills``, for clients seeding a multi-select. - `app_id` (`delete_mind_banner.response.200.app_id`; any of, optional) - any of: `string`, `null` - `description` (`delete_mind_banner.response.200.description`; any of, optional) - any of: `string`, `null` - `emoji` (`delete_mind_banner.response.200.emoji`; any of, optional) - any of: `string`, `null` - `usage_hint` (`delete_mind_banner.response.200.usage_hint`; any of, optional) - any of: `string`, `null` - `visibility` (`delete_mind_banner.response.200.visibility`; string, optional) - one of `"private"`, `"app"`, `"public"`; default `"private"` - `banner_image_url` (`delete_mind_banner.response.200.banner_image_url`; any of, optional) - any of: `string`, `null` - `skills` (`delete_mind_banner.response.200.skills`; array, optional) - `id` (`delete_mind_banner.response.200.skills.id`; string, required) - `key` (`delete_mind_banner.response.200.skills.key`; string, required) - `name` (`delete_mind_banner.response.200.skills.name`; string, required) - `brain_count` (`delete_mind_banner.response.200.brain_count`; integer, optional) - default `0` ### 422 Validation Error #### Example ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string", "input": null, "ctx": {} } ] } ``` - `detail` (`delete_mind_banner.response.422.detail`; array, optional) - `loc` (`delete_mind_banner.response.422.detail.loc`; array, required) - any of: `string`, `integer` - `msg` (`delete_mind_banner.response.422.detail.msg`; string, required) - `type` (`delete_mind_banner.response.422.detail.type`; string, required) - `input` (`delete_mind_banner.response.422.detail.input`; unknown, optional) - `ctx` (`delete_mind_banner.response.422.detail.ctx`; object, optional) # Delete a mind > Soft-delete a mind. Its memories are kept and can be restored. The mind stops being readable immediately, and so does everything it reached — brains and skills shared *through* this mind go with it, because ``mind_readable`` is what those containment arms compose over. Source: https://truememory-docs.mvstg.com/api/minds/delete/ · Markdown: https://truememory-docs.mvstg.com/api/minds/delete/index.md Path: AI Gateway API › Minds `DELETE /minds/{mind_id}` Soft-delete a mind. Its memories are kept and can be restored. The mind stops being readable immediately, and so does everything it reached — brains and skills shared *through* this mind go with it, because ``mind_readable`` is what those containment arms compose over. ## Authentication - `HTTPBearer`, http, header `Authorization` ## Path parameters - `mind_id` (`delete_mind.path.mind_id`; string, required) - format `uuid` ## Header parameters - `X-App-Id` (`delete_mind.header.X-App-Id`; any of, optional) - any of: `string`, `null` - `X-On-Behalf-Of` (`delete_mind.header.X-On-Behalf-Of`; any of, optional) - any of: `string`, `null` - `X-External-User-Id` (`delete_mind.header.X-External-User-Id`; any of, optional) - any of: `string`, `null` ## Code samples ### cURL ```bash curl --request DELETE \ --url 'https://truememory-api.mvstg.com/minds/' \ --header 'Authorization: Bearer ' ``` ### TypeScript ```typescript const url = 'https://truememory-api.mvstg.com/minds/'; const options = {method: 'DELETE', headers: {Authorization: 'Bearer '}}; fetch(url, options) .then(res => res.json()) .then(json => console.log(json)) .catch(err => console.error(err)); ``` ### Python ```python import requests url = "https://truememory-api.mvstg.com/minds/" headers = {"Authorization": "Bearer "} response = requests.delete(url, headers=headers) print(response.text) ``` ## Responses ### 204 Successful Response ### 422 Validation Error #### Example ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string", "input": null, "ctx": {} } ] } ``` - `detail` (`delete_mind.response.422.detail`; array, optional) - `loc` (`delete_mind.response.422.detail.loc`; array, required) - any of: `string`, `integer` - `msg` (`delete_mind.response.422.detail.msg`; string, required) - `type` (`delete_mind.response.422.detail.type`; string, required) - `input` (`delete_mind.response.422.detail.input`; unknown, optional) - `ctx` (`delete_mind.response.422.detail.ctx`; object, optional) # Get a mind > Fetch one mind with its skills. 404 if you cannot reach it. Source: https://truememory-docs.mvstg.com/api/minds/get/ · Markdown: https://truememory-docs.mvstg.com/api/minds/get/index.md Path: AI Gateway API › Minds `GET /minds/{mind_id}` Fetch one mind with its skills. 404 if you cannot reach it. ## Authentication - `HTTPBearer`, http, header `Authorization` ## Path parameters - `mind_id` (`get_mind.path.mind_id`; string, required) - format `uuid` ## Header parameters - `X-App-Id` (`get_mind.header.X-App-Id`; any of, optional) - any of: `string`, `null` - `X-On-Behalf-Of` (`get_mind.header.X-On-Behalf-Of`; any of, optional) - any of: `string`, `null` - `X-External-User-Id` (`get_mind.header.X-External-User-Id`; any of, optional) - any of: `string`, `null` ## Code samples ### cURL ```bash curl --request GET \ --url 'https://truememory-api.mvstg.com/minds/' \ --header 'Authorization: Bearer ' ``` ### TypeScript ```typescript const url = 'https://truememory-api.mvstg.com/minds/'; const options = {method: 'GET', headers: {Authorization: 'Bearer '}}; fetch(url, options) .then(res => res.json()) .then(json => console.log(json)) .catch(err => console.error(err)); ``` ### Python ```python import requests url = "https://truememory-api.mvstg.com/minds/" headers = {"Authorization": "Bearer "} response = requests.get(url, headers=headers) print(response.text) ``` ## Responses ### 200 Successful Response #### Example ```json { "id": "string", "owner_type": "user", "owner_id": "string", "app_id": "string", "slug": "string", "name": "string", "description": "string", "emoji": "string", "usage_hint": "string", "visibility": "private", "banner_image_url": "string", "skills": [ { "id": "string", "key": "string", "name": "string" } ], "brain_count": 0, "created_at": "2019-08-24T14:15:22Z", "updated_at": "2019-08-24T14:15:22Z", "skill_ids": [ "string" ] } ``` - `id` (`get_mind.response.200.id`; string, required) - `owner_type` (`get_mind.response.200.owner_type`; string, required) - one of `"user"`, `"org"` - `owner_id` (`get_mind.response.200.owner_id`; string, required) - `slug` (`get_mind.response.200.slug`; string, required) - `name` (`get_mind.response.200.name`; string, required) - `created_at` (`get_mind.response.200.created_at`; string, required) - format `date-time` - `updated_at` (`get_mind.response.200.updated_at`; string, required) - format `date-time` - `skill_ids` (`get_mind.response.200.skill_ids`; array, required) — Ids of ``skills``, for clients seeding a multi-select. - `app_id` (`get_mind.response.200.app_id`; any of, optional) - any of: `string`, `null` - `description` (`get_mind.response.200.description`; any of, optional) - any of: `string`, `null` - `emoji` (`get_mind.response.200.emoji`; any of, optional) - any of: `string`, `null` - `usage_hint` (`get_mind.response.200.usage_hint`; any of, optional) - any of: `string`, `null` - `visibility` (`get_mind.response.200.visibility`; string, optional) - one of `"private"`, `"app"`, `"public"`; default `"private"` - `banner_image_url` (`get_mind.response.200.banner_image_url`; any of, optional) - any of: `string`, `null` - `skills` (`get_mind.response.200.skills`; array, optional) - `id` (`get_mind.response.200.skills.id`; string, required) - `key` (`get_mind.response.200.skills.key`; string, required) - `name` (`get_mind.response.200.skills.name`; string, required) - `brain_count` (`get_mind.response.200.brain_count`; integer, optional) - default `0` ### 422 Validation Error #### Example ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string", "input": null, "ctx": {} } ] } ``` - `detail` (`get_mind.response.422.detail`; array, optional) - `loc` (`get_mind.response.422.detail.loc`; array, required) - any of: `string`, `integer` - `msg` (`get_mind.response.422.detail.msg`; string, required) - `type` (`get_mind.response.422.detail.type`; string, required) - `input` (`get_mind.response.422.detail.input`; unknown, optional) - `ctx` (`get_mind.response.422.detail.ctx`; object, optional) # Share a mind with a user > Give one user access to this mind. Re-granting changes their role. Self-service: the owner, or an admin the owner appointed. **Only the owner may grant `admin`** — an admin sharing onward as admin would let write access propagate with no step at which the owner is consulted. A `viewer` reads the mind and everything it reaches: the brains it holds and the skills it carries. That is the point of sharing a mind, and it is a wider grant than sharing a brain — the skills become instructions in the recipient's context. Share deliberately. Source: https://truememory-docs.mvstg.com/api/minds/grants/create/ · Markdown: https://truememory-docs.mvstg.com/api/minds/grants/create/index.md Path: AI Gateway API › Minds `POST /minds/{mind_id}/grants` Give one user access to this mind. Re-granting changes their role. Self-service: the owner, or an admin the owner appointed. **Only the owner may grant `admin`** — an admin sharing onward as admin would let write access propagate with no step at which the owner is consulted. A `viewer` reads the mind and everything it reaches: the brains it holds and the skills it carries. That is the point of sharing a mind, and it is a wider grant than sharing a brain — the skills become instructions in the recipient's context. Share deliberately. ## Authentication - `HTTPBearer`, http, header `Authorization` ## Path parameters - `mind_id` (`grant_mind_access.path.mind_id`; string, required) - format `uuid` ## Header parameters - `X-App-Id` (`grant_mind_access.header.X-App-Id`; any of, optional) - any of: `string`, `null` - `X-On-Behalf-Of` (`grant_mind_access.header.X-On-Behalf-Of`; any of, optional) - any of: `string`, `null` - `X-External-User-Id` (`grant_mind_access.header.X-External-User-Id`; any of, optional) - any of: `string`, `null` ## Request body _Required._ - `user_id` (`grant_mind_access.user_id`; string, required) - minLength 1; maxLength 255 - `role` (`grant_mind_access.role`; string, optional) - one of `"viewer"`, `"admin"`; default `"viewer"` ## Example request ```json { "user_id": "string", "role": "viewer" } ``` ## Code samples ### cURL ```bash curl --request POST \ --url 'https://truememory-api.mvstg.com/minds//grants' \ --header 'Authorization: Bearer ' \ --header 'Content-Type: application/json' \ --data ' { "user_id": "string", "role": "viewer" } ' ``` ### TypeScript ```typescript const url = 'https://truememory-api.mvstg.com/minds//grants'; const options = { method: 'POST', headers: {'Content-Type': 'application/json', Authorization: 'Bearer '}, body: JSON.stringify({user_id: 'string', role: 'viewer'}) }; fetch(url, options) .then(res => res.json()) .then(json => console.log(json)) .catch(err => console.error(err)); ``` ### Python ```python import requests url = "https://truememory-api.mvstg.com/minds//grants" payload = { "user_id": "string", "role": "viewer" } headers = { "Content-Type": "application/json", "Authorization": "Bearer " } response = requests.post(url, json=payload, headers=headers) print(response.text) ``` ## Responses ### 201 Successful Response #### Example ```json { "mind_id": "string", "user_id": "string", "role": "viewer", "app_id": "string", "granted_at": "2019-08-24T14:15:22Z", "display_name": "string", "email": "string" } ``` - `mind_id` (`grant_mind_access.response.201.mind_id`; string, required) - `user_id` (`grant_mind_access.response.201.user_id`; string, required) - `role` (`grant_mind_access.response.201.role`; string, required) - one of `"viewer"`, `"admin"` - `granted_at` (`grant_mind_access.response.201.granted_at`; string, required) - format `date-time` - `app_id` (`grant_mind_access.response.201.app_id`; any of, optional) - any of: `string`, `null` - `display_name` (`grant_mind_access.response.201.display_name`; any of, optional) - any of: `string`, `null` - `email` (`grant_mind_access.response.201.email`; any of, optional) - any of: `string`, `null` ### 422 Validation Error #### Example ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string", "input": null, "ctx": {} } ] } ``` - `detail` (`grant_mind_access.response.422.detail`; array, optional) - `loc` (`grant_mind_access.response.422.detail.loc`; array, required) - any of: `string`, `integer` - `msg` (`grant_mind_access.response.422.detail.msg`; string, required) - `type` (`grant_mind_access.response.422.detail.type`; string, required) - `input` (`grant_mind_access.response.422.detail.input`; unknown, optional) - `ctx` (`grant_mind_access.response.422.detail.ctx`; object, optional) # Revoke a user's access to a mind > Withdraw access. An admin may revoke a viewer but not another admin; only the owner can do that. Source: https://truememory-docs.mvstg.com/api/minds/grants/delete/ · Markdown: https://truememory-docs.mvstg.com/api/minds/grants/delete/index.md Path: AI Gateway API › Minds `DELETE /minds/{mind_id}/grants/{user_id}` Withdraw access. An admin may revoke a viewer but not another admin; only the owner can do that. ## Authentication - `HTTPBearer`, http, header `Authorization` ## Path parameters - `mind_id` (`revoke_mind_access.path.mind_id`; string, required) - format `uuid` - `user_id` (`revoke_mind_access.path.user_id`; string, required) ## Header parameters - `X-App-Id` (`revoke_mind_access.header.X-App-Id`; any of, optional) - any of: `string`, `null` - `X-On-Behalf-Of` (`revoke_mind_access.header.X-On-Behalf-Of`; any of, optional) - any of: `string`, `null` - `X-External-User-Id` (`revoke_mind_access.header.X-External-User-Id`; any of, optional) - any of: `string`, `null` ## Code samples ### cURL ```bash curl --request DELETE \ --url 'https://truememory-api.mvstg.com/minds//grants/' \ --header 'Authorization: Bearer ' ``` ### TypeScript ```typescript const url = 'https://truememory-api.mvstg.com/minds//grants/'; const options = {method: 'DELETE', headers: {Authorization: 'Bearer '}}; fetch(url, options) .then(res => res.json()) .then(json => console.log(json)) .catch(err => console.error(err)); ``` ### Python ```python import requests url = "https://truememory-api.mvstg.com/minds//grants/" headers = {"Authorization": "Bearer "} response = requests.delete(url, headers=headers) print(response.text) ``` ## Responses ### 204 Successful Response ### 422 Validation Error #### Example ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string", "input": null, "ctx": {} } ] } ``` - `detail` (`revoke_mind_access.response.422.detail`; array, optional) - `loc` (`revoke_mind_access.response.422.detail.loc`; array, required) - any of: `string`, `integer` - `msg` (`revoke_mind_access.response.422.detail.msg`; string, required) - `type` (`revoke_mind_access.response.422.detail.type`; string, required) - `input` (`revoke_mind_access.response.422.detail.input`; unknown, optional) - `ctx` (`revoke_mind_access.response.422.detail.ctx`; object, optional) # List who a mind is shared with > Readable by anyone who can read the mind — knowing who else is in a shared collection is part of using it. Source: https://truememory-docs.mvstg.com/api/minds/grants/list/ · Markdown: https://truememory-docs.mvstg.com/api/minds/grants/list/index.md Path: AI Gateway API › Minds `GET /minds/{mind_id}/grants` Readable by anyone who can read the mind — knowing who else is in a shared collection is part of using it. ## Authentication - `HTTPBearer`, http, header `Authorization` ## Path parameters - `mind_id` (`list_mind_grants.path.mind_id`; string, required) - format `uuid` ## Header parameters - `X-App-Id` (`list_mind_grants.header.X-App-Id`; any of, optional) - any of: `string`, `null` - `X-On-Behalf-Of` (`list_mind_grants.header.X-On-Behalf-Of`; any of, optional) - any of: `string`, `null` - `X-External-User-Id` (`list_mind_grants.header.X-External-User-Id`; any of, optional) - any of: `string`, `null` ## Code samples ### cURL ```bash curl --request GET \ --url 'https://truememory-api.mvstg.com/minds//grants' \ --header 'Authorization: Bearer ' ``` ### TypeScript ```typescript const url = 'https://truememory-api.mvstg.com/minds//grants'; const options = {method: 'GET', headers: {Authorization: 'Bearer '}}; fetch(url, options) .then(res => res.json()) .then(json => console.log(json)) .catch(err => console.error(err)); ``` ### Python ```python import requests url = "https://truememory-api.mvstg.com/minds//grants" headers = {"Authorization": "Bearer "} response = requests.get(url, headers=headers) print(response.text) ``` ## Responses ### 200 Successful Response #### Example ```json [ { "mind_id": "string", "user_id": "string", "role": "viewer", "app_id": "string", "granted_at": "2019-08-24T14:15:22Z", "display_name": "string", "email": "string" } ] ``` - `mind_id` (`list_mind_grants.response.200.mind_id`; string, required) - `user_id` (`list_mind_grants.response.200.user_id`; string, required) - `role` (`list_mind_grants.response.200.role`; string, required) - one of `"viewer"`, `"admin"` - `granted_at` (`list_mind_grants.response.200.granted_at`; string, required) - format `date-time` - `app_id` (`list_mind_grants.response.200.app_id`; any of, optional) - any of: `string`, `null` - `display_name` (`list_mind_grants.response.200.display_name`; any of, optional) - any of: `string`, `null` - `email` (`list_mind_grants.response.200.email`; any of, optional) - any of: `string`, `null` ### 422 Validation Error #### Example ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string", "input": null, "ctx": {} } ] } ``` - `detail` (`list_mind_grants.response.422.detail`; array, optional) - `loc` (`list_mind_grants.response.422.detail.loc`; array, required) - any of: `string`, `integer` - `msg` (`list_mind_grants.response.422.detail.msg`; string, required) - `type` (`list_mind_grants.response.422.detail.type`; string, required) - `input` (`list_mind_grants.response.422.detail.input`; unknown, optional) - `ctx` (`list_mind_grants.response.422.detail.ctx`; object, optional) # List minds > Every mind the caller can read — owned, shared with them, or visible to their app. Deleted minds are excluded. Source: https://truememory-docs.mvstg.com/api/minds/list/ · Markdown: https://truememory-docs.mvstg.com/api/minds/list/index.md Path: AI Gateway API › Minds `GET /minds` Every mind the caller can read — owned, shared with them, or visible to their app. Deleted minds are excluded. ## Authentication - `HTTPBearer`, http, header `Authorization` ## Query parameters - `limit` (`list_minds.query.limit`; integer, optional) - min 1; max 200; default `50` - `offset` (`list_minds.query.offset`; integer, optional) - min 0; default `0` ## Header parameters - `X-App-Id` (`list_minds.header.X-App-Id`; any of, optional) - any of: `string`, `null` - `X-On-Behalf-Of` (`list_minds.header.X-On-Behalf-Of`; any of, optional) - any of: `string`, `null` - `X-External-User-Id` (`list_minds.header.X-External-User-Id`; any of, optional) - any of: `string`, `null` ## Code samples ### cURL ```bash curl --request GET \ --url https://truememory-api.mvstg.com/minds \ --header 'Authorization: Bearer ' ``` ### TypeScript ```typescript const url = 'https://truememory-api.mvstg.com/minds'; const options = {method: 'GET', headers: {Authorization: 'Bearer '}}; fetch(url, options) .then(res => res.json()) .then(json => console.log(json)) .catch(err => console.error(err)); ``` ### Python ```python import requests url = "https://truememory-api.mvstg.com/minds" headers = {"Authorization": "Bearer "} response = requests.get(url, headers=headers) print(response.text) ``` ## Responses ### 200 Successful Response #### Example ```json { "minds": [ { "id": "string", "owner_type": "user", "owner_id": "string", "app_id": "string", "slug": "string", "name": "string", "description": "string", "emoji": "string", "usage_hint": "string", "visibility": "private", "banner_image_url": "string", "skills": [ { "id": "string", "key": "string", "name": "string" } ], "brain_count": 0, "created_at": "2019-08-24T14:15:22Z", "updated_at": "2019-08-24T14:15:22Z", "skill_ids": [ "string" ] } ] } ``` - `minds` (`list_minds.response.200.minds`; array, required) - `id` (`list_minds.response.200.minds.id`; string, required) - `owner_type` (`list_minds.response.200.minds.owner_type`; string, required) - one of `"user"`, `"org"` - `owner_id` (`list_minds.response.200.minds.owner_id`; string, required) - `slug` (`list_minds.response.200.minds.slug`; string, required) - `name` (`list_minds.response.200.minds.name`; string, required) - `created_at` (`list_minds.response.200.minds.created_at`; string, required) - format `date-time` - `updated_at` (`list_minds.response.200.minds.updated_at`; string, required) - format `date-time` - `skill_ids` (`list_minds.response.200.minds.skill_ids`; array, required) — Ids of ``skills``, for clients seeding a multi-select. - `app_id` (`list_minds.response.200.minds.app_id`; any of, optional) - any of: `string`, `null` - `description` (`list_minds.response.200.minds.description`; any of, optional) - any of: `string`, `null` - `emoji` (`list_minds.response.200.minds.emoji`; any of, optional) - any of: `string`, `null` - `usage_hint` (`list_minds.response.200.minds.usage_hint`; any of, optional) - any of: `string`, `null` - `visibility` (`list_minds.response.200.minds.visibility`; string, optional) - one of `"private"`, `"app"`, `"public"`; default `"private"` - `banner_image_url` (`list_minds.response.200.minds.banner_image_url`; any of, optional) - any of: `string`, `null` - `skills` (`list_minds.response.200.minds.skills`; array, optional) - `id` (`list_minds.response.200.minds.skills.id`; string, required) - `key` (`list_minds.response.200.minds.skills.key`; string, required) - `name` (`list_minds.response.200.minds.skills.name`; string, required) - `brain_count` (`list_minds.response.200.minds.brain_count`; integer, optional) - default `0` ### 422 Validation Error #### Example ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string", "input": null, "ctx": {} } ] } ``` - `detail` (`list_minds.response.422.detail`; array, optional) - `loc` (`list_minds.response.422.detail.loc`; array, required) - any of: `string`, `integer` - `msg` (`list_minds.response.422.detail.msg`; string, required) - `type` (`list_minds.response.422.detail.type`; string, required) - `input` (`list_minds.response.422.detail.input`; unknown, optional) - `ctx` (`list_minds.response.422.detail.ctx`; object, optional) # Restore a deleted mind > Bring back a soft-deleted mind. **Owner only** — an admin grant does not carry, because undeleting is a decision about the owner's own namespace. 409 if a live mind has taken the slug in the meantime. Source: https://truememory-docs.mvstg.com/api/minds/restore/ · Markdown: https://truememory-docs.mvstg.com/api/minds/restore/index.md Path: AI Gateway API › Minds `POST /minds/{mind_id}/restore` Bring back a soft-deleted mind. **Owner only** — an admin grant does not carry, because undeleting is a decision about the owner's own namespace. 409 if a live mind has taken the slug in the meantime. ## Authentication - `HTTPBearer`, http, header `Authorization` ## Path parameters - `mind_id` (`restore_mind.path.mind_id`; string, required) - format `uuid` ## Header parameters - `X-On-Behalf-Of` (`restore_mind.header.X-On-Behalf-Of`; any of, optional) - any of: `string`, `null` - `X-External-User-Id` (`restore_mind.header.X-External-User-Id`; any of, optional) - any of: `string`, `null` - `X-App-Id` (`restore_mind.header.X-App-Id`; any of, optional) - any of: `string`, `null` ## Code samples ### cURL ```bash curl --request POST \ --url 'https://truememory-api.mvstg.com/minds//restore' \ --header 'Authorization: Bearer ' ``` ### TypeScript ```typescript const url = 'https://truememory-api.mvstg.com/minds//restore'; const options = {method: 'POST', headers: {Authorization: 'Bearer '}}; fetch(url, options) .then(res => res.json()) .then(json => console.log(json)) .catch(err => console.error(err)); ``` ### Python ```python import requests url = "https://truememory-api.mvstg.com/minds//restore" headers = {"Authorization": "Bearer "} response = requests.post(url, headers=headers) print(response.text) ``` ## Responses ### 200 Successful Response #### Example ```json { "id": "string", "owner_type": "user", "owner_id": "string", "app_id": "string", "slug": "string", "name": "string", "description": "string", "emoji": "string", "usage_hint": "string", "visibility": "private", "banner_image_url": "string", "skills": [ { "id": "string", "key": "string", "name": "string" } ], "brain_count": 0, "created_at": "2019-08-24T14:15:22Z", "updated_at": "2019-08-24T14:15:22Z", "skill_ids": [ "string" ] } ``` - `id` (`restore_mind.response.200.id`; string, required) - `owner_type` (`restore_mind.response.200.owner_type`; string, required) - one of `"user"`, `"org"` - `owner_id` (`restore_mind.response.200.owner_id`; string, required) - `slug` (`restore_mind.response.200.slug`; string, required) - `name` (`restore_mind.response.200.name`; string, required) - `created_at` (`restore_mind.response.200.created_at`; string, required) - format `date-time` - `updated_at` (`restore_mind.response.200.updated_at`; string, required) - format `date-time` - `skill_ids` (`restore_mind.response.200.skill_ids`; array, required) — Ids of ``skills``, for clients seeding a multi-select. - `app_id` (`restore_mind.response.200.app_id`; any of, optional) - any of: `string`, `null` - `description` (`restore_mind.response.200.description`; any of, optional) - any of: `string`, `null` - `emoji` (`restore_mind.response.200.emoji`; any of, optional) - any of: `string`, `null` - `usage_hint` (`restore_mind.response.200.usage_hint`; any of, optional) - any of: `string`, `null` - `visibility` (`restore_mind.response.200.visibility`; string, optional) - one of `"private"`, `"app"`, `"public"`; default `"private"` - `banner_image_url` (`restore_mind.response.200.banner_image_url`; any of, optional) - any of: `string`, `null` - `skills` (`restore_mind.response.200.skills`; array, optional) - `id` (`restore_mind.response.200.skills.id`; string, required) - `key` (`restore_mind.response.200.skills.key`; string, required) - `name` (`restore_mind.response.200.skills.name`; string, required) - `brain_count` (`restore_mind.response.200.brain_count`; integer, optional) - default `0` ### 422 Validation Error #### Example ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string", "input": null, "ctx": {} } ] } ``` - `detail` (`restore_mind.response.422.detail`; array, optional) - `loc` (`restore_mind.response.422.detail.loc`; array, required) - any of: `string`, `integer` - `msg` (`restore_mind.response.422.detail.msg`; string, required) - `type` (`restore_mind.response.422.detail.type`; string, required) - `input` (`restore_mind.response.422.detail.input`; unknown, optional) - `ctx` (`restore_mind.response.422.detail.ctx`; object, optional) # Read a file of a skill in a mind > One file of a skill this mind carries, for anyone who may read the mind. The read path for someone who does not own the skill: a grantee gets the file list from ``consult`` and the bytes here. The skill must be one the mind carries, so naming any other key is a 404 however readable that skill is elsewhere. Source: https://truememory-docs.mvstg.com/api/minds/skills/files/get/ · Markdown: https://truememory-docs.mvstg.com/api/minds/skills/files/get/index.md Path: AI Gateway API › Minds `GET /minds/{mind_id}/skills/{skill_key}/files/{file_path}` One file of a skill this mind carries, for anyone who may read the mind. The read path for someone who does not own the skill: a grantee gets the file list from ``consult`` and the bytes here. The skill must be one the mind carries, so naming any other key is a 404 however readable that skill is elsewhere. ## Authentication - `HTTPBearer`, http, header `Authorization` ## Path parameters - `mind_id` (`read_mind_skill_file.path.mind_id`; string, required) - format `uuid` - `skill_key` (`read_mind_skill_file.path.skill_key`; string, required) - `file_path` (`read_mind_skill_file.path.file_path`; string, required) ## Header parameters - `X-App-Id` (`read_mind_skill_file.header.X-App-Id`; any of, optional) - any of: `string`, `null` - `X-On-Behalf-Of` (`read_mind_skill_file.header.X-On-Behalf-Of`; any of, optional) - any of: `string`, `null` - `X-External-User-Id` (`read_mind_skill_file.header.X-External-User-Id`; any of, optional) - any of: `string`, `null` ## Code samples ### cURL ```bash curl --request GET \ --url 'https://truememory-api.mvstg.com/minds//skills//files/' \ --header 'Authorization: Bearer ' ``` ### TypeScript ```typescript const url = 'https://truememory-api.mvstg.com/minds//skills//files/'; const options = {method: 'GET', headers: {Authorization: 'Bearer '}}; fetch(url, options) .then(res => res.json()) .then(json => console.log(json)) .catch(err => console.error(err)); ``` ### Python ```python import requests url = "https://truememory-api.mvstg.com/minds//skills//files/" headers = {"Authorization": "Bearer "} response = requests.get(url, headers=headers) print(response.text) ``` ## Responses ### 200 The file's bytes. ### 422 Validation Error #### Example ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string", "input": null, "ctx": {} } ] } ``` - `detail` (`read_mind_skill_file.response.422.detail`; array, optional) - `loc` (`read_mind_skill_file.response.422.detail.loc`; array, required) - any of: `string`, `integer` - `msg` (`read_mind_skill_file.response.422.detail.msg`; string, required) - `type` (`read_mind_skill_file.response.422.detail.type`; string, required) - `input` (`read_mind_skill_file.response.422.detail.input`; unknown, optional) - `ctx` (`read_mind_skill_file.response.422.detail.ctx`; object, optional) # Replace a mind's skills > Set the mind's skill tags to exactly this list. A mind may only carry its **owner's** skills; naming anyone else's is a 404 that says which, rather than a silent drop — a skill the caller believes they tagged, which never reaches a prompt, produces a confident answer under the wrong instructions. Source: https://truememory-docs.mvstg.com/api/minds/skills/update/ · Markdown: https://truememory-docs.mvstg.com/api/minds/skills/update/index.md Path: AI Gateway API › Minds `PUT /minds/{mind_id}/skills` Set the mind's skill tags to exactly this list. A mind may only carry its **owner's** skills; naming anyone else's is a 404 that says which, rather than a silent drop — a skill the caller believes they tagged, which never reaches a prompt, produces a confident answer under the wrong instructions. ## Authentication - `HTTPBearer`, http, header `Authorization` ## Path parameters - `mind_id` (`replace_mind_skills.path.mind_id`; string, required) - format `uuid` ## Header parameters - `X-App-Id` (`replace_mind_skills.header.X-App-Id`; any of, optional) - any of: `string`, `null` - `X-On-Behalf-Of` (`replace_mind_skills.header.X-On-Behalf-Of`; any of, optional) - any of: `string`, `null` - `X-External-User-Id` (`replace_mind_skills.header.X-External-User-Id`; any of, optional) - any of: `string`, `null` ## Request body _Required._ - `skill_ids` (`replace_mind_skills.skill_ids`; array, optional) - format `uuid` ## Example request ```json { "skill_ids": [ "497f6eca-6276-4993-bfeb-53cbbbba6f08" ] } ``` ## Code samples ### cURL ```bash curl --request PUT \ --url 'https://truememory-api.mvstg.com/minds//skills' \ --header 'Authorization: Bearer ' \ --header 'Content-Type: application/json' \ --data ' { "skill_ids": [ "497f6eca-6276-4993-bfeb-53cbbbba6f08" ] } ' ``` ### TypeScript ```typescript const url = 'https://truememory-api.mvstg.com/minds//skills'; const options = { method: 'PUT', headers: {'Content-Type': 'application/json', Authorization: 'Bearer '}, body: JSON.stringify({skill_ids: ['497f6eca-6276-4993-bfeb-53cbbbba6f08']}) }; fetch(url, options) .then(res => res.json()) .then(json => console.log(json)) .catch(err => console.error(err)); ``` ### Python ```python import requests url = "https://truememory-api.mvstg.com/minds//skills" payload = { "skill_ids": ["497f6eca-6276-4993-bfeb-53cbbbba6f08"] } headers = { "Content-Type": "application/json", "Authorization": "Bearer " } response = requests.put(url, json=payload, headers=headers) print(response.text) ``` ## Responses ### 200 Successful Response #### Example ```json { "id": "string", "owner_type": "user", "owner_id": "string", "app_id": "string", "slug": "string", "name": "string", "description": "string", "emoji": "string", "usage_hint": "string", "visibility": "private", "banner_image_url": "string", "skills": [ { "id": "string", "key": "string", "name": "string" } ], "brain_count": 0, "created_at": "2019-08-24T14:15:22Z", "updated_at": "2019-08-24T14:15:22Z", "skill_ids": [ "string" ] } ``` - `id` (`replace_mind_skills.response.200.id`; string, required) - `owner_type` (`replace_mind_skills.response.200.owner_type`; string, required) - one of `"user"`, `"org"` - `owner_id` (`replace_mind_skills.response.200.owner_id`; string, required) - `slug` (`replace_mind_skills.response.200.slug`; string, required) - `name` (`replace_mind_skills.response.200.name`; string, required) - `created_at` (`replace_mind_skills.response.200.created_at`; string, required) - format `date-time` - `updated_at` (`replace_mind_skills.response.200.updated_at`; string, required) - format `date-time` - `skill_ids` (`replace_mind_skills.response.200.skill_ids`; array, required) — Ids of ``skills``, for clients seeding a multi-select. - `app_id` (`replace_mind_skills.response.200.app_id`; any of, optional) - any of: `string`, `null` - `description` (`replace_mind_skills.response.200.description`; any of, optional) - any of: `string`, `null` - `emoji` (`replace_mind_skills.response.200.emoji`; any of, optional) - any of: `string`, `null` - `usage_hint` (`replace_mind_skills.response.200.usage_hint`; any of, optional) - any of: `string`, `null` - `visibility` (`replace_mind_skills.response.200.visibility`; string, optional) - one of `"private"`, `"app"`, `"public"`; default `"private"` - `banner_image_url` (`replace_mind_skills.response.200.banner_image_url`; any of, optional) - any of: `string`, `null` - `skills` (`replace_mind_skills.response.200.skills`; array, optional) - `id` (`replace_mind_skills.response.200.skills.id`; string, required) - `key` (`replace_mind_skills.response.200.skills.key`; string, required) - `name` (`replace_mind_skills.response.200.skills.name`; string, required) - `brain_count` (`replace_mind_skills.response.200.brain_count`; integer, optional) - default `0` ### 422 Validation Error #### Example ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string", "input": null, "ctx": {} } ] } ``` - `detail` (`replace_mind_skills.response.422.detail`; array, optional) - `loc` (`replace_mind_skills.response.422.detail.loc`; array, required) - any of: `string`, `integer` - `msg` (`replace_mind_skills.response.422.detail.msg`; string, required) - `type` (`replace_mind_skills.response.422.detail.type`; string, required) - `input` (`replace_mind_skills.response.422.detail.input`; unknown, optional) - `ctx` (`replace_mind_skills.response.422.detail.ctx`; object, optional) # Update a mind > Rename a mind, retag it, or change its visibility. Owner or admin grant. Only fields actually sent are touched — ``exclude_unset`` is what keeps an omitted field from being read as "clear this". Source: https://truememory-docs.mvstg.com/api/minds/update/ · Markdown: https://truememory-docs.mvstg.com/api/minds/update/index.md Path: AI Gateway API › Minds `PATCH /minds/{mind_id}` Rename a mind, retag it, or change its visibility. Owner or admin grant. Only fields actually sent are touched — ``exclude_unset`` is what keeps an omitted field from being read as "clear this". ## Authentication - `HTTPBearer`, http, header `Authorization` ## Path parameters - `mind_id` (`update_mind.path.mind_id`; string, required) - format `uuid` ## Header parameters - `X-App-Id` (`update_mind.header.X-App-Id`; any of, optional) - any of: `string`, `null` - `X-On-Behalf-Of` (`update_mind.header.X-On-Behalf-Of`; any of, optional) - any of: `string`, `null` - `X-External-User-Id` (`update_mind.header.X-External-User-Id`; any of, optional) - any of: `string`, `null` ## Request body _Required._ - `name` (`update_mind.name`; any of, optional) - any of: `string`, `null` - `slug` (`update_mind.slug`; any of, optional) - any of: `string`, `null` - `description` (`update_mind.description`; any of, optional) - any of: `string`, `null` - `emoji` (`update_mind.emoji`; any of, optional) - any of: `string`, `null` - `usage_hint` (`update_mind.usage_hint`; any of, optional) - any of: `string`, `null` - `visibility` (`update_mind.visibility`; any of, optional) - any of: `string`, `null` - `skill_ids` (`update_mind.skill_ids`; any of, optional) - any of: `array`, `null` ## Example request ```json { "name": "string", "slug": "string", "description": "string", "emoji": "string", "usage_hint": "string", "visibility": "private", "skill_ids": [ "497f6eca-6276-4993-bfeb-53cbbbba6f08" ] } ``` ## Code samples ### cURL ```bash curl --request PATCH \ --url 'https://truememory-api.mvstg.com/minds/' \ --header 'Authorization: Bearer ' \ --header 'Content-Type: application/json' \ --data ' { "name": "string", "slug": "string", "description": "string", "emoji": "string", "usage_hint": "string", "visibility": "private", "skill_ids": [ "497f6eca-6276-4993-bfeb-53cbbbba6f08" ] } ' ``` ### TypeScript ```typescript const url = 'https://truememory-api.mvstg.com/minds/'; const options = { method: 'PATCH', headers: {'Content-Type': 'application/json', Authorization: 'Bearer '}, body: JSON.stringify({ name: 'string', slug: 'string', description: 'string', emoji: 'string', usage_hint: 'string', visibility: 'private', skill_ids: ['497f6eca-6276-4993-bfeb-53cbbbba6f08'] }) }; fetch(url, options) .then(res => res.json()) .then(json => console.log(json)) .catch(err => console.error(err)); ``` ### Python ```python import requests url = "https://truememory-api.mvstg.com/minds/" payload = { "name": "string", "slug": "string", "description": "string", "emoji": "string", "usage_hint": "string", "visibility": "private", "skill_ids": ["497f6eca-6276-4993-bfeb-53cbbbba6f08"] } headers = { "Content-Type": "application/json", "Authorization": "Bearer " } response = requests.patch(url, json=payload, headers=headers) print(response.text) ``` ## Responses ### 200 Successful Response #### Example ```json { "id": "string", "owner_type": "user", "owner_id": "string", "app_id": "string", "slug": "string", "name": "string", "description": "string", "emoji": "string", "usage_hint": "string", "visibility": "private", "banner_image_url": "string", "skills": [ { "id": "string", "key": "string", "name": "string" } ], "brain_count": 0, "created_at": "2019-08-24T14:15:22Z", "updated_at": "2019-08-24T14:15:22Z", "skill_ids": [ "string" ] } ``` - `id` (`update_mind.response.200.id`; string, required) - `owner_type` (`update_mind.response.200.owner_type`; string, required) - one of `"user"`, `"org"` - `owner_id` (`update_mind.response.200.owner_id`; string, required) - `slug` (`update_mind.response.200.slug`; string, required) - `name` (`update_mind.response.200.name`; string, required) - `created_at` (`update_mind.response.200.created_at`; string, required) - format `date-time` - `updated_at` (`update_mind.response.200.updated_at`; string, required) - format `date-time` - `skill_ids` (`update_mind.response.200.skill_ids`; array, required) — Ids of ``skills``, for clients seeding a multi-select. - `app_id` (`update_mind.response.200.app_id`; any of, optional) - any of: `string`, `null` - `description` (`update_mind.response.200.description`; any of, optional) - any of: `string`, `null` - `emoji` (`update_mind.response.200.emoji`; any of, optional) - any of: `string`, `null` - `usage_hint` (`update_mind.response.200.usage_hint`; any of, optional) - any of: `string`, `null` - `visibility` (`update_mind.response.200.visibility`; string, optional) - one of `"private"`, `"app"`, `"public"`; default `"private"` - `banner_image_url` (`update_mind.response.200.banner_image_url`; any of, optional) - any of: `string`, `null` - `skills` (`update_mind.response.200.skills`; array, optional) - `id` (`update_mind.response.200.skills.id`; string, required) - `key` (`update_mind.response.200.skills.key`; string, required) - `name` (`update_mind.response.200.skills.name`; string, required) - `brain_count` (`update_mind.response.200.brain_count`; integer, optional) - default `0` ### 422 Validation Error #### Example ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string", "input": null, "ctx": {} } ] } ``` - `detail` (`update_mind.response.422.detail`; array, optional) - `loc` (`update_mind.response.422.detail.loc`; array, required) - any of: `string`, `integer` - `msg` (`update_mind.response.422.detail.msg`; string, required) - `type` (`update_mind.response.422.detail.type`; string, required) - `input` (`update_mind.response.422.detail.input`; unknown, optional) - `ctx` (`update_mind.response.422.detail.ctx`; object, optional) # Register a device for push notifications > Register an FCM registration token for the subject user. Idempotent — re-sending the same token refreshes it. If the token was previously registered to a different user (the device was handed over, or someone logged out and back in), ownership moves to the subject user so the previous owner stops receiving that device's notifications. Source: https://truememory-docs.mvstg.com/api/notifications/devices/create/ · Markdown: https://truememory-docs.mvstg.com/api/notifications/devices/create/index.md Path: AI Gateway API › Notifications `POST /notifications/devices` Register an FCM registration token for the subject user. Idempotent — re-sending the same token refreshes it. If the token was previously registered to a different user (the device was handed over, or someone logged out and back in), ownership moves to the subject user so the previous owner stops receiving that device's notifications. ## Authentication - `HTTPBearer`, http, header `Authorization` ## Header parameters - `X-App-Id` (`register_device.header.X-App-Id`; any of, optional) - any of: `string`, `null` - `X-On-Behalf-Of` (`register_device.header.X-On-Behalf-Of`; any of, optional) - any of: `string`, `null` - `X-External-User-Id` (`register_device.header.X-External-User-Id`; any of, optional) - any of: `string`, `null` ## Request body _Required._ - `token` (`register_device.token`; string, required) — FCM registration token. - minLength 1; maxLength 4096 - `platform` (`register_device.platform`; any of, optional) — Informational only — FCM routes by token, not by this field. - any of: `string`, `null` ## Example request ```json { "token": "string", "platform": "ios" } ``` ## Code samples ### cURL ```bash curl --request POST \ --url https://truememory-api.mvstg.com/notifications/devices \ --header 'Authorization: Bearer ' \ --header 'Content-Type: application/json' \ --data ' { "token": "string", "platform": "ios" } ' ``` ### TypeScript ```typescript const url = 'https://truememory-api.mvstg.com/notifications/devices'; const options = { method: 'POST', headers: {'Content-Type': 'application/json', Authorization: 'Bearer '}, body: JSON.stringify({token: 'string', platform: 'ios'}) }; fetch(url, options) .then(res => res.json()) .then(json => console.log(json)) .catch(err => console.error(err)); ``` ### Python ```python import requests url = "https://truememory-api.mvstg.com/notifications/devices" payload = { "token": "string", "platform": "ios" } headers = { "Content-Type": "application/json", "Authorization": "Bearer " } response = requests.post(url, json=payload, headers=headers) print(response.text) ``` ## Responses ### 201 Successful Response #### Example ```json { "id": "string", "app_id": "string", "user_id": "string", "token": "string", "platform": "ios", "created_at": "2019-08-24T14:15:22Z", "updated_at": "2019-08-24T14:15:22Z" } ``` - `id` (`register_device.response.201.id`; string, required) - `app_id` (`register_device.response.201.app_id`; string, required) - `user_id` (`register_device.response.201.user_id`; string, required) - `token` (`register_device.response.201.token`; string, required) - `created_at` (`register_device.response.201.created_at`; string, required) - format `date-time` - `updated_at` (`register_device.response.201.updated_at`; string, required) - format `date-time` - `platform` (`register_device.response.201.platform`; any of, optional) - any of: `string`, `null` ### 422 Validation Error #### Example ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string", "input": null, "ctx": {} } ] } ``` - `detail` (`register_device.response.422.detail`; array, optional) - `loc` (`register_device.response.422.detail.loc`; array, required) - any of: `string`, `integer` - `msg` (`register_device.response.422.detail.msg`; string, required) - `type` (`register_device.response.422.detail.type`; string, required) - `input` (`register_device.response.422.detail.input`; unknown, optional) - `ctx` (`register_device.response.422.detail.ctx`; object, optional) # Unregister a device > Remove a device. Idempotent — unregistering an unknown token is a no-op, so a client retrying a logout never has to special-case 404. Source: https://truememory-docs.mvstg.com/api/notifications/devices/delete/ · Markdown: https://truememory-docs.mvstg.com/api/notifications/devices/delete/index.md Path: AI Gateway API › Notifications `DELETE /notifications/devices/{token}` Remove a device. Idempotent — unregistering an unknown token is a no-op, so a client retrying a logout never has to special-case 404. ## Authentication - `HTTPBearer`, http, header `Authorization` ## Path parameters - `token` (`unregister_device.path.token`; string, required) ## Header parameters - `X-App-Id` (`unregister_device.header.X-App-Id`; any of, optional) - any of: `string`, `null` - `X-On-Behalf-Of` (`unregister_device.header.X-On-Behalf-Of`; any of, optional) - any of: `string`, `null` - `X-External-User-Id` (`unregister_device.header.X-External-User-Id`; any of, optional) - any of: `string`, `null` ## Code samples ### cURL ```bash curl --request DELETE \ --url 'https://truememory-api.mvstg.com/notifications/devices/' \ --header 'Authorization: Bearer ' ``` ### TypeScript ```typescript const url = 'https://truememory-api.mvstg.com/notifications/devices/'; const options = {method: 'DELETE', headers: {Authorization: 'Bearer '}}; fetch(url, options) .then(res => res.json()) .then(json => console.log(json)) .catch(err => console.error(err)); ``` ### Python ```python import requests url = "https://truememory-api.mvstg.com/notifications/devices/" headers = {"Authorization": "Bearer "} response = requests.delete(url, headers=headers) print(response.text) ``` ## Responses ### 204 Successful Response ### 422 Validation Error #### Example ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string", "input": null, "ctx": {} } ] } ``` - `detail` (`unregister_device.response.422.detail`; array, optional) - `loc` (`unregister_device.response.422.detail.loc`; array, required) - any of: `string`, `integer` - `msg` (`unregister_device.response.422.detail.msg`; string, required) - `type` (`unregister_device.response.422.detail.type`; string, required) - `input` (`unregister_device.response.422.detail.input`; unknown, optional) - `ctx` (`unregister_device.response.422.detail.ctx`; object, optional) # List the subject user's registered devices > Every device registered for the subject user under the calling app. Source: https://truememory-docs.mvstg.com/api/notifications/devices/list/ · Markdown: https://truememory-docs.mvstg.com/api/notifications/devices/list/index.md Path: AI Gateway API › Notifications `GET /notifications/devices` Every device registered for the subject user under the calling app. ## Authentication - `HTTPBearer`, http, header `Authorization` ## Header parameters - `X-App-Id` (`list_devices.header.X-App-Id`; any of, optional) - any of: `string`, `null` - `X-On-Behalf-Of` (`list_devices.header.X-On-Behalf-Of`; any of, optional) - any of: `string`, `null` - `X-External-User-Id` (`list_devices.header.X-External-User-Id`; any of, optional) - any of: `string`, `null` ## Code samples ### cURL ```bash curl --request GET \ --url https://truememory-api.mvstg.com/notifications/devices \ --header 'Authorization: Bearer ' ``` ### TypeScript ```typescript const url = 'https://truememory-api.mvstg.com/notifications/devices'; const options = {method: 'GET', headers: {Authorization: 'Bearer '}}; fetch(url, options) .then(res => res.json()) .then(json => console.log(json)) .catch(err => console.error(err)); ``` ### Python ```python import requests url = "https://truememory-api.mvstg.com/notifications/devices" headers = {"Authorization": "Bearer "} response = requests.get(url, headers=headers) print(response.text) ``` ## Responses ### 200 Successful Response #### Example ```json { "devices": [ { "id": "string", "app_id": "string", "user_id": "string", "token": "string", "platform": "ios", "created_at": "2019-08-24T14:15:22Z", "updated_at": "2019-08-24T14:15:22Z" } ], "total": 0 } ``` - `devices` (`list_devices.response.200.devices`; array, required) - `id` (`list_devices.response.200.devices.id`; string, required) - `app_id` (`list_devices.response.200.devices.app_id`; string, required) - `user_id` (`list_devices.response.200.devices.user_id`; string, required) - `token` (`list_devices.response.200.devices.token`; string, required) - `created_at` (`list_devices.response.200.devices.created_at`; string, required) - format `date-time` - `updated_at` (`list_devices.response.200.devices.updated_at`; string, required) - format `date-time` - `platform` (`list_devices.response.200.devices.platform`; any of, optional) - any of: `string`, `null` - `total` (`list_devices.response.200.total`; integer, required) ### 422 Validation Error #### Example ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string", "input": null, "ctx": {} } ] } ``` - `detail` (`list_devices.response.422.detail`; array, optional) - `loc` (`list_devices.response.422.detail.loc`; array, required) - any of: `string`, `integer` - `msg` (`list_devices.response.422.detail.msg`; string, required) - `type` (`list_devices.response.422.detail.type`; string, required) - `input` (`list_devices.response.422.detail.input`; unknown, optional) - `ctx` (`list_devices.response.422.detail.ctx`; object, optional) # Send a push notification > Send a push notification through Truememory's FCM credential. Target with exactly one of ``user_ids`` (resolved through the device registry), ``tokens`` (raw registration tokens you manage yourself), or ``topic``/``condition`` (FCM-side fan-out). Per-device failures are reported in ``results`` rather than failing the request; only an upstream FCM failure returns an error status. Tokens FCM reports as dead are dropped from the registry and flagged ``pruned``. Source: https://truememory-docs.mvstg.com/api/notifications/push/ · Markdown: https://truememory-docs.mvstg.com/api/notifications/push/index.md Path: AI Gateway API › Notifications `POST /notifications/push` Send a push notification through Truememory's FCM credential. Target with exactly one of ``user_ids`` (resolved through the device registry), ``tokens`` (raw registration tokens you manage yourself), or ``topic``/``condition`` (FCM-side fan-out). Per-device failures are reported in ``results`` rather than failing the request; only an upstream FCM failure returns an error status. Tokens FCM reports as dead are dropped from the registry and flagged ``pruned``. ## Authentication - `HTTPBearer`, http, header `Authorization` ## Header parameters - `X-App-Id` (`send_push.header.X-App-Id`; any of, optional) - any of: `string`, `null` - `X-On-Behalf-Of` (`send_push.header.X-On-Behalf-Of`; any of, optional) - any of: `string`, `null` - `X-External-User-Id` (`send_push.header.X-External-User-Id`; any of, optional) - any of: `string`, `null` ## Request body _Required._ - `user_ids` (`send_push.user_ids`; any of, optional) — Truememory user ids; resolved to registered devices for the calling app. - any of: `array`, `null` - `tokens` (`send_push.tokens`; any of, optional) — Raw FCM registration tokens, for callers holding their own registry. - any of: `array`, `null` - `topic` (`send_push.topic`; any of, optional) - any of: `string`, `null` - `condition` (`send_push.condition`; any of, optional) — FCM condition expression, e.g. "'a' in topics && 'b' in topics". - any of: `string`, `null` - `title` (`send_push.title`; any of, optional) - any of: `string`, `null` - `body` (`send_push.body`; any of, optional) - any of: `string`, `null` - `image` (`send_push.image`; any of, optional) — Public image URL. - any of: `string`, `null` - `data` (`send_push.data`; any of, optional) — Arbitrary string key/value pairs delivered to the app. - any of: `map`, `null` - `priority` (`send_push.priority`; any of, optional) - any of: `string`, `null` - `ttl_seconds` (`send_push.ttl_seconds`; any of, optional) - any of: `integer`, `null` - `sound` (`send_push.sound`; any of, optional) - any of: `string`, `null` - `badge` (`send_push.badge`; any of, optional) - any of: `integer`, `null` - `dry_run` (`send_push.dry_run`; boolean, optional) — Validate with FCM without delivering to devices. - default `false` ## Example request ```json { "user_ids": [ "string" ], "tokens": [ "string" ], "topic": "string", "condition": "string", "title": "string", "body": "string", "image": "string", "data": { "property1": "string", "property2": "string" }, "priority": "normal", "ttl_seconds": 2419200, "sound": "string", "badge": 0, "dry_run": false } ``` ## Code samples ### cURL ```bash curl --request POST \ --url https://truememory-api.mvstg.com/notifications/push \ --header 'Authorization: Bearer ' \ --header 'Content-Type: application/json' \ --data ' { "user_ids": [ "string" ], "tokens": [ "string" ], "topic": "string", "condition": "string", "title": "string", "body": "string", "image": "string", "data": { "property1": "string", "property2": "string" }, "priority": "normal", "ttl_seconds": 2419200, "sound": "string", "badge": 0, "dry_run": false } ' ``` ### TypeScript ```typescript const url = 'https://truememory-api.mvstg.com/notifications/push'; const options = { method: 'POST', headers: {'Content-Type': 'application/json', Authorization: 'Bearer '}, body: JSON.stringify({ user_ids: ['string'], tokens: ['string'], topic: 'string', condition: 'string', title: 'string', body: 'string', image: 'string', data: {property1: 'string', property2: 'string'}, priority: 'normal', ttl_seconds: 2419200, sound: 'string', badge: 0, dry_run: false }) }; fetch(url, options) .then(res => res.json()) .then(json => console.log(json)) .catch(err => console.error(err)); ``` ### Python ```python import requests url = "https://truememory-api.mvstg.com/notifications/push" payload = { "user_ids": ["string"], "tokens": ["string"], "topic": "string", "condition": "string", "title": "string", "body": "string", "image": "string", "data": { "property1": "string", "property2": "string" }, "priority": "normal", "ttl_seconds": 2419200, "sound": "string", "badge": 0, "dry_run": False } headers = { "Content-Type": "application/json", "Authorization": "Bearer " } response = requests.post(url, json=payload, headers=headers) print(response.text) ``` ## Responses ### 200 Successful Response #### Example ```json { "success_count": 0, "failure_count": 0, "results": [ { "target": "string", "user_id": "string", "success": true, "message_id": "string", "error_code": "string", "error": "string", "pruned": false } ], "unreachable_user_ids": [ "string" ], "pruned_token_count": 0 } ``` - `success_count` (`send_push.response.200.success_count`; integer, required) - `failure_count` (`send_push.response.200.failure_count`; integer, required) - `results` (`send_push.response.200.results`; array, required) - `target` (`send_push.response.200.results.target`; string, required) — The registration token, topic, or condition sent to. - `success` (`send_push.response.200.results.success`; boolean, required) - `user_id` (`send_push.response.200.results.user_id`; any of, optional) — Set when the target was resolved from the device registry. - any of: `string`, `null` - `message_id` (`send_push.response.200.results.message_id`; any of, optional) - any of: `string`, `null` - `error_code` (`send_push.response.200.results.error_code`; any of, optional) — FCM's error code, uppercased. - any of: `string`, `null` - `error` (`send_push.response.200.results.error`; any of, optional) - any of: `string`, `null` - `pruned` (`send_push.response.200.results.pruned`; boolean, optional) — The token was dropped from the registry because FCM reports it dead. - default `false` - `unreachable_user_ids` (`send_push.response.200.unreachable_user_ids`; array, optional) — Requested users with no device registered for this app. - `pruned_token_count` (`send_push.response.200.pruned_token_count`; integer, optional) - default `0` ### 422 Validation Error #### Example ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string", "input": null, "ctx": {} } ] } ``` - `detail` (`send_push.response.422.detail`; array, optional) - `loc` (`send_push.response.422.detail.loc`; array, required) - any of: `string`, `integer` - `msg` (`send_push.response.422.detail.msg`; string, required) - `type` (`send_push.response.422.detail.type`; string, required) - `input` (`send_push.response.422.detail.input`; unknown, optional) - `ctx` (`send_push.response.422.detail.ctx`; object, optional) # Subscribe devices to a topic > Subscribe devices to one of your app's topics, server-side. Use this when your backend decides membership (a plan, a cohort, a segment) rather than the device deciding for itself — a client can subscribe itself through the Firebase SDK without involving Truememory at all. The topic is namespaced to your app before it reaches FCM, and the response echoes the qualified name. **Truememory does not store subscriptions**: FCM owns that state and offers no way to read it back, so a local copy would drift. Keep your own record if you need to enumerate membership. Source: https://truememory-docs.mvstg.com/api/notifications/topics/subscribe/ · Markdown: https://truememory-docs.mvstg.com/api/notifications/topics/subscribe/index.md Path: AI Gateway API › Notifications `POST /notifications/topics/{topic}/subscribe` Subscribe devices to one of your app's topics, server-side. Use this when your backend decides membership (a plan, a cohort, a segment) rather than the device deciding for itself — a client can subscribe itself through the Firebase SDK without involving Truememory at all. The topic is namespaced to your app before it reaches FCM, and the response echoes the qualified name. **Truememory does not store subscriptions**: FCM owns that state and offers no way to read it back, so a local copy would drift. Keep your own record if you need to enumerate membership. ## Authentication - `HTTPBearer`, http, header `Authorization` ## Path parameters - `topic` (`subscribe_to_topic.path.topic`; string, required) — Topic name, unprefixed. - maxLength 900; pattern `^[a-zA-Z0-9\-_.~%]+$` ## Header parameters - `X-App-Id` (`subscribe_to_topic.header.X-App-Id`; any of, optional) - any of: `string`, `null` - `X-On-Behalf-Of` (`subscribe_to_topic.header.X-On-Behalf-Of`; any of, optional) - any of: `string`, `null` - `X-External-User-Id` (`subscribe_to_topic.header.X-External-User-Id`; any of, optional) - any of: `string`, `null` ## Request body _Required._ - `user_ids` (`subscribe_to_topic.user_ids`; any of, optional) — Truememory user ids; resolved to registered devices for the calling app. - any of: `array`, `null` - `tokens` (`subscribe_to_topic.tokens`; any of, optional) — Raw FCM registration tokens. - any of: `array`, `null` ## Example request ```json { "user_ids": [ "string" ], "tokens": [ "string" ] } ``` ## Code samples ### cURL ```bash curl --request POST \ --url 'https://truememory-api.mvstg.com/notifications/topics//subscribe' \ --header 'Authorization: Bearer ' \ --header 'Content-Type: application/json' \ --data ' { "user_ids": [ "string" ], "tokens": [ "string" ] } ' ``` ### TypeScript ```typescript const url = 'https://truememory-api.mvstg.com/notifications/topics//subscribe'; const options = { method: 'POST', headers: {'Content-Type': 'application/json', Authorization: 'Bearer '}, body: JSON.stringify({user_ids: ['string'], tokens: ['string']}) }; fetch(url, options) .then(res => res.json()) .then(json => console.log(json)) .catch(err => console.error(err)); ``` ### Python ```python import requests url = "https://truememory-api.mvstg.com/notifications/topics//subscribe" payload = { "user_ids": ["string"], "tokens": ["string"] } headers = { "Content-Type": "application/json", "Authorization": "Bearer " } response = requests.post(url, json=payload, headers=headers) print(response.text) ``` ## Responses ### 200 Successful Response #### Example ```json { "topic": "string", "success_count": 0, "failure_count": 0, "results": [ { "target": "string", "user_id": "string", "success": true, "message_id": "string", "error_code": "string", "error": "string", "pruned": false } ], "unreachable_user_ids": [ "string" ], "pruned_token_count": 0 } ``` - `topic` (`subscribe_to_topic.response.200.topic`; string, required) — The app-namespaced topic FCM was given. - `success_count` (`subscribe_to_topic.response.200.success_count`; integer, required) - `failure_count` (`subscribe_to_topic.response.200.failure_count`; integer, required) - `results` (`subscribe_to_topic.response.200.results`; array, required) - `target` (`subscribe_to_topic.response.200.results.target`; string, required) — The registration token, topic, or condition sent to. - `success` (`subscribe_to_topic.response.200.results.success`; boolean, required) - `user_id` (`subscribe_to_topic.response.200.results.user_id`; any of, optional) — Set when the target was resolved from the device registry. - any of: `string`, `null` - `message_id` (`subscribe_to_topic.response.200.results.message_id`; any of, optional) - any of: `string`, `null` - `error_code` (`subscribe_to_topic.response.200.results.error_code`; any of, optional) — FCM's error code, uppercased. - any of: `string`, `null` - `error` (`subscribe_to_topic.response.200.results.error`; any of, optional) - any of: `string`, `null` - `pruned` (`subscribe_to_topic.response.200.results.pruned`; boolean, optional) — The token was dropped from the registry because FCM reports it dead. - default `false` - `unreachable_user_ids` (`subscribe_to_topic.response.200.unreachable_user_ids`; array, optional) — Requested users with no device registered for this app. - `pruned_token_count` (`subscribe_to_topic.response.200.pruned_token_count`; integer, optional) - default `0` ### 422 Validation Error #### Example ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string", "input": null, "ctx": {} } ] } ``` - `detail` (`subscribe_to_topic.response.422.detail`; array, optional) - `loc` (`subscribe_to_topic.response.422.detail.loc`; array, required) - any of: `string`, `integer` - `msg` (`subscribe_to_topic.response.422.detail.msg`; string, required) - `type` (`subscribe_to_topic.response.422.detail.type`; string, required) - `input` (`subscribe_to_topic.response.422.detail.input`; unknown, optional) - `ctx` (`subscribe_to_topic.response.422.detail.ctx`; object, optional) # Unsubscribe devices from a topic > Remove devices from one of your app's topics. Idempotent from the caller's side — unsubscribing a device that was never subscribed succeeds. Source: https://truememory-docs.mvstg.com/api/notifications/topics/unsubscribe/ · Markdown: https://truememory-docs.mvstg.com/api/notifications/topics/unsubscribe/index.md Path: AI Gateway API › Notifications `POST /notifications/topics/{topic}/unsubscribe` Remove devices from one of your app's topics. Idempotent from the caller's side — unsubscribing a device that was never subscribed succeeds. ## Authentication - `HTTPBearer`, http, header `Authorization` ## Path parameters - `topic` (`unsubscribe_from_topic.path.topic`; string, required) — Topic name, unprefixed. - maxLength 900; pattern `^[a-zA-Z0-9\-_.~%]+$` ## Header parameters - `X-App-Id` (`unsubscribe_from_topic.header.X-App-Id`; any of, optional) - any of: `string`, `null` - `X-On-Behalf-Of` (`unsubscribe_from_topic.header.X-On-Behalf-Of`; any of, optional) - any of: `string`, `null` - `X-External-User-Id` (`unsubscribe_from_topic.header.X-External-User-Id`; any of, optional) - any of: `string`, `null` ## Request body _Required._ - `user_ids` (`unsubscribe_from_topic.user_ids`; any of, optional) — Truememory user ids; resolved to registered devices for the calling app. - any of: `array`, `null` - `tokens` (`unsubscribe_from_topic.tokens`; any of, optional) — Raw FCM registration tokens. - any of: `array`, `null` ## Example request ```json { "user_ids": [ "string" ], "tokens": [ "string" ] } ``` ## Code samples ### cURL ```bash curl --request POST \ --url 'https://truememory-api.mvstg.com/notifications/topics//unsubscribe' \ --header 'Authorization: Bearer ' \ --header 'Content-Type: application/json' \ --data ' { "user_ids": [ "string" ], "tokens": [ "string" ] } ' ``` ### TypeScript ```typescript const url = 'https://truememory-api.mvstg.com/notifications/topics//unsubscribe'; const options = { method: 'POST', headers: {'Content-Type': 'application/json', Authorization: 'Bearer '}, body: JSON.stringify({user_ids: ['string'], tokens: ['string']}) }; fetch(url, options) .then(res => res.json()) .then(json => console.log(json)) .catch(err => console.error(err)); ``` ### Python ```python import requests url = "https://truememory-api.mvstg.com/notifications/topics//unsubscribe" payload = { "user_ids": ["string"], "tokens": ["string"] } headers = { "Content-Type": "application/json", "Authorization": "Bearer " } response = requests.post(url, json=payload, headers=headers) print(response.text) ``` ## Responses ### 200 Successful Response #### Example ```json { "topic": "string", "success_count": 0, "failure_count": 0, "results": [ { "target": "string", "user_id": "string", "success": true, "message_id": "string", "error_code": "string", "error": "string", "pruned": false } ], "unreachable_user_ids": [ "string" ], "pruned_token_count": 0 } ``` - `topic` (`unsubscribe_from_topic.response.200.topic`; string, required) — The app-namespaced topic FCM was given. - `success_count` (`unsubscribe_from_topic.response.200.success_count`; integer, required) - `failure_count` (`unsubscribe_from_topic.response.200.failure_count`; integer, required) - `results` (`unsubscribe_from_topic.response.200.results`; array, required) - `target` (`unsubscribe_from_topic.response.200.results.target`; string, required) — The registration token, topic, or condition sent to. - `success` (`unsubscribe_from_topic.response.200.results.success`; boolean, required) - `user_id` (`unsubscribe_from_topic.response.200.results.user_id`; any of, optional) — Set when the target was resolved from the device registry. - any of: `string`, `null` - `message_id` (`unsubscribe_from_topic.response.200.results.message_id`; any of, optional) - any of: `string`, `null` - `error_code` (`unsubscribe_from_topic.response.200.results.error_code`; any of, optional) — FCM's error code, uppercased. - any of: `string`, `null` - `error` (`unsubscribe_from_topic.response.200.results.error`; any of, optional) - any of: `string`, `null` - `pruned` (`unsubscribe_from_topic.response.200.results.pruned`; boolean, optional) — The token was dropped from the registry because FCM reports it dead. - default `false` - `unreachable_user_ids` (`unsubscribe_from_topic.response.200.unreachable_user_ids`; array, optional) — Requested users with no device registered for this app. - `pruned_token_count` (`unsubscribe_from_topic.response.200.pruned_token_count`; integer, optional) - default `0` ### 422 Validation Error #### Example ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string", "input": null, "ctx": {} } ] } ``` - `detail` (`unsubscribe_from_topic.response.422.detail`; array, optional) - `loc` (`unsubscribe_from_topic.response.422.detail.loc`; array, required) - any of: `string`, `integer` - `msg` (`unsubscribe_from_topic.response.422.detail.msg`; string, required) - `type` (`unsubscribe_from_topic.response.422.detail.type`; string, required) - `input` (`unsubscribe_from_topic.response.422.detail.input`; unknown, optional) - `ctx` (`unsubscribe_from_topic.response.422.detail.ctx`; object, optional) # Clear active persona > Clear the active persona (revert to default agent behavior). Source: https://truememory-docs.mvstg.com/api/personas/active/delete/ · Markdown: https://truememory-docs.mvstg.com/api/personas/active/delete/index.md Path: AI Gateway API › Personas `DELETE /personas/active` Clear the active persona (revert to default agent behavior). ## Authentication - `HTTPBearer`, http, header `Authorization` ## Header parameters - `X-On-Behalf-Of` (`clear_active_persona.header.X-On-Behalf-Of`; any of, optional) - any of: `string`, `null` - `X-External-User-Id` (`clear_active_persona.header.X-External-User-Id`; any of, optional) - any of: `string`, `null` - `X-App-Id` (`clear_active_persona.header.X-App-Id`; any of, optional) - any of: `string`, `null` ## Code samples ### cURL ```bash curl --request DELETE \ --url https://truememory-api.mvstg.com/personas/active \ --header 'Authorization: Bearer ' ``` ### TypeScript ```typescript const url = 'https://truememory-api.mvstg.com/personas/active'; const options = {method: 'DELETE', headers: {Authorization: 'Bearer '}}; fetch(url, options) .then(res => res.json()) .then(json => console.log(json)) .catch(err => console.error(err)); ``` ### Python ```python import requests url = "https://truememory-api.mvstg.com/personas/active" headers = {"Authorization": "Bearer "} response = requests.delete(url, headers=headers) print(response.text) ``` ## Responses ### 200 Successful Response #### Example ```json { "status": "cleared" } ``` - `status` (`clear_active_persona.response.200.status`; string, optional) - default `"cleared"` ### 422 Validation Error #### Example ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string", "input": null, "ctx": {} } ] } ``` - `detail` (`clear_active_persona.response.422.detail`; array, optional) - `loc` (`clear_active_persona.response.422.detail.loc`; array, required) - any of: `string`, `integer` - `msg` (`clear_active_persona.response.422.detail.msg`; string, required) - `type` (`clear_active_persona.response.422.detail.type`; string, required) - `input` (`clear_active_persona.response.422.detail.input`; unknown, optional) - `ctx` (`clear_active_persona.response.422.detail.ctx`; object, optional) # Get active persona > Get the authenticated user's currently active persona. Source: https://truememory-docs.mvstg.com/api/personas/active/list/ · Markdown: https://truememory-docs.mvstg.com/api/personas/active/list/index.md Path: AI Gateway API › Personas `GET /personas/active` Get the authenticated user's currently active persona. ## Authentication - `HTTPBearer`, http, header `Authorization` ## Header parameters - `X-On-Behalf-Of` (`get_active_persona.header.X-On-Behalf-Of`; any of, optional) - any of: `string`, `null` - `X-External-User-Id` (`get_active_persona.header.X-External-User-Id`; any of, optional) - any of: `string`, `null` - `X-App-Id` (`get_active_persona.header.X-App-Id`; any of, optional) - any of: `string`, `null` ## Code samples ### cURL ```bash curl --request GET \ --url https://truememory-api.mvstg.com/personas/active \ --header 'Authorization: Bearer ' ``` ### TypeScript ```typescript const url = 'https://truememory-api.mvstg.com/personas/active'; const options = {method: 'GET', headers: {Authorization: 'Bearer '}}; fetch(url, options) .then(res => res.json()) .then(json => console.log(json)) .catch(err => console.error(err)); ``` ### Python ```python import requests url = "https://truememory-api.mvstg.com/personas/active" headers = {"Authorization": "Bearer "} response = requests.get(url, headers=headers) print(response.text) ``` ## Responses ### 200 Successful Response #### Example ```json { "id": "string", "user_id": "string", "name": "string", "description": "string", "system_prompt": "string", "is_preset": true, "visibility": "private", "is_active": true, "archetype": "string", "soul_document": {}, "human_profile": "string", "avatar": "string", "avatar_config": {}, "created_at": "2019-08-24T14:15:22Z", "updated_at": "2019-08-24T14:15:22Z", "activated_at": "2019-08-24T14:15:22Z" } ``` - `id` (`get_active_persona.response.200.id`; string, required) - `user_id` (`get_active_persona.response.200.user_id`; any of, required) — Owner user_id, NULL for presets. - any of: `string`, `null` - `name` (`get_active_persona.response.200.name`; string, required) - `description` (`get_active_persona.response.200.description`; any of, required) - any of: `string`, `null` - `system_prompt` (`get_active_persona.response.200.system_prompt`; string, required) - `is_preset` (`get_active_persona.response.200.is_preset`; boolean, required) - `created_at` (`get_active_persona.response.200.created_at`; string, required) - format `date-time` - `updated_at` (`get_active_persona.response.200.updated_at`; string, required) - format `date-time` - `activated_at` (`get_active_persona.response.200.activated_at`; string, required) - format `date-time` - `visibility` (`get_active_persona.response.200.visibility`; string, optional) - one of `"private"`, `"public"`; default `"private"` - `is_active` (`get_active_persona.response.200.is_active`; boolean, optional) - default `true` - `archetype` (`get_active_persona.response.200.archetype`; any of, optional) — App-defined archetype identifier (e.g. SAGE, EXPLORER). Each app renders its own visual for this. - any of: `string`, `null` - `soul_document` (`get_active_persona.response.200.soul_document`; any of, optional) — Structured personality document: voice style, anti-patterns, learned adaptations, boundaries, custom instructions. - any of: `object`, `null` - `human_profile` (`get_active_persona.response.200.human_profile`; any of, optional) — Creator-specific profile data. Hidden from other users when the persona is public. - any of: `string`, `null` - `avatar` (`get_active_persona.response.200.avatar`; any of, optional) — URL of the persona's avatar image. - any of: `string`, `null` - `avatar_config` (`get_active_persona.response.200.avatar_config`; any of, optional) — App-defined dynamic avatar settings (a JSON object, 64 KB max). Truememory stores and returns it unchanged and never puts it in a prompt. - any of: `object`, `null` ### 422 Validation Error #### Example ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string", "input": null, "ctx": {} } ] } ``` - `detail` (`get_active_persona.response.422.detail`; array, optional) - `loc` (`get_active_persona.response.422.detail.loc`; array, required) - any of: `string`, `integer` - `msg` (`get_active_persona.response.422.detail.msg`; string, required) - `type` (`get_active_persona.response.422.detail.type`; string, required) - `input` (`get_active_persona.response.422.detail.input`; unknown, optional) - `ctx` (`get_active_persona.response.422.detail.ctx`; object, optional) # Set active persona > Set the active persona for the authenticated user. Source: https://truememory-docs.mvstg.com/api/personas/active/update/ · Markdown: https://truememory-docs.mvstg.com/api/personas/active/update/index.md Path: AI Gateway API › Personas `PUT /personas/active/{persona_id}` Set the active persona for the authenticated user. ## Authentication - `HTTPBearer`, http, header `Authorization` ## Path parameters - `persona_id` (`set_active_persona.path.persona_id`; string, required) ## Header parameters - `X-On-Behalf-Of` (`set_active_persona.header.X-On-Behalf-Of`; any of, optional) - any of: `string`, `null` - `X-External-User-Id` (`set_active_persona.header.X-External-User-Id`; any of, optional) - any of: `string`, `null` - `X-App-Id` (`set_active_persona.header.X-App-Id`; any of, optional) - any of: `string`, `null` ## Code samples ### cURL ```bash curl --request PUT \ --url 'https://truememory-api.mvstg.com/personas/active/' \ --header 'Authorization: Bearer ' ``` ### TypeScript ```typescript const url = 'https://truememory-api.mvstg.com/personas/active/'; const options = {method: 'PUT', headers: {Authorization: 'Bearer '}}; fetch(url, options) .then(res => res.json()) .then(json => console.log(json)) .catch(err => console.error(err)); ``` ### Python ```python import requests url = "https://truememory-api.mvstg.com/personas/active/" headers = {"Authorization": "Bearer "} response = requests.put(url, headers=headers) print(response.text) ``` ## Responses ### 200 Successful Response #### Example ```json { "status": "active", "persona_id": "string" } ``` - `persona_id` (`set_active_persona.response.200.persona_id`; string, required) - `status` (`set_active_persona.response.200.status`; string, optional) - default `"active"` ### 422 Validation Error #### Example ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string", "input": null, "ctx": {} } ] } ``` - `detail` (`set_active_persona.response.422.detail`; array, optional) - `loc` (`set_active_persona.response.422.detail.loc`; array, required) - any of: `string`, `integer` - `msg` (`set_active_persona.response.422.detail.msg`; string, required) - `type` (`set_active_persona.response.422.detail.type`; string, required) - `input` (`set_active_persona.response.422.detail.input`; unknown, optional) - `ctx` (`set_active_persona.response.422.detail.ctx`; object, optional) # Create a persona > Create a custom persona for the authenticated user. Source: https://truememory-docs.mvstg.com/api/personas/create/ · Markdown: https://truememory-docs.mvstg.com/api/personas/create/index.md Path: AI Gateway API › Personas `POST /personas` Create a custom persona for the authenticated user. ## Authentication - `HTTPBearer`, http, header `Authorization` ## Header parameters - `X-On-Behalf-Of` (`create_persona.header.X-On-Behalf-Of`; any of, optional) - any of: `string`, `null` - `X-External-User-Id` (`create_persona.header.X-External-User-Id`; any of, optional) - any of: `string`, `null` - `X-App-Id` (`create_persona.header.X-App-Id`; any of, optional) - any of: `string`, `null` ## Request body _Required._ - `name` (`create_persona.name`; string, required) — Display name for the persona. - minLength 1; maxLength 255 - `system_prompt` (`create_persona.system_prompt`; string, required) — System prompt injected at the start of every conversation when this persona is active. - minLength 1 - `description` (`create_persona.description`; any of, optional) — Short human-readable description of the persona's purpose. - any of: `string`, `null` - `visibility` (`create_persona.visibility`; string, optional) — private = creator only; public = available to users in every app. - one of `"private"`, `"public"`; default `"private"` - `archetype` (`create_persona.archetype`; any of, optional) — App-defined archetype identifier (e.g. SAGE, EXPLORER). Shared across apps. - any of: `string`, `null` - `soul_document` (`create_persona.soul_document`; any of, optional) — Structured personality document (JSON). Keys: voiceAndStyle, antiPatterns, learnedAdaptations, boundaries, customInstructions. - any of: `object`, `null` - `human_profile` (`create_persona.human_profile`; any of, optional) — Creator-specific profile data. Never shared with public persona consumers. - any of: `string`, `null` - `avatar` (`create_persona.avatar`; any of, optional) — URL of the persona's avatar image. - any of: `string`, `null` - `avatar_config` (`create_persona.avatar_config`; any of, optional) — App-defined dynamic avatar settings (a JSON object, 64 KB max). Truememory stores and returns it unchanged and never puts it in a prompt. - any of: `object`, `null` ## Example request ```json { "name": "Friendly Coach", "description": "A warm, encouraging coaching persona", "system_prompt": "You are a friendly life coach. Respond with empathy and encouragement.", "visibility": "private", "archetype": "string", "soul_document": {}, "human_profile": "string", "avatar": "string", "avatar_config": {} } ``` ## Code samples ### cURL ```bash curl --request POST \ --url https://truememory-api.mvstg.com/personas \ --header 'Authorization: Bearer ' \ --header 'Content-Type: application/json' \ --data ' { "name": "Friendly Coach", "description": "A warm, encouraging coaching persona", "system_prompt": "You are a friendly life coach. Respond with empathy and encouragement.", "visibility": "private", "archetype": "string", "soul_document": {}, "human_profile": "string", "avatar": "string", "avatar_config": {} } ' ``` ### TypeScript ```typescript const url = 'https://truememory-api.mvstg.com/personas'; const options = { method: 'POST', headers: {'Content-Type': 'application/json', Authorization: 'Bearer '}, body: JSON.stringify({ name: 'Friendly Coach', description: 'A warm, encouraging coaching persona', system_prompt: 'You are a friendly life coach. Respond with empathy and encouragement.', visibility: 'private', archetype: 'string', soul_document: {}, human_profile: 'string', avatar: 'string', avatar_config: {} }) }; fetch(url, options) .then(res => res.json()) .then(json => console.log(json)) .catch(err => console.error(err)); ``` ### Python ```python import requests url = "https://truememory-api.mvstg.com/personas" payload = { "name": "Friendly Coach", "description": "A warm, encouraging coaching persona", "system_prompt": "You are a friendly life coach. Respond with empathy and encouragement.", "visibility": "private", "archetype": "string", "soul_document": {}, "human_profile": "string", "avatar": "string", "avatar_config": {} } headers = { "Content-Type": "application/json", "Authorization": "Bearer " } response = requests.post(url, json=payload, headers=headers) print(response.text) ``` ## Responses ### 201 Successful Response #### Example ```json { "id": "string", "user_id": "string", "name": "string", "description": "string", "system_prompt": "string", "is_preset": true, "visibility": "private", "is_active": false, "archetype": "string", "soul_document": {}, "human_profile": "string", "avatar": "string", "avatar_config": {}, "created_at": "2019-08-24T14:15:22Z", "updated_at": "2019-08-24T14:15:22Z" } ``` - `id` (`create_persona.response.201.id`; string, required) - `user_id` (`create_persona.response.201.user_id`; any of, required) — Owner user_id, NULL for presets. - any of: `string`, `null` - `name` (`create_persona.response.201.name`; string, required) - `description` (`create_persona.response.201.description`; any of, required) - any of: `string`, `null` - `system_prompt` (`create_persona.response.201.system_prompt`; string, required) - `is_preset` (`create_persona.response.201.is_preset`; boolean, required) - `created_at` (`create_persona.response.201.created_at`; string, required) - format `date-time` - `updated_at` (`create_persona.response.201.updated_at`; string, required) - format `date-time` - `visibility` (`create_persona.response.201.visibility`; string, optional) - one of `"private"`, `"public"`; default `"private"` - `is_active` (`create_persona.response.201.is_active`; boolean, optional) - default `false` - `archetype` (`create_persona.response.201.archetype`; any of, optional) — App-defined archetype identifier (e.g. SAGE, EXPLORER). Each app renders its own visual for this. - any of: `string`, `null` - `soul_document` (`create_persona.response.201.soul_document`; any of, optional) — Structured personality document: voice style, anti-patterns, learned adaptations, boundaries, custom instructions. - any of: `object`, `null` - `human_profile` (`create_persona.response.201.human_profile`; any of, optional) — Creator-specific profile data. Hidden from other users when the persona is public. - any of: `string`, `null` - `avatar` (`create_persona.response.201.avatar`; any of, optional) — URL of the persona's avatar image. - any of: `string`, `null` - `avatar_config` (`create_persona.response.201.avatar_config`; any of, optional) — App-defined dynamic avatar settings (a JSON object, 64 KB max). Truememory stores and returns it unchanged and never puts it in a prompt. - any of: `object`, `null` ### 422 Validation Error #### Example ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string", "input": null, "ctx": {} } ] } ``` - `detail` (`create_persona.response.422.detail`; array, optional) - `loc` (`create_persona.response.422.detail.loc`; array, required) - any of: `string`, `integer` - `msg` (`create_persona.response.422.detail.msg`; string, required) - `type` (`create_persona.response.422.detail.type`; string, required) - `input` (`create_persona.response.422.detail.input`; unknown, optional) - `ctx` (`create_persona.response.422.detail.ctx`; object, optional) # Delete a persona > Delete a custom (non-preset) persona owned by the authenticated user. Source: https://truememory-docs.mvstg.com/api/personas/delete/ · Markdown: https://truememory-docs.mvstg.com/api/personas/delete/index.md Path: AI Gateway API › Personas `DELETE /personas/{persona_id}` Delete a custom (non-preset) persona owned by the authenticated user. ## Authentication - `HTTPBearer`, http, header `Authorization` ## Path parameters - `persona_id` (`delete_persona.path.persona_id`; string, required) ## Header parameters - `X-On-Behalf-Of` (`delete_persona.header.X-On-Behalf-Of`; any of, optional) - any of: `string`, `null` - `X-External-User-Id` (`delete_persona.header.X-External-User-Id`; any of, optional) - any of: `string`, `null` - `X-App-Id` (`delete_persona.header.X-App-Id`; any of, optional) - any of: `string`, `null` ## Code samples ### cURL ```bash curl --request DELETE \ --url 'https://truememory-api.mvstg.com/personas/' \ --header 'Authorization: Bearer ' ``` ### TypeScript ```typescript const url = 'https://truememory-api.mvstg.com/personas/'; const options = {method: 'DELETE', headers: {Authorization: 'Bearer '}}; fetch(url, options) .then(res => res.json()) .then(json => console.log(json)) .catch(err => console.error(err)); ``` ### Python ```python import requests url = "https://truememory-api.mvstg.com/personas/" headers = {"Authorization": "Bearer "} response = requests.delete(url, headers=headers) print(response.text) ``` ## Responses ### 200 Successful Response #### Example ```json { "status": "deleted", "persona_id": "string" } ``` - `persona_id` (`delete_persona.response.200.persona_id`; string, required) - `status` (`delete_persona.response.200.status`; string, optional) - default `"deleted"` ### 422 Validation Error #### Example ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string", "input": null, "ctx": {} } ] } ``` - `detail` (`delete_persona.response.422.detail`; array, optional) - `loc` (`delete_persona.response.422.detail.loc`; array, required) - any of: `string`, `integer` - `msg` (`delete_persona.response.422.detail.msg`; string, required) - `type` (`delete_persona.response.422.detail.type`; string, required) - `input` (`delete_persona.response.422.detail.input`; unknown, optional) - `ctx` (`delete_persona.response.422.detail.ctx`; object, optional) # List personas > List available personas (presets + user's custom) with is_active flag. Source: https://truememory-docs.mvstg.com/api/personas/list/ · Markdown: https://truememory-docs.mvstg.com/api/personas/list/index.md Path: AI Gateway API › Personas `GET /personas` List available personas (presets + user's custom) with is_active flag. ## Authentication - `HTTPBearer`, http, header `Authorization` ## Header parameters - `X-On-Behalf-Of` (`list_personas.header.X-On-Behalf-Of`; any of, optional) - any of: `string`, `null` - `X-External-User-Id` (`list_personas.header.X-External-User-Id`; any of, optional) - any of: `string`, `null` - `X-App-Id` (`list_personas.header.X-App-Id`; any of, optional) - any of: `string`, `null` ## Code samples ### cURL ```bash curl --request GET \ --url https://truememory-api.mvstg.com/personas \ --header 'Authorization: Bearer ' ``` ### TypeScript ```typescript const url = 'https://truememory-api.mvstg.com/personas'; const options = {method: 'GET', headers: {Authorization: 'Bearer '}}; fetch(url, options) .then(res => res.json()) .then(json => console.log(json)) .catch(err => console.error(err)); ``` ### Python ```python import requests url = "https://truememory-api.mvstg.com/personas" headers = {"Authorization": "Bearer "} response = requests.get(url, headers=headers) print(response.text) ``` ## Responses ### 200 Successful Response #### Example ```json [ { "id": "string", "user_id": "string", "name": "string", "description": "string", "system_prompt": "string", "is_preset": true, "visibility": "private", "is_active": false, "archetype": "string", "soul_document": {}, "human_profile": "string", "avatar": "string", "avatar_config": {}, "created_at": "2019-08-24T14:15:22Z", "updated_at": "2019-08-24T14:15:22Z" } ] ``` - `id` (`list_personas.response.200.id`; string, required) - `user_id` (`list_personas.response.200.user_id`; any of, required) — Owner user_id, NULL for presets. - any of: `string`, `null` - `name` (`list_personas.response.200.name`; string, required) - `description` (`list_personas.response.200.description`; any of, required) - any of: `string`, `null` - `system_prompt` (`list_personas.response.200.system_prompt`; string, required) - `is_preset` (`list_personas.response.200.is_preset`; boolean, required) - `created_at` (`list_personas.response.200.created_at`; string, required) - format `date-time` - `updated_at` (`list_personas.response.200.updated_at`; string, required) - format `date-time` - `visibility` (`list_personas.response.200.visibility`; string, optional) - one of `"private"`, `"public"`; default `"private"` - `is_active` (`list_personas.response.200.is_active`; boolean, optional) - default `false` - `archetype` (`list_personas.response.200.archetype`; any of, optional) — App-defined archetype identifier (e.g. SAGE, EXPLORER). Each app renders its own visual for this. - any of: `string`, `null` - `soul_document` (`list_personas.response.200.soul_document`; any of, optional) — Structured personality document: voice style, anti-patterns, learned adaptations, boundaries, custom instructions. - any of: `object`, `null` - `human_profile` (`list_personas.response.200.human_profile`; any of, optional) — Creator-specific profile data. Hidden from other users when the persona is public. - any of: `string`, `null` - `avatar` (`list_personas.response.200.avatar`; any of, optional) — URL of the persona's avatar image. - any of: `string`, `null` - `avatar_config` (`list_personas.response.200.avatar_config`; any of, optional) — App-defined dynamic avatar settings (a JSON object, 64 KB max). Truememory stores and returns it unchanged and never puts it in a prompt. - any of: `object`, `null` ### 422 Validation Error #### Example ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string", "input": null, "ctx": {} } ] } ``` - `detail` (`list_personas.response.422.detail`; array, optional) - `loc` (`list_personas.response.422.detail.loc`; array, required) - any of: `string`, `integer` - `msg` (`list_personas.response.422.detail.msg`; string, required) - `type` (`list_personas.response.422.detail.type`; string, required) - `input` (`list_personas.response.422.detail.input`; unknown, optional) - `ctx` (`list_personas.response.422.detail.ctx`; object, optional) # Update a persona > Update a custom (non-preset) persona owned by the authenticated user. Source: https://truememory-docs.mvstg.com/api/personas/update/ · Markdown: https://truememory-docs.mvstg.com/api/personas/update/index.md Path: AI Gateway API › Personas `PUT /personas/{persona_id}` Update a custom (non-preset) persona owned by the authenticated user. ## Authentication - `HTTPBearer`, http, header `Authorization` ## Path parameters - `persona_id` (`update_persona.path.persona_id`; string, required) ## Header parameters - `X-On-Behalf-Of` (`update_persona.header.X-On-Behalf-Of`; any of, optional) - any of: `string`, `null` - `X-External-User-Id` (`update_persona.header.X-External-User-Id`; any of, optional) - any of: `string`, `null` - `X-App-Id` (`update_persona.header.X-App-Id`; any of, optional) - any of: `string`, `null` ## Request body _Required._ - `name` (`update_persona.name`; any of, optional) — New display name for the persona. - any of: `string`, `null` - `description` (`update_persona.description`; any of, optional) — Updated description of the persona's purpose. Pass null to clear. - any of: `string`, `null` - `system_prompt` (`update_persona.system_prompt`; any of, optional) — Updated system prompt. - any of: `string`, `null` - `visibility` (`update_persona.visibility`; any of, optional) — Updated platform-wide visibility. - any of: `string`, `null` - `archetype` (`update_persona.archetype`; any of, optional) — Updated archetype identifier. Pass null to clear. - any of: `string`, `null` - `soul_document` (`update_persona.soul_document`; any of, optional) — Updated structured personality document. Pass null to clear. - any of: `object`, `null` - `human_profile` (`update_persona.human_profile`; any of, optional) — Updated user profile text. Pass null to clear. - any of: `string`, `null` - `avatar` (`update_persona.avatar`; any of, optional) — Updated avatar URL. Pass null to clear. - any of: `string`, `null` - `avatar_config` (`update_persona.avatar_config`; any of, optional) — Replaces the whole avatar config (no merge). Pass null to clear. - any of: `object`, `null` ## Example request ```json { "name": "Strict Mentor", "description": "A direct, no-nonsense mentoring persona", "system_prompt": "You are a strict mentor. Be concise and direct.", "visibility": "private", "archetype": "string", "soul_document": {}, "human_profile": "string", "avatar": "string", "avatar_config": {} } ``` ## Code samples ### cURL ```bash curl --request PUT \ --url 'https://truememory-api.mvstg.com/personas/' \ --header 'Authorization: Bearer ' \ --header 'Content-Type: application/json' \ --data ' { "name": "Strict Mentor", "description": "A direct, no-nonsense mentoring persona", "system_prompt": "You are a strict mentor. Be concise and direct.", "visibility": "private", "archetype": "string", "soul_document": {}, "human_profile": "string", "avatar": "string", "avatar_config": {} } ' ``` ### TypeScript ```typescript const url = 'https://truememory-api.mvstg.com/personas/'; const options = { method: 'PUT', headers: {'Content-Type': 'application/json', Authorization: 'Bearer '}, body: JSON.stringify({ name: 'Strict Mentor', description: 'A direct, no-nonsense mentoring persona', system_prompt: 'You are a strict mentor. Be concise and direct.', visibility: 'private', archetype: 'string', soul_document: {}, human_profile: 'string', avatar: 'string', avatar_config: {} }) }; fetch(url, options) .then(res => res.json()) .then(json => console.log(json)) .catch(err => console.error(err)); ``` ### Python ```python import requests url = "https://truememory-api.mvstg.com/personas/" payload = { "name": "Strict Mentor", "description": "A direct, no-nonsense mentoring persona", "system_prompt": "You are a strict mentor. Be concise and direct.", "visibility": "private", "archetype": "string", "soul_document": {}, "human_profile": "string", "avatar": "string", "avatar_config": {} } headers = { "Content-Type": "application/json", "Authorization": "Bearer " } response = requests.put(url, json=payload, headers=headers) print(response.text) ``` ## Responses ### 200 Successful Response #### Example ```json { "id": "string", "user_id": "string", "name": "string", "description": "string", "system_prompt": "string", "is_preset": true, "visibility": "private", "is_active": false, "archetype": "string", "soul_document": {}, "human_profile": "string", "avatar": "string", "avatar_config": {}, "created_at": "2019-08-24T14:15:22Z", "updated_at": "2019-08-24T14:15:22Z" } ``` - `id` (`update_persona.response.200.id`; string, required) - `user_id` (`update_persona.response.200.user_id`; any of, required) — Owner user_id, NULL for presets. - any of: `string`, `null` - `name` (`update_persona.response.200.name`; string, required) - `description` (`update_persona.response.200.description`; any of, required) - any of: `string`, `null` - `system_prompt` (`update_persona.response.200.system_prompt`; string, required) - `is_preset` (`update_persona.response.200.is_preset`; boolean, required) - `created_at` (`update_persona.response.200.created_at`; string, required) - format `date-time` - `updated_at` (`update_persona.response.200.updated_at`; string, required) - format `date-time` - `visibility` (`update_persona.response.200.visibility`; string, optional) - one of `"private"`, `"public"`; default `"private"` - `is_active` (`update_persona.response.200.is_active`; boolean, optional) - default `false` - `archetype` (`update_persona.response.200.archetype`; any of, optional) — App-defined archetype identifier (e.g. SAGE, EXPLORER). Each app renders its own visual for this. - any of: `string`, `null` - `soul_document` (`update_persona.response.200.soul_document`; any of, optional) — Structured personality document: voice style, anti-patterns, learned adaptations, boundaries, custom instructions. - any of: `object`, `null` - `human_profile` (`update_persona.response.200.human_profile`; any of, optional) — Creator-specific profile data. Hidden from other users when the persona is public. - any of: `string`, `null` - `avatar` (`update_persona.response.200.avatar`; any of, optional) — URL of the persona's avatar image. - any of: `string`, `null` - `avatar_config` (`update_persona.response.200.avatar_config`; any of, optional) — App-defined dynamic avatar settings (a JSON object, 64 KB max). Truememory stores and returns it unchanged and never puts it in a prompt. - any of: `object`, `null` ### 422 Validation Error #### Example ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string", "input": null, "ctx": {} } ] } ``` - `detail` (`update_persona.response.422.detail`; array, optional) - `loc` (`update_persona.response.422.detail.loc`; array, required) - any of: `string`, `integer` - `msg` (`update_persona.response.422.detail.msg`; string, required) - `type` (`update_persona.response.422.detail.type`; string, required) - `input` (`update_persona.response.422.detail.input`; unknown, optional) - `ctx` (`update_persona.response.422.detail.ctx`; object, optional) # Get a skill by key > Get a single skill (preset or owned) by its key. 404 if not found. When the user owns a skill with the same key as a preset, the user's skill wins; within each tier an app-scoped row wins over a global one (standard override semantics). Source: https://truememory-docs.mvstg.com/api/skills/by-key/get/ · Markdown: https://truememory-docs.mvstg.com/api/skills/by-key/get/index.md Path: AI Gateway API › Skills `GET /skills/by-key/{key}` Get a single skill (preset or owned) by its key. 404 if not found. When the user owns a skill with the same key as a preset, the user's skill wins; within each tier an app-scoped row wins over a global one (standard override semantics). ## Authentication - `HTTPBearer`, http, header `Authorization` ## Path parameters - `key` (`get_skill_by_key.path.key`; string, required) ## Query parameters - `app_id` (`get_skill_by_key.query.app_id`; any of, optional) — App scope: prefer this app's row over a global one. - any of: `string`, `null` ## Header parameters - `X-On-Behalf-Of` (`get_skill_by_key.header.X-On-Behalf-Of`; any of, optional) - any of: `string`, `null` - `X-External-User-Id` (`get_skill_by_key.header.X-External-User-Id`; any of, optional) - any of: `string`, `null` - `X-App-Id` (`get_skill_by_key.header.X-App-Id`; any of, optional) - any of: `string`, `null` ## Code samples ### cURL ```bash curl --request GET \ --url 'https://truememory-api.mvstg.com/skills/by-key/' \ --header 'Authorization: Bearer ' ``` ### TypeScript ```typescript const url = 'https://truememory-api.mvstg.com/skills/by-key/'; const options = {method: 'GET', headers: {Authorization: 'Bearer '}}; fetch(url, options) .then(res => res.json()) .then(json => console.log(json)) .catch(err => console.error(err)); ``` ### Python ```python import requests url = "https://truememory-api.mvstg.com/skills/by-key/" headers = {"Authorization": "Bearer "} response = requests.get(url, headers=headers) print(response.text) ``` ## Responses ### 200 Successful Response #### Example ```json { "id": "string", "user_id": "string", "app_ids": [ "string" ], "key": "string", "name": "string", "description": "string", "prompt": "string", "category": "string", "categories": [ { "id": "string", "name": "string" } ], "icon": "string", "argument_hint": "string", "allowed_tools": [ "string" ], "metadata": {}, "use_case": "string", "detection_patterns": [ "string" ], "license": "string", "compatibility": "string", "is_preset": true, "is_active": true, "sort_order": 0, "forked_from_skill_id": "string", "usage_count": 0, "last_used_at": "2019-08-24T14:15:22Z", "parent_synced_at": "2019-08-24T14:15:22Z", "created_at": "2019-08-24T14:15:22Z", "updated_at": "2019-08-24T14:15:22Z", "app_id": "string" } ``` - `id` (`get_skill_by_key.response.200.id`; string, required) - `user_id` (`get_skill_by_key.response.200.user_id`; any of, required) — Owner user_id, NULL for presets. - any of: `string`, `null` - `app_ids` (`get_skill_by_key.response.200.app_ids`; any of, required) — Apps this skill is assigned to. NULL means all apps (global/unscoped). - any of: `array`, `null` - `key` (`get_skill_by_key.response.200.key`; string, required) - `name` (`get_skill_by_key.response.200.name`; string, required) - `description` (`get_skill_by_key.response.200.description`; any of, required) - any of: `string`, `null` - `prompt` (`get_skill_by_key.response.200.prompt`; string, required) - `category` (`get_skill_by_key.response.200.category`; any of, required) — Freeform grouping label (legacy string). Distinct from `categories`. - any of: `string`, `null` - `icon` (`get_skill_by_key.response.200.icon`; any of, required) - any of: `string`, `null` - `argument_hint` (`get_skill_by_key.response.200.argument_hint`; any of, required) - any of: `string`, `null` - `allowed_tools` (`get_skill_by_key.response.200.allowed_tools`; array, required) - `metadata` (`get_skill_by_key.response.200.metadata`; object, required) - `is_preset` (`get_skill_by_key.response.200.is_preset`; boolean, required) - `is_active` (`get_skill_by_key.response.200.is_active`; boolean, required) - `sort_order` (`get_skill_by_key.response.200.sort_order`; integer, required) - `forked_from_skill_id` (`get_skill_by_key.response.200.forked_from_skill_id`; any of, required) - any of: `string`, `null` - `usage_count` (`get_skill_by_key.response.200.usage_count`; integer, required) — Global invocation count, bumped via POST /skills/{id}/usage. - `last_used_at` (`get_skill_by_key.response.200.last_used_at`; any of, required) — When the skill was last invoked, NULL if never. - any of: `string`, `null` - `parent_synced_at` (`get_skill_by_key.response.200.parent_synced_at`; any of, required) — For forks: the preset updated_at this fork is synced to. The fork is stale when its preset's updated_at is newer. NULL for non-forks. - any of: `string`, `null` - `created_at` (`get_skill_by_key.response.200.created_at`; string, required) - format `date-time` - `updated_at` (`get_skill_by_key.response.200.updated_at`; string, required) - format `date-time` - `app_id` (`get_skill_by_key.response.200.app_id`; any of, required) — DEPRECATED, read app_ids instead. Legacy single-app scalar derived from app_ids for pre-multi-app consumers: the first app id, or null for all apps. Lossy for multi-app skills. - any of: `string`, `null` - `categories` (`get_skill_by_key.response.200.categories`; array, optional) — Category entities assigned via the registry. Returned by the list endpoints, the single-skill reads (user and admin) and PUT /admin/skills/{skill_id}/categories; omitted by writes. - `id` (`get_skill_by_key.response.200.categories.id`; string, required) - `name` (`get_skill_by_key.response.200.categories.name`; string, required) - `use_case` (`get_skill_by_key.response.200.use_case`; any of, optional) — When this skill applies, in natural language. Read by skill selection. - any of: `string`, `null` - `detection_patterns` (`get_skill_by_key.response.200.detection_patterns`; any of, optional) — Example utterances that should invoke this skill. - any of: `array`, `null` - `license` (`get_skill_by_key.response.200.license`; any of, optional) — SKILL.md `license`: a license name, or the file in the skill that holds it. - any of: `string`, `null` - `compatibility` (`get_skill_by_key.response.200.compatibility`; any of, optional) — SKILL.md `compatibility`: what the skill needs to run (product, packages, network). - any of: `string`, `null` ### 422 Validation Error #### Example ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string", "input": null, "ctx": {} } ] } ``` - `detail` (`get_skill_by_key.response.422.detail`; array, optional) - `loc` (`get_skill_by_key.response.422.detail.loc`; array, required) - any of: `string`, `integer` - `msg` (`get_skill_by_key.response.422.detail.msg`; string, required) - `type` (`get_skill_by_key.response.422.detail.type`; string, required) - `input` (`get_skill_by_key.response.422.detail.input`; unknown, optional) - `ctx` (`get_skill_by_key.response.422.detail.ctx`; object, optional) # List skill categories > Distinct categories across the skills visible to the caller. Source: https://truememory-docs.mvstg.com/api/skills/categories/ · Markdown: https://truememory-docs.mvstg.com/api/skills/categories/index.md Path: AI Gateway API › Skills `GET /skills/categories` Distinct categories across the skills visible to the caller. ## Authentication - `HTTPBearer`, http, header `Authorization` ## Query parameters - `app_id` (`list_skill_categories.query.app_id`; any of, optional) — App scope: rows for this app plus global rows. None = no filter. - any of: `string`, `null` ## Header parameters - `X-On-Behalf-Of` (`list_skill_categories.header.X-On-Behalf-Of`; any of, optional) - any of: `string`, `null` - `X-External-User-Id` (`list_skill_categories.header.X-External-User-Id`; any of, optional) - any of: `string`, `null` - `X-App-Id` (`list_skill_categories.header.X-App-Id`; any of, optional) - any of: `string`, `null` ## Code samples ### cURL ```bash curl --request GET \ --url https://truememory-api.mvstg.com/skills/categories \ --header 'Authorization: Bearer ' ``` ### TypeScript ```typescript const url = 'https://truememory-api.mvstg.com/skills/categories'; const options = {method: 'GET', headers: {Authorization: 'Bearer '}}; fetch(url, options) .then(res => res.json()) .then(json => console.log(json)) .catch(err => console.error(err)); ``` ### Python ```python import requests url = "https://truememory-api.mvstg.com/skills/categories" headers = {"Authorization": "Bearer "} response = requests.get(url, headers=headers) print(response.text) ``` ## Responses ### 200 Successful Response #### Example ```json { "categories": [ "string" ] } ``` - `categories` (`list_skill_categories.response.200.categories`; array, required) — Distinct non-null categories, sorted alphabetically. ### 422 Validation Error #### Example ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string", "input": null, "ctx": {} } ] } ``` - `detail` (`list_skill_categories.response.422.detail`; array, optional) - `loc` (`list_skill_categories.response.422.detail.loc`; array, required) - any of: `string`, `integer` - `msg` (`list_skill_categories.response.422.detail.msg`; string, required) - `type` (`list_skill_categories.response.422.detail.type`; string, required) - `input` (`list_skill_categories.response.422.detail.input`; unknown, optional) - `ctx` (`list_skill_categories.response.422.detail.ctx`; object, optional) # Create a skill > Create a custom skill for the authenticated user. Source: https://truememory-docs.mvstg.com/api/skills/create/ · Markdown: https://truememory-docs.mvstg.com/api/skills/create/index.md Path: AI Gateway API › Skills `POST /skills` Create a custom skill for the authenticated user. ## Authentication - `HTTPBearer`, http, header `Authorization` ## Header parameters - `X-On-Behalf-Of` (`create_skill.header.X-On-Behalf-Of`; any of, optional) - any of: `string`, `null` - `X-External-User-Id` (`create_skill.header.X-External-User-Id`; any of, optional) - any of: `string`, `null` - `X-App-Id` (`create_skill.header.X-App-Id`; any of, optional) - any of: `string`, `null` ## Request body _Required._ - `key` (`create_skill.key`; string, required) — Stable client-facing identifier (e.g. for slash commands). Lowercase letters, digits, hyphens, underscores; must start and end with a letter or digit. - minLength 2; maxLength 64 - `name` (`create_skill.name`; string, required) — Display name for the skill. - minLength 1; maxLength 255 - `prompt` (`create_skill.prompt`; string, required) — Prompt body executed when the skill is invoked. - minLength 1 - `description` (`create_skill.description`; any of, optional) — Short human-readable description of what the skill does. - any of: `string`, `null` - `category` (`create_skill.category`; any of, optional) — Optional grouping label (e.g. 'productivity', 'writing'). - any of: `string`, `null` - `app_ids` (`create_skill.app_ids`; any of, optional) — Apps this skill is assigned to. NULL (or omitted) means all apps — visible to every app. A non-empty list scopes it to those apps. The legacy scalar ``app_id`` is still accepted and folded into a one-element list. - any of: `array`, `null` - `icon` (`create_skill.icon`; any of, optional) — Icon identifier or URL — interpreted by the caller. - any of: `string`, `null` - `argument_hint` (`create_skill.argument_hint`; any of, optional) — Hint shown to the user about expected arguments. - any of: `string`, `null` - `allowed_tools` (`create_skill.allowed_tools`; array, optional) — Tool names the skill is allowed to use. Empty list means no restriction at this layer. - `metadata` (`create_skill.metadata`; object, optional) — Free-form caller-defined metadata. - `is_active` (`create_skill.is_active`; boolean, optional) — Whether the skill is currently enabled. Off skills are still listable. - default `true` - `sort_order` (`create_skill.sort_order`; integer, optional) — Caller-defined display order; lower sorts earlier. - default `0` - `use_case` (`create_skill.use_case`; any of, optional) — When this skill applies, in natural language — e.g. 'Triggers when a user shares a photo of food, a menu, or ingredients.' Read by skill selection, so write it to describe the trigger, not the behaviour. - any of: `string`, `null` - `detection_patterns` (`create_skill.detection_patterns`; any of, optional) — Example utterances that should invoke this skill. Matched semantically, not literally, so paraphrases count — list distinct phrasings rather than keyword variants. - any of: `array`, `null` - `license` (`create_skill.license`; any of, optional) — SKILL.md `license`: a license name, or the file in the skill that holds it. - any of: `string`, `null` - `compatibility` (`create_skill.compatibility`; any of, optional) — SKILL.md `compatibility`: what the skill needs to run. - any of: `string`, `null` - `forked_from_skill_id` (`create_skill.forked_from_skill_id`; any of, optional) — If set, the new skill is a fork of this preset skill. Only preset skills (is_preset = TRUE) may be forked. - any of: `string`, `null` ## Example request ```json { "key": "summarize-thread", "name": "Summarize Thread", "description": "Summarize the current conversation in three bullets.", "prompt": "Summarize the conversation so far in 3 short bullet points.", "category": "productivity", "app_ids": [ "vibrantly", "blinklife" ], "icon": "sparkles", "argument_hint": "[topic]", "allowed_tools": [ "web_search", "memory_query" ], "metadata": {}, "is_active": true, "sort_order": 0, "use_case": "Triggers when a user shares a photo of food or a menu.", "detection_patterns": [ "what are the macros in this", "can I eat these berries" ], "license": "Apache-2.0", "compatibility": "string", "forked_from_skill_id": "string" } ``` ## Code samples ### cURL ```bash curl --request POST \ --url https://truememory-api.mvstg.com/skills \ --header 'Authorization: Bearer ' \ --header 'Content-Type: application/json' \ --data ' { "key": "summarize-thread", "name": "Summarize Thread", "description": "Summarize the current conversation in three bullets.", "prompt": "Summarize the conversation so far in 3 short bullet points.", "category": "productivity", "app_ids": [ "vibrantly", "blinklife" ], "icon": "sparkles", "argument_hint": "[topic]", "allowed_tools": [ "web_search", "memory_query" ], "metadata": {}, "is_active": true, "sort_order": 0, "use_case": "Triggers when a user shares a photo of food or a menu.", "detection_patterns": [ "what are the macros in this", "can I eat these berries" ], "license": "Apache-2.0", "compatibility": "string", "forked_from_skill_id": "string" } ' ``` ### TypeScript ```typescript const url = 'https://truememory-api.mvstg.com/skills'; const options = { method: 'POST', headers: {'Content-Type': 'application/json', Authorization: 'Bearer '}, body: JSON.stringify({ key: 'summarize-thread', name: 'Summarize Thread', description: 'Summarize the current conversation in three bullets.', prompt: 'Summarize the conversation so far in 3 short bullet points.', category: 'productivity', app_ids: ['vibrantly', 'blinklife'], icon: 'sparkles', argument_hint: '[topic]', allowed_tools: ['web_search', 'memory_query'], metadata: {}, is_active: true, sort_order: 0, use_case: 'Triggers when a user shares a photo of food or a menu.', detection_patterns: ['what are the macros in this', 'can I eat these berries'], license: 'Apache-2.0', compatibility: 'string', forked_from_skill_id: 'string' }) }; fetch(url, options) .then(res => res.json()) .then(json => console.log(json)) .catch(err => console.error(err)); ``` ### Python ```python import requests url = "https://truememory-api.mvstg.com/skills" payload = { "key": "summarize-thread", "name": "Summarize Thread", "description": "Summarize the current conversation in three bullets.", "prompt": "Summarize the conversation so far in 3 short bullet points.", "category": "productivity", "app_ids": ["vibrantly", "blinklife"], "icon": "sparkles", "argument_hint": "[topic]", "allowed_tools": ["web_search", "memory_query"], "metadata": {}, "is_active": True, "sort_order": 0, "use_case": "Triggers when a user shares a photo of food or a menu.", "detection_patterns": ["what are the macros in this", "can I eat these berries"], "license": "Apache-2.0", "compatibility": "string", "forked_from_skill_id": "string" } headers = { "Content-Type": "application/json", "Authorization": "Bearer " } response = requests.post(url, json=payload, headers=headers) print(response.text) ``` ## Responses ### 201 Successful Response #### Example ```json { "id": "string", "user_id": "string", "app_ids": [ "string" ], "key": "string", "name": "string", "description": "string", "prompt": "string", "category": "string", "categories": [ { "id": "string", "name": "string" } ], "icon": "string", "argument_hint": "string", "allowed_tools": [ "string" ], "metadata": {}, "use_case": "string", "detection_patterns": [ "string" ], "license": "string", "compatibility": "string", "is_preset": true, "is_active": true, "sort_order": 0, "forked_from_skill_id": "string", "usage_count": 0, "last_used_at": "2019-08-24T14:15:22Z", "parent_synced_at": "2019-08-24T14:15:22Z", "created_at": "2019-08-24T14:15:22Z", "updated_at": "2019-08-24T14:15:22Z", "app_id": "string" } ``` - `id` (`create_skill.response.201.id`; string, required) - `user_id` (`create_skill.response.201.user_id`; any of, required) — Owner user_id, NULL for presets. - any of: `string`, `null` - `app_ids` (`create_skill.response.201.app_ids`; any of, required) — Apps this skill is assigned to. NULL means all apps (global/unscoped). - any of: `array`, `null` - `key` (`create_skill.response.201.key`; string, required) - `name` (`create_skill.response.201.name`; string, required) - `description` (`create_skill.response.201.description`; any of, required) - any of: `string`, `null` - `prompt` (`create_skill.response.201.prompt`; string, required) - `category` (`create_skill.response.201.category`; any of, required) — Freeform grouping label (legacy string). Distinct from `categories`. - any of: `string`, `null` - `icon` (`create_skill.response.201.icon`; any of, required) - any of: `string`, `null` - `argument_hint` (`create_skill.response.201.argument_hint`; any of, required) - any of: `string`, `null` - `allowed_tools` (`create_skill.response.201.allowed_tools`; array, required) - `metadata` (`create_skill.response.201.metadata`; object, required) - `is_preset` (`create_skill.response.201.is_preset`; boolean, required) - `is_active` (`create_skill.response.201.is_active`; boolean, required) - `sort_order` (`create_skill.response.201.sort_order`; integer, required) - `forked_from_skill_id` (`create_skill.response.201.forked_from_skill_id`; any of, required) - any of: `string`, `null` - `usage_count` (`create_skill.response.201.usage_count`; integer, required) — Global invocation count, bumped via POST /skills/{id}/usage. - `last_used_at` (`create_skill.response.201.last_used_at`; any of, required) — When the skill was last invoked, NULL if never. - any of: `string`, `null` - `parent_synced_at` (`create_skill.response.201.parent_synced_at`; any of, required) — For forks: the preset updated_at this fork is synced to. The fork is stale when its preset's updated_at is newer. NULL for non-forks. - any of: `string`, `null` - `created_at` (`create_skill.response.201.created_at`; string, required) - format `date-time` - `updated_at` (`create_skill.response.201.updated_at`; string, required) - format `date-time` - `app_id` (`create_skill.response.201.app_id`; any of, required) — DEPRECATED, read app_ids instead. Legacy single-app scalar derived from app_ids for pre-multi-app consumers: the first app id, or null for all apps. Lossy for multi-app skills. - any of: `string`, `null` - `categories` (`create_skill.response.201.categories`; array, optional) — Category entities assigned via the registry. Returned by the list endpoints, the single-skill reads (user and admin) and PUT /admin/skills/{skill_id}/categories; omitted by writes. - `id` (`create_skill.response.201.categories.id`; string, required) - `name` (`create_skill.response.201.categories.name`; string, required) - `use_case` (`create_skill.response.201.use_case`; any of, optional) — When this skill applies, in natural language. Read by skill selection. - any of: `string`, `null` - `detection_patterns` (`create_skill.response.201.detection_patterns`; any of, optional) — Example utterances that should invoke this skill. - any of: `array`, `null` - `license` (`create_skill.response.201.license`; any of, optional) — SKILL.md `license`: a license name, or the file in the skill that holds it. - any of: `string`, `null` - `compatibility` (`create_skill.response.201.compatibility`; any of, optional) — SKILL.md `compatibility`: what the skill needs to run (product, packages, network). - any of: `string`, `null` ### 422 Validation Error #### Example ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string", "input": null, "ctx": {} } ] } ``` - `detail` (`create_skill.response.422.detail`; array, optional) - `loc` (`create_skill.response.422.detail.loc`; array, required) - any of: `string`, `integer` - `msg` (`create_skill.response.422.detail.msg`; string, required) - `type` (`create_skill.response.422.detail.type`; string, required) - `input` (`create_skill.response.422.detail.input`; unknown, optional) - `ctx` (`create_skill.response.422.detail.ctx`; object, optional) # Delete a skill > Delete a custom (non-preset) skill owned by the authenticated user. Source: https://truememory-docs.mvstg.com/api/skills/delete/ · Markdown: https://truememory-docs.mvstg.com/api/skills/delete/index.md Path: AI Gateway API › Skills `DELETE /skills/{skill_id}` Delete a custom (non-preset) skill owned by the authenticated user. ## Authentication - `HTTPBearer`, http, header `Authorization` ## Path parameters - `skill_id` (`delete_skill.path.skill_id`; string, required) ## Header parameters - `X-On-Behalf-Of` (`delete_skill.header.X-On-Behalf-Of`; any of, optional) - any of: `string`, `null` - `X-External-User-Id` (`delete_skill.header.X-External-User-Id`; any of, optional) - any of: `string`, `null` - `X-App-Id` (`delete_skill.header.X-App-Id`; any of, optional) - any of: `string`, `null` ## Code samples ### cURL ```bash curl --request DELETE \ --url 'https://truememory-api.mvstg.com/skills/' \ --header 'Authorization: Bearer ' ``` ### TypeScript ```typescript const url = 'https://truememory-api.mvstg.com/skills/'; const options = {method: 'DELETE', headers: {Authorization: 'Bearer '}}; fetch(url, options) .then(res => res.json()) .then(json => console.log(json)) .catch(err => console.error(err)); ``` ### Python ```python import requests url = "https://truememory-api.mvstg.com/skills/" headers = {"Authorization": "Bearer "} response = requests.delete(url, headers=headers) print(response.text) ``` ## Responses ### 200 Successful Response #### Example ```json { "status": "deleted", "skill_id": "string" } ``` - `skill_id` (`delete_skill.response.200.skill_id`; string, required) - `status` (`delete_skill.response.200.status`; string, optional) - default `"deleted"` ### 422 Validation Error #### Example ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string", "input": null, "ctx": {} } ] } ``` - `detail` (`delete_skill.response.422.detail`; array, optional) - `loc` (`delete_skill.response.422.detail.loc`; array, required) - any of: `string`, `integer` - `msg` (`delete_skill.response.422.detail.msg`; string, required) - `type` (`delete_skill.response.422.detail.type`; string, required) - `input` (`delete_skill.response.422.detail.input`; unknown, optional) - `ctx` (`delete_skill.response.422.detail.ctx`; object, optional) # Download a skill folder > The skill as a zip that the Claude and OpenAI Skills APIs accept: SKILL.md with the agentskills.io frontmatter fields, and every file. Source: https://truememory-docs.mvstg.com/api/skills/export/ · Markdown: https://truememory-docs.mvstg.com/api/skills/export/index.md Path: AI Gateway API › Skills `GET /skills/{skill_id}/export` The skill as a zip that the Claude and OpenAI Skills APIs accept: SKILL.md with the agentskills.io frontmatter fields, and every file. ## Authentication - `HTTPBearer`, http, header `Authorization` ## Path parameters - `skill_id` (`export_skill.path.skill_id`; string, required) - format `uuid` ## Header parameters - `X-On-Behalf-Of` (`export_skill.header.X-On-Behalf-Of`; any of, optional) - any of: `string`, `null` - `X-External-User-Id` (`export_skill.header.X-External-User-Id`; any of, optional) - any of: `string`, `null` - `X-App-Id` (`export_skill.header.X-App-Id`; any of, optional) - any of: `string`, `null` ## Code samples ### cURL ```bash curl --request GET \ --url 'https://truememory-api.mvstg.com/skills//export' \ --header 'Authorization: Bearer ' ``` ### TypeScript ```typescript const url = 'https://truememory-api.mvstg.com/skills//export'; const options = {method: 'GET', headers: {Authorization: 'Bearer '}}; fetch(url, options) .then(res => res.json()) .then(json => console.log(json)) .catch(err => console.error(err)); ``` ### Python ```python import requests url = "https://truememory-api.mvstg.com/skills//export" headers = {"Authorization": "Bearer "} response = requests.get(url, headers=headers) print(response.text) ``` ## Responses ### 200 The skill folder as a zip. ### 422 Validation Error #### Example ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string", "input": null, "ctx": {} } ] } ``` - `detail` (`export_skill.response.422.detail`; array, optional) - `loc` (`export_skill.response.422.detail.loc`; array, required) - any of: `string`, `integer` - `msg` (`export_skill.response.422.detail.msg`; string, required) - `type` (`export_skill.response.422.detail.type`; string, required) - `input` (`export_skill.response.422.detail.input`; unknown, optional) - `ctx` (`export_skill.response.422.detail.ctx`; object, optional) # Delete a skill file Source: https://truememory-docs.mvstg.com/api/skills/files/delete/ · Markdown: https://truememory-docs.mvstg.com/api/skills/files/delete/index.md Path: AI Gateway API › Skills `DELETE /skills/{skill_id}/files/{file_path}` ## Authentication - `HTTPBearer`, http, header `Authorization` ## Path parameters - `skill_id` (`delete_skill_file.path.skill_id`; string, required) - format `uuid` - `file_path` (`delete_skill_file.path.file_path`; string, required) — Path in the skill folder, e.g. `references/method.md`. ## Header parameters - `X-On-Behalf-Of` (`delete_skill_file.header.X-On-Behalf-Of`; any of, optional) - any of: `string`, `null` - `X-External-User-Id` (`delete_skill_file.header.X-External-User-Id`; any of, optional) - any of: `string`, `null` - `X-App-Id` (`delete_skill_file.header.X-App-Id`; any of, optional) - any of: `string`, `null` ## Code samples ### cURL ```bash curl --request DELETE \ --url 'https://truememory-api.mvstg.com/skills//files/' \ --header 'Authorization: Bearer ' ``` ### TypeScript ```typescript const url = 'https://truememory-api.mvstg.com/skills//files/'; const options = {method: 'DELETE', headers: {Authorization: 'Bearer '}}; fetch(url, options) .then(res => res.json()) .then(json => console.log(json)) .catch(err => console.error(err)); ``` ### Python ```python import requests url = "https://truememory-api.mvstg.com/skills//files/" headers = {"Authorization": "Bearer "} response = requests.delete(url, headers=headers) print(response.text) ``` ## Responses ### 200 Successful Response #### Example ```json { "status": "deleted", "skill_id": "string", "path": "string" } ``` - `skill_id` (`delete_skill_file.response.200.skill_id`; string, required) - `path` (`delete_skill_file.response.200.path`; string, required) - `status` (`delete_skill_file.response.200.status`; string, optional) - default `"deleted"` ### 422 Validation Error #### Example ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string", "input": null, "ctx": {} } ] } ``` - `detail` (`delete_skill_file.response.422.detail`; array, optional) - `loc` (`delete_skill_file.response.422.detail.loc`; array, required) - any of: `string`, `integer` - `msg` (`delete_skill_file.response.422.detail.msg`; string, required) - `type` (`delete_skill_file.response.422.detail.type`; string, required) - `input` (`delete_skill_file.response.422.detail.input`; unknown, optional) - `ctx` (`delete_skill_file.response.422.detail.ctx`; object, optional) # Read a skill file Source: https://truememory-docs.mvstg.com/api/skills/files/get/ · Markdown: https://truememory-docs.mvstg.com/api/skills/files/get/index.md Path: AI Gateway API › Skills `GET /skills/{skill_id}/files/{file_path}` ## Authentication - `HTTPBearer`, http, header `Authorization` ## Path parameters - `skill_id` (`read_skill_file.path.skill_id`; string, required) - format `uuid` - `file_path` (`read_skill_file.path.file_path`; string, required) — Path in the skill folder, e.g. `references/method.md`. ## Header parameters - `X-On-Behalf-Of` (`read_skill_file.header.X-On-Behalf-Of`; any of, optional) - any of: `string`, `null` - `X-External-User-Id` (`read_skill_file.header.X-External-User-Id`; any of, optional) - any of: `string`, `null` - `X-App-Id` (`read_skill_file.header.X-App-Id`; any of, optional) - any of: `string`, `null` ## Code samples ### cURL ```bash curl --request GET \ --url 'https://truememory-api.mvstg.com/skills//files/' \ --header 'Authorization: Bearer ' ``` ### TypeScript ```typescript const url = 'https://truememory-api.mvstg.com/skills//files/'; const options = {method: 'GET', headers: {Authorization: 'Bearer '}}; fetch(url, options) .then(res => res.json()) .then(json => console.log(json)) .catch(err => console.error(err)); ``` ### Python ```python import requests url = "https://truememory-api.mvstg.com/skills//files/" headers = {"Authorization": "Bearer "} response = requests.get(url, headers=headers) print(response.text) ``` ## Responses ### 200 The file's bytes. ### 422 Validation Error #### Example ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string", "input": null, "ctx": {} } ] } ``` - `detail` (`read_skill_file.response.422.detail`; array, optional) - `loc` (`read_skill_file.response.422.detail.loc`; array, required) - any of: `string`, `integer` - `msg` (`read_skill_file.response.422.detail.msg`; string, required) - `type` (`read_skill_file.response.422.detail.type`; string, required) - `input` (`read_skill_file.response.422.detail.input`; unknown, optional) - `ctx` (`read_skill_file.response.422.detail.ctx`; object, optional) # List a skill's files > Every file besides SKILL.md, with its size and hash; no contents. Source: https://truememory-docs.mvstg.com/api/skills/files/list/ · Markdown: https://truememory-docs.mvstg.com/api/skills/files/list/index.md Path: AI Gateway API › Skills `GET /skills/{skill_id}/files` Every file besides SKILL.md, with its size and hash; no contents. ## Authentication - `HTTPBearer`, http, header `Authorization` ## Path parameters - `skill_id` (`list_skill_files.path.skill_id`; string, required) - format `uuid` ## Header parameters - `X-On-Behalf-Of` (`list_skill_files.header.X-On-Behalf-Of`; any of, optional) - any of: `string`, `null` - `X-External-User-Id` (`list_skill_files.header.X-External-User-Id`; any of, optional) - any of: `string`, `null` - `X-App-Id` (`list_skill_files.header.X-App-Id`; any of, optional) - any of: `string`, `null` ## Code samples ### cURL ```bash curl --request GET \ --url 'https://truememory-api.mvstg.com/skills//files' \ --header 'Authorization: Bearer ' ``` ### TypeScript ```typescript const url = 'https://truememory-api.mvstg.com/skills//files'; const options = {method: 'GET', headers: {Authorization: 'Bearer '}}; fetch(url, options) .then(res => res.json()) .then(json => console.log(json)) .catch(err => console.error(err)); ``` ### Python ```python import requests url = "https://truememory-api.mvstg.com/skills//files" headers = {"Authorization": "Bearer "} response = requests.get(url, headers=headers) print(response.text) ``` ## Responses ### 200 Successful Response #### Example ```json { "files": [ { "path": "string", "sha256": "string", "size": 0, "content_type": "string", "created_at": "2019-08-24T14:15:22Z", "updated_at": "2019-08-24T14:15:22Z" } ] } ``` - `files` (`list_skill_files.response.200.files`; array, required) - `path` (`list_skill_files.response.200.files.path`; string, required) — Path in the skill folder, e.g. `references/method.md`. - `sha256` (`list_skill_files.response.200.files.sha256`; string, required) — Hex SHA-256 of the file's bytes. - `size` (`list_skill_files.response.200.files.size`; integer, required) — Size in bytes. - `content_type` (`list_skill_files.response.200.files.content_type`; string, required) - `created_at` (`list_skill_files.response.200.files.created_at`; string, required) - format `date-time` - `updated_at` (`list_skill_files.response.200.files.updated_at`; string, required) — When this path last got different bytes. - format `date-time` ### 422 Validation Error #### Example ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string", "input": null, "ctx": {} } ] } ``` - `detail` (`list_skill_files.response.422.detail`; array, optional) - `loc` (`list_skill_files.response.422.detail.loc`; array, required) - any of: `string`, `integer` - `msg` (`list_skill_files.response.422.detail.msg`; string, required) - `type` (`list_skill_files.response.422.detail.type`; string, required) - `input` (`list_skill_files.response.422.detail.input`; unknown, optional) - `ctx` (`list_skill_files.response.422.detail.ctx`; object, optional) # Write a skill file > Add a file to one of your skills, or replace the file at that path. The request body is the file's bytes. Each write is a version in its history. Source: https://truememory-docs.mvstg.com/api/skills/files/update/ · Markdown: https://truememory-docs.mvstg.com/api/skills/files/update/index.md Path: AI Gateway API › Skills `PUT /skills/{skill_id}/files/{file_path}` Add a file to one of your skills, or replace the file at that path. The request body is the file's bytes. Each write is a version in its history. ## Authentication - `HTTPBearer`, http, header `Authorization` ## Path parameters - `skill_id` (`write_skill_file.path.skill_id`; string, required) - format `uuid` - `file_path` (`write_skill_file.path.file_path`; string, required) — Path in the skill folder, e.g. `references/method.md`. ## Header parameters - `X-On-Behalf-Of` (`write_skill_file.header.X-On-Behalf-Of`; any of, optional) - any of: `string`, `null` - `X-External-User-Id` (`write_skill_file.header.X-External-User-Id`; any of, optional) - any of: `string`, `null` - `X-App-Id` (`write_skill_file.header.X-App-Id`; any of, optional) - any of: `string`, `null` ## Request body _Required._ ## Example request ```text string ``` ## Code samples ### cURL ```bash curl --request PUT \ --url 'https://truememory-api.mvstg.com/skills//files/' \ --header 'Authorization: Bearer ' \ --header 'Content-Type: application/octet-stream' \ --data-raw 'string' ``` ### TypeScript ```typescript const url = 'https://truememory-api.mvstg.com/skills//files/'; const options = { method: 'PUT', headers: {'Content-Type': 'application/octet-stream', Authorization: 'Bearer '}, body: 'string' }; fetch(url, options) .then(res => res.json()) .then(json => console.log(json)) .catch(err => console.error(err)); ``` ### Python ```python import requests url = "https://truememory-api.mvstg.com/skills//files/" payload = "string" headers = { "Content-Type": "application/octet-stream", "Authorization": "Bearer " } response = requests.put(url, data=payload, headers=headers) print(response.text) ``` ## Responses ### 200 Successful Response #### Example ```json { "path": "string", "sha256": "string", "size": 0, "content_type": "string", "created_at": "2019-08-24T14:15:22Z", "updated_at": "2019-08-24T14:15:22Z" } ``` - `path` (`write_skill_file.response.200.path`; string, required) — Path in the skill folder, e.g. `references/method.md`. - `sha256` (`write_skill_file.response.200.sha256`; string, required) — Hex SHA-256 of the file's bytes. - `size` (`write_skill_file.response.200.size`; integer, required) — Size in bytes. - `content_type` (`write_skill_file.response.200.content_type`; string, required) - `created_at` (`write_skill_file.response.200.created_at`; string, required) - format `date-time` - `updated_at` (`write_skill_file.response.200.updated_at`; string, required) — When this path last got different bytes. - format `date-time` ### 422 Validation Error #### Example ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string", "input": null, "ctx": {} } ] } ``` - `detail` (`write_skill_file.response.422.detail`; array, optional) - `loc` (`write_skill_file.response.422.detail.loc`; array, required) - any of: `string`, `integer` - `msg` (`write_skill_file.response.422.detail.msg`; string, required) - `type` (`write_skill_file.response.422.detail.type`; string, required) - `input` (`write_skill_file.response.422.detail.input`; unknown, optional) - `ctx` (`write_skill_file.response.422.detail.ctx`; object, optional) # Get a skill > Get a single skill (must be a preset or owned by the user). Source: https://truememory-docs.mvstg.com/api/skills/get/ · Markdown: https://truememory-docs.mvstg.com/api/skills/get/index.md Path: AI Gateway API › Skills `GET /skills/{skill_id}` Get a single skill (must be a preset or owned by the user). ## Authentication - `HTTPBearer`, http, header `Authorization` ## Path parameters - `skill_id` (`get_skill.path.skill_id`; string, required) ## Header parameters - `X-On-Behalf-Of` (`get_skill.header.X-On-Behalf-Of`; any of, optional) - any of: `string`, `null` - `X-External-User-Id` (`get_skill.header.X-External-User-Id`; any of, optional) - any of: `string`, `null` - `X-App-Id` (`get_skill.header.X-App-Id`; any of, optional) - any of: `string`, `null` ## Code samples ### cURL ```bash curl --request GET \ --url 'https://truememory-api.mvstg.com/skills/' \ --header 'Authorization: Bearer ' ``` ### TypeScript ```typescript const url = 'https://truememory-api.mvstg.com/skills/'; const options = {method: 'GET', headers: {Authorization: 'Bearer '}}; fetch(url, options) .then(res => res.json()) .then(json => console.log(json)) .catch(err => console.error(err)); ``` ### Python ```python import requests url = "https://truememory-api.mvstg.com/skills/" headers = {"Authorization": "Bearer "} response = requests.get(url, headers=headers) print(response.text) ``` ## Responses ### 200 Successful Response #### Example ```json { "id": "string", "user_id": "string", "app_ids": [ "string" ], "key": "string", "name": "string", "description": "string", "prompt": "string", "category": "string", "categories": [ { "id": "string", "name": "string" } ], "icon": "string", "argument_hint": "string", "allowed_tools": [ "string" ], "metadata": {}, "use_case": "string", "detection_patterns": [ "string" ], "license": "string", "compatibility": "string", "is_preset": true, "is_active": true, "sort_order": 0, "forked_from_skill_id": "string", "usage_count": 0, "last_used_at": "2019-08-24T14:15:22Z", "parent_synced_at": "2019-08-24T14:15:22Z", "created_at": "2019-08-24T14:15:22Z", "updated_at": "2019-08-24T14:15:22Z", "app_id": "string" } ``` - `id` (`get_skill.response.200.id`; string, required) - `user_id` (`get_skill.response.200.user_id`; any of, required) — Owner user_id, NULL for presets. - any of: `string`, `null` - `app_ids` (`get_skill.response.200.app_ids`; any of, required) — Apps this skill is assigned to. NULL means all apps (global/unscoped). - any of: `array`, `null` - `key` (`get_skill.response.200.key`; string, required) - `name` (`get_skill.response.200.name`; string, required) - `description` (`get_skill.response.200.description`; any of, required) - any of: `string`, `null` - `prompt` (`get_skill.response.200.prompt`; string, required) - `category` (`get_skill.response.200.category`; any of, required) — Freeform grouping label (legacy string). Distinct from `categories`. - any of: `string`, `null` - `icon` (`get_skill.response.200.icon`; any of, required) - any of: `string`, `null` - `argument_hint` (`get_skill.response.200.argument_hint`; any of, required) - any of: `string`, `null` - `allowed_tools` (`get_skill.response.200.allowed_tools`; array, required) - `metadata` (`get_skill.response.200.metadata`; object, required) - `is_preset` (`get_skill.response.200.is_preset`; boolean, required) - `is_active` (`get_skill.response.200.is_active`; boolean, required) - `sort_order` (`get_skill.response.200.sort_order`; integer, required) - `forked_from_skill_id` (`get_skill.response.200.forked_from_skill_id`; any of, required) - any of: `string`, `null` - `usage_count` (`get_skill.response.200.usage_count`; integer, required) — Global invocation count, bumped via POST /skills/{id}/usage. - `last_used_at` (`get_skill.response.200.last_used_at`; any of, required) — When the skill was last invoked, NULL if never. - any of: `string`, `null` - `parent_synced_at` (`get_skill.response.200.parent_synced_at`; any of, required) — For forks: the preset updated_at this fork is synced to. The fork is stale when its preset's updated_at is newer. NULL for non-forks. - any of: `string`, `null` - `created_at` (`get_skill.response.200.created_at`; string, required) - format `date-time` - `updated_at` (`get_skill.response.200.updated_at`; string, required) - format `date-time` - `app_id` (`get_skill.response.200.app_id`; any of, required) — DEPRECATED, read app_ids instead. Legacy single-app scalar derived from app_ids for pre-multi-app consumers: the first app id, or null for all apps. Lossy for multi-app skills. - any of: `string`, `null` - `categories` (`get_skill.response.200.categories`; array, optional) — Category entities assigned via the registry. Returned by the list endpoints, the single-skill reads (user and admin) and PUT /admin/skills/{skill_id}/categories; omitted by writes. - `id` (`get_skill.response.200.categories.id`; string, required) - `name` (`get_skill.response.200.categories.name`; string, required) - `use_case` (`get_skill.response.200.use_case`; any of, optional) — When this skill applies, in natural language. Read by skill selection. - any of: `string`, `null` - `detection_patterns` (`get_skill.response.200.detection_patterns`; any of, optional) — Example utterances that should invoke this skill. - any of: `array`, `null` - `license` (`get_skill.response.200.license`; any of, optional) — SKILL.md `license`: a license name, or the file in the skill that holds it. - any of: `string`, `null` - `compatibility` (`get_skill.response.200.compatibility`; any of, optional) — SKILL.md `compatibility`: what the skill needs to run (product, packages, network). - any of: `string`, `null` ### 422 Validation Error #### Example ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string", "input": null, "ctx": {} } ] } ``` - `detail` (`get_skill.response.422.detail`; array, optional) - `loc` (`get_skill.response.422.detail.loc`; array, required) - any of: `string`, `integer` - `msg` (`get_skill.response.422.detail.msg`; string, required) - `type` (`get_skill.response.422.detail.type`; string, required) - `input` (`get_skill.response.422.detail.input`; unknown, optional) - `ctx` (`get_skill.response.422.detail.ctx`; object, optional) # Import a skill folder > Create a skill from an Agent Skills folder: SKILL.md's frontmatter ``name`` becomes the key (409 if you already have one), its body the prompt, and every other file is stored with its path. Source: https://truememory-docs.mvstg.com/api/skills/import/ · Markdown: https://truememory-docs.mvstg.com/api/skills/import/index.md Path: AI Gateway API › Skills `POST /skills/import` Create a skill from an Agent Skills folder: SKILL.md's frontmatter ``name`` becomes the key (409 if you already have one), its body the prompt, and every other file is stored with its path. ## Authentication - `HTTPBearer`, http, header `Authorization` ## Header parameters - `X-On-Behalf-Of` (`import_skill.header.X-On-Behalf-Of`; any of, optional) - any of: `string`, `null` - `X-External-User-Id` (`import_skill.header.X-External-User-Id`; any of, optional) - any of: `string`, `null` - `X-App-Id` (`import_skill.header.X-App-Id`; any of, optional) - any of: `string`, `null` ## Request body - `files` (`import_skill.files`; any of, optional) — One .zip or .skill file, or the skill folder's files named by their paths. - any of: `array`, `null` - `files[]` (`import_skill.files[]`; any of, optional) — The same as `files`, under the name the Claude and OpenAI APIs use. - any of: `array`, `null` - `app_ids` (`import_skill.app_ids`; any of, optional) — Apps to assign it to; none means all. - any of: `array`, `null` - `is_active` (`import_skill.is_active`; boolean, optional) — false imports it as a draft (off until switched on); true by default. - default `true` ## Example request ```text { "files": [ "string" ], "files[]": [ "string" ], "app_ids": [ "string" ], "is_active": true } ``` ## Code samples ### cURL ```bash curl --request POST \ --url https://truememory-api.mvstg.com/skills/import \ --header 'Authorization: Bearer ' \ --form-string 'files=string' \ --form-string 'files[]=string' \ --form-string 'app_ids=string' \ --form-string 'is_active=true' ``` ### TypeScript ```typescript const formData = new FormData(); formData.append('files', 'string'); formData.append('files[]', 'string'); formData.append('app_ids', 'string'); formData.append('is_active', 'true'); const url = 'https://truememory-api.mvstg.com/skills/import'; const options = {method: 'POST', headers: {Authorization: 'Bearer '}, body: formData}; fetch(url, options) .then(res => res.json()) .then(json => console.log(json)) .catch(err => console.error(err)); ``` ### Python ```python import requests url = "https://truememory-api.mvstg.com/skills/import" files = [ ("files", (None, "string")), ("files[]", (None, "string")), ("app_ids", (None, "string")), ("is_active", (None, "true")) ] headers = {"Authorization": "Bearer "} response = requests.post(url, files=files, headers=headers) print(response.text) ``` ## Responses ### 201 Successful Response #### Example ```json { "id": "string", "user_id": "string", "app_ids": [ "string" ], "key": "string", "name": "string", "description": "string", "prompt": "string", "category": "string", "categories": [ { "id": "string", "name": "string" } ], "icon": "string", "argument_hint": "string", "allowed_tools": [ "string" ], "metadata": {}, "use_case": "string", "detection_patterns": [ "string" ], "license": "string", "compatibility": "string", "is_preset": true, "is_active": true, "sort_order": 0, "forked_from_skill_id": "string", "usage_count": 0, "last_used_at": "2019-08-24T14:15:22Z", "parent_synced_at": "2019-08-24T14:15:22Z", "created_at": "2019-08-24T14:15:22Z", "updated_at": "2019-08-24T14:15:22Z", "files": [ { "path": "string", "sha256": "string", "size": 0, "content_type": "string", "created_at": "2019-08-24T14:15:22Z", "updated_at": "2019-08-24T14:15:22Z" } ], "ignored_frontmatter": [ "string" ], "app_id": "string" } ``` - `id` (`import_skill.response.201.id`; string, required) - `user_id` (`import_skill.response.201.user_id`; any of, required) — Owner user_id, NULL for presets. - any of: `string`, `null` - `app_ids` (`import_skill.response.201.app_ids`; any of, required) — Apps this skill is assigned to. NULL means all apps (global/unscoped). - any of: `array`, `null` - `key` (`import_skill.response.201.key`; string, required) - `name` (`import_skill.response.201.name`; string, required) - `description` (`import_skill.response.201.description`; any of, required) - any of: `string`, `null` - `prompt` (`import_skill.response.201.prompt`; string, required) - `category` (`import_skill.response.201.category`; any of, required) — Freeform grouping label (legacy string). Distinct from `categories`. - any of: `string`, `null` - `icon` (`import_skill.response.201.icon`; any of, required) - any of: `string`, `null` - `argument_hint` (`import_skill.response.201.argument_hint`; any of, required) - any of: `string`, `null` - `allowed_tools` (`import_skill.response.201.allowed_tools`; array, required) - `metadata` (`import_skill.response.201.metadata`; object, required) - `is_preset` (`import_skill.response.201.is_preset`; boolean, required) - `is_active` (`import_skill.response.201.is_active`; boolean, required) - `sort_order` (`import_skill.response.201.sort_order`; integer, required) - `forked_from_skill_id` (`import_skill.response.201.forked_from_skill_id`; any of, required) - any of: `string`, `null` - `usage_count` (`import_skill.response.201.usage_count`; integer, required) — Global invocation count, bumped via POST /skills/{id}/usage. - `last_used_at` (`import_skill.response.201.last_used_at`; any of, required) — When the skill was last invoked, NULL if never. - any of: `string`, `null` - `parent_synced_at` (`import_skill.response.201.parent_synced_at`; any of, required) — For forks: the preset updated_at this fork is synced to. The fork is stale when its preset's updated_at is newer. NULL for non-forks. - any of: `string`, `null` - `created_at` (`import_skill.response.201.created_at`; string, required) - format `date-time` - `updated_at` (`import_skill.response.201.updated_at`; string, required) - format `date-time` - `files` (`import_skill.response.201.files`; array, required) - `path` (`import_skill.response.201.files.path`; string, required) — Path in the skill folder, e.g. `references/method.md`. - `sha256` (`import_skill.response.201.files.sha256`; string, required) — Hex SHA-256 of the file's bytes. - `size` (`import_skill.response.201.files.size`; integer, required) — Size in bytes. - `content_type` (`import_skill.response.201.files.content_type`; string, required) - `created_at` (`import_skill.response.201.files.created_at`; string, required) - format `date-time` - `updated_at` (`import_skill.response.201.files.updated_at`; string, required) — When this path last got different bytes. - format `date-time` - `app_id` (`import_skill.response.201.app_id`; any of, required) — DEPRECATED, read app_ids instead. Legacy single-app scalar derived from app_ids for pre-multi-app consumers: the first app id, or null for all apps. Lossy for multi-app skills. - any of: `string`, `null` - `categories` (`import_skill.response.201.categories`; array, optional) — Category entities assigned via the registry. Returned by the list endpoints, the single-skill reads (user and admin) and PUT /admin/skills/{skill_id}/categories; omitted by writes. - `id` (`import_skill.response.201.categories.id`; string, required) - `name` (`import_skill.response.201.categories.name`; string, required) - `use_case` (`import_skill.response.201.use_case`; any of, optional) — When this skill applies, in natural language. Read by skill selection. - any of: `string`, `null` - `detection_patterns` (`import_skill.response.201.detection_patterns`; any of, optional) — Example utterances that should invoke this skill. - any of: `array`, `null` - `license` (`import_skill.response.201.license`; any of, optional) — SKILL.md `license`: a license name, or the file in the skill that holds it. - any of: `string`, `null` - `compatibility` (`import_skill.response.201.compatibility`; any of, optional) — SKILL.md `compatibility`: what the skill needs to run (product, packages, network). - any of: `string`, `null` - `ignored_frontmatter` (`import_skill.response.201.ignored_frontmatter`; array, optional) — SKILL.md frontmatter keys with no Truememory field. They were not stored, and an export will not contain them. ### 422 Validation Error #### Example ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string", "input": null, "ctx": {} } ] } ``` - `detail` (`import_skill.response.422.detail`; array, optional) - `loc` (`import_skill.response.422.detail.loc`; array, required) - any of: `string`, `integer` - `msg` (`import_skill.response.422.detail.msg`; string, required) - `type` (`import_skill.response.422.detail.type`; string, required) - `input` (`import_skill.response.422.detail.input`; unknown, optional) - `ctx` (`import_skill.response.422.detail.ctx`; object, optional) # List skills > List available skills (presets + user's custom). Returns {skills, total}. Source: https://truememory-docs.mvstg.com/api/skills/list/ · Markdown: https://truememory-docs.mvstg.com/api/skills/list/index.md Path: AI Gateway API › Skills `GET /skills` List available skills (presets + user's custom). Returns {skills, total}. ## Authentication - `HTTPBearer`, http, header `Authorization` ## Query parameters - `app_id` (`list_skills.query.app_id`; any of, optional) — App scope. Default: matches skills.app_ids plus global rows. With the X-Use-Taxonomy header: an app slug resolved through the category taxonomy (no global rows); an unknown slug 404s. None = no filter. - any of: `string`, `null` - `category` (`list_skills.query.category`; any of, optional) — Filter by category. Default: skills.category string. With the X-Use-Taxonomy header: a categories.id UUID. - any of: `string`, `null` - `is_active` (`list_skills.query.is_active`; any of, optional) — Filter by active flag. - any of: `boolean`, `null` - `search` (`list_skills.query.search`; any of, optional) — ILIKE match on name or description. - any of: `string`, `null` - `limit` (`list_skills.query.limit`; integer, optional) - min 1; max 200; default `50` - `offset` (`list_skills.query.offset`; integer, optional) - min 0; default `0` ## Header parameters - `X-Use-Taxonomy` (`list_skills.header.X-Use-Taxonomy`; boolean, optional) — When true, resolve app_id (as an app slug) and category (as a categories.id UUID) through the admin apps/categories taxonomy instead of the legacy app_ids array and category string. When absent/false, app_id and category use their legacy meanings. - default `false` - `X-On-Behalf-Of` (`list_skills.header.X-On-Behalf-Of`; any of, optional) - any of: `string`, `null` - `X-External-User-Id` (`list_skills.header.X-External-User-Id`; any of, optional) - any of: `string`, `null` - `X-App-Id` (`list_skills.header.X-App-Id`; any of, optional) - any of: `string`, `null` ## Code samples ### cURL ```bash curl --request GET \ --url https://truememory-api.mvstg.com/skills \ --header 'Authorization: Bearer ' ``` ### TypeScript ```typescript const url = 'https://truememory-api.mvstg.com/skills'; const options = {method: 'GET', headers: {Authorization: 'Bearer '}}; fetch(url, options) .then(res => res.json()) .then(json => console.log(json)) .catch(err => console.error(err)); ``` ### Python ```python import requests url = "https://truememory-api.mvstg.com/skills" headers = {"Authorization": "Bearer "} response = requests.get(url, headers=headers) print(response.text) ``` ## Responses ### 200 Successful Response #### Example ```json { "skills": [ { "id": "string", "user_id": "string", "app_ids": [ "string" ], "key": "string", "name": "string", "description": "string", "prompt": "string", "category": "string", "categories": [ { "id": "string", "name": "string" } ], "icon": "string", "argument_hint": "string", "allowed_tools": [ "string" ], "metadata": {}, "use_case": "string", "detection_patterns": [ "string" ], "license": "string", "compatibility": "string", "is_preset": true, "is_active": true, "sort_order": 0, "forked_from_skill_id": "string", "usage_count": 0, "last_used_at": "2019-08-24T14:15:22Z", "parent_synced_at": "2019-08-24T14:15:22Z", "created_at": "2019-08-24T14:15:22Z", "updated_at": "2019-08-24T14:15:22Z", "app_id": "string" } ], "total": 0 } ``` - `skills` (`list_skills.response.200.skills`; array, required) - `id` (`list_skills.response.200.skills.id`; string, required) - `user_id` (`list_skills.response.200.skills.user_id`; any of, required) — Owner user_id, NULL for presets. - any of: `string`, `null` - `app_ids` (`list_skills.response.200.skills.app_ids`; any of, required) — Apps this skill is assigned to. NULL means all apps (global/unscoped). - any of: `array`, `null` - `key` (`list_skills.response.200.skills.key`; string, required) - `name` (`list_skills.response.200.skills.name`; string, required) - `description` (`list_skills.response.200.skills.description`; any of, required) - any of: `string`, `null` - `prompt` (`list_skills.response.200.skills.prompt`; string, required) - `category` (`list_skills.response.200.skills.category`; any of, required) — Freeform grouping label (legacy string). Distinct from `categories`. - any of: `string`, `null` - `icon` (`list_skills.response.200.skills.icon`; any of, required) - any of: `string`, `null` - `argument_hint` (`list_skills.response.200.skills.argument_hint`; any of, required) - any of: `string`, `null` - `allowed_tools` (`list_skills.response.200.skills.allowed_tools`; array, required) - `metadata` (`list_skills.response.200.skills.metadata`; object, required) - `is_preset` (`list_skills.response.200.skills.is_preset`; boolean, required) - `is_active` (`list_skills.response.200.skills.is_active`; boolean, required) - `sort_order` (`list_skills.response.200.skills.sort_order`; integer, required) - `forked_from_skill_id` (`list_skills.response.200.skills.forked_from_skill_id`; any of, required) - any of: `string`, `null` - `usage_count` (`list_skills.response.200.skills.usage_count`; integer, required) — Global invocation count, bumped via POST /skills/{id}/usage. - `last_used_at` (`list_skills.response.200.skills.last_used_at`; any of, required) — When the skill was last invoked, NULL if never. - any of: `string`, `null` - `parent_synced_at` (`list_skills.response.200.skills.parent_synced_at`; any of, required) — For forks: the preset updated_at this fork is synced to. The fork is stale when its preset's updated_at is newer. NULL for non-forks. - any of: `string`, `null` - `created_at` (`list_skills.response.200.skills.created_at`; string, required) - format `date-time` - `updated_at` (`list_skills.response.200.skills.updated_at`; string, required) - format `date-time` - `app_id` (`list_skills.response.200.skills.app_id`; any of, required) — DEPRECATED, read app_ids instead. Legacy single-app scalar derived from app_ids for pre-multi-app consumers: the first app id, or null for all apps. Lossy for multi-app skills. - any of: `string`, `null` - `categories` (`list_skills.response.200.skills.categories`; array, optional) — Category entities assigned via the registry. Returned by the list endpoints, the single-skill reads (user and admin) and PUT /admin/skills/{skill_id}/categories; omitted by writes. - `id` (`list_skills.response.200.skills.categories.id`; string, required) - `name` (`list_skills.response.200.skills.categories.name`; string, required) - `use_case` (`list_skills.response.200.skills.use_case`; any of, optional) — When this skill applies, in natural language. Read by skill selection. - any of: `string`, `null` - `detection_patterns` (`list_skills.response.200.skills.detection_patterns`; any of, optional) — Example utterances that should invoke this skill. - any of: `array`, `null` - `license` (`list_skills.response.200.skills.license`; any of, optional) — SKILL.md `license`: a license name, or the file in the skill that holds it. - any of: `string`, `null` - `compatibility` (`list_skills.response.200.skills.compatibility`; any of, optional) — SKILL.md `compatibility`: what the skill needs to run (product, packages, network). - any of: `string`, `null` - `total` (`list_skills.response.200.total`; integer, required) — Total rows matching the filters before LIMIT/OFFSET. ### 422 Validation Error #### Example ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string", "input": null, "ctx": {} } ] } ``` - `detail` (`list_skills.response.422.detail`; array, optional) - `loc` (`list_skills.response.422.detail.loc`; array, required) - any of: `string`, `integer` - `msg` (`list_skills.response.422.detail.msg`; string, required) - `type` (`list_skills.response.422.detail.type`; string, required) - `input` (`list_skills.response.422.detail.input`; unknown, optional) - `ctx` (`list_skills.response.422.detail.ctx`; object, optional) # Replace a skill from a skill folder > Replace one of your skills with a new upload of the same skill (same ``name``): SKILL.md and the whole file tree, as one version in its history. Source: https://truememory-docs.mvstg.com/api/skills/post-import/ · Markdown: https://truememory-docs.mvstg.com/api/skills/post-import/index.md Path: AI Gateway API › Skills `POST /skills/{skill_id}/import` Replace one of your skills with a new upload of the same skill (same ``name``): SKILL.md and the whole file tree, as one version in its history. ## Authentication - `HTTPBearer`, http, header `Authorization` ## Path parameters - `skill_id` (`reimport_skill.path.skill_id`; string, required) - format `uuid` ## Header parameters - `X-On-Behalf-Of` (`reimport_skill.header.X-On-Behalf-Of`; any of, optional) - any of: `string`, `null` - `X-External-User-Id` (`reimport_skill.header.X-External-User-Id`; any of, optional) - any of: `string`, `null` - `X-App-Id` (`reimport_skill.header.X-App-Id`; any of, optional) - any of: `string`, `null` ## Request body - `files` (`reimport_skill.files`; any of, optional) — One .zip or .skill file, or the skill folder's files named by their paths. - any of: `array`, `null` - `files[]` (`reimport_skill.files[]`; any of, optional) — The same as `files`, under the name the Claude and OpenAI APIs use. - any of: `array`, `null` ## Example request ```text { "files": [ "string" ], "files[]": [ "string" ] } ``` ## Code samples ### cURL ```bash curl --request POST \ --url 'https://truememory-api.mvstg.com/skills//import' \ --header 'Authorization: Bearer ' \ --form-string 'files=string' \ --form-string 'files[]=string' ``` ### TypeScript ```typescript const formData = new FormData(); formData.append('files', 'string'); formData.append('files[]', 'string'); const url = 'https://truememory-api.mvstg.com/skills//import'; const options = {method: 'POST', headers: {Authorization: 'Bearer '}, body: formData}; fetch(url, options) .then(res => res.json()) .then(json => console.log(json)) .catch(err => console.error(err)); ``` ### Python ```python import requests url = "https://truememory-api.mvstg.com/skills//import" files = [ ("files", (None, "string")), ("files[]", (None, "string")) ] headers = {"Authorization": "Bearer "} response = requests.post(url, files=files, headers=headers) print(response.text) ``` ## Responses ### 200 Successful Response #### Example ```json { "id": "string", "user_id": "string", "app_ids": [ "string" ], "key": "string", "name": "string", "description": "string", "prompt": "string", "category": "string", "categories": [ { "id": "string", "name": "string" } ], "icon": "string", "argument_hint": "string", "allowed_tools": [ "string" ], "metadata": {}, "use_case": "string", "detection_patterns": [ "string" ], "license": "string", "compatibility": "string", "is_preset": true, "is_active": true, "sort_order": 0, "forked_from_skill_id": "string", "usage_count": 0, "last_used_at": "2019-08-24T14:15:22Z", "parent_synced_at": "2019-08-24T14:15:22Z", "created_at": "2019-08-24T14:15:22Z", "updated_at": "2019-08-24T14:15:22Z", "files": [ { "path": "string", "sha256": "string", "size": 0, "content_type": "string", "created_at": "2019-08-24T14:15:22Z", "updated_at": "2019-08-24T14:15:22Z" } ], "ignored_frontmatter": [ "string" ], "app_id": "string" } ``` - `id` (`reimport_skill.response.200.id`; string, required) - `user_id` (`reimport_skill.response.200.user_id`; any of, required) — Owner user_id, NULL for presets. - any of: `string`, `null` - `app_ids` (`reimport_skill.response.200.app_ids`; any of, required) — Apps this skill is assigned to. NULL means all apps (global/unscoped). - any of: `array`, `null` - `key` (`reimport_skill.response.200.key`; string, required) - `name` (`reimport_skill.response.200.name`; string, required) - `description` (`reimport_skill.response.200.description`; any of, required) - any of: `string`, `null` - `prompt` (`reimport_skill.response.200.prompt`; string, required) - `category` (`reimport_skill.response.200.category`; any of, required) — Freeform grouping label (legacy string). Distinct from `categories`. - any of: `string`, `null` - `icon` (`reimport_skill.response.200.icon`; any of, required) - any of: `string`, `null` - `argument_hint` (`reimport_skill.response.200.argument_hint`; any of, required) - any of: `string`, `null` - `allowed_tools` (`reimport_skill.response.200.allowed_tools`; array, required) - `metadata` (`reimport_skill.response.200.metadata`; object, required) - `is_preset` (`reimport_skill.response.200.is_preset`; boolean, required) - `is_active` (`reimport_skill.response.200.is_active`; boolean, required) - `sort_order` (`reimport_skill.response.200.sort_order`; integer, required) - `forked_from_skill_id` (`reimport_skill.response.200.forked_from_skill_id`; any of, required) - any of: `string`, `null` - `usage_count` (`reimport_skill.response.200.usage_count`; integer, required) — Global invocation count, bumped via POST /skills/{id}/usage. - `last_used_at` (`reimport_skill.response.200.last_used_at`; any of, required) — When the skill was last invoked, NULL if never. - any of: `string`, `null` - `parent_synced_at` (`reimport_skill.response.200.parent_synced_at`; any of, required) — For forks: the preset updated_at this fork is synced to. The fork is stale when its preset's updated_at is newer. NULL for non-forks. - any of: `string`, `null` - `created_at` (`reimport_skill.response.200.created_at`; string, required) - format `date-time` - `updated_at` (`reimport_skill.response.200.updated_at`; string, required) - format `date-time` - `files` (`reimport_skill.response.200.files`; array, required) - `path` (`reimport_skill.response.200.files.path`; string, required) — Path in the skill folder, e.g. `references/method.md`. - `sha256` (`reimport_skill.response.200.files.sha256`; string, required) — Hex SHA-256 of the file's bytes. - `size` (`reimport_skill.response.200.files.size`; integer, required) — Size in bytes. - `content_type` (`reimport_skill.response.200.files.content_type`; string, required) - `created_at` (`reimport_skill.response.200.files.created_at`; string, required) - format `date-time` - `updated_at` (`reimport_skill.response.200.files.updated_at`; string, required) — When this path last got different bytes. - format `date-time` - `app_id` (`reimport_skill.response.200.app_id`; any of, required) — DEPRECATED, read app_ids instead. Legacy single-app scalar derived from app_ids for pre-multi-app consumers: the first app id, or null for all apps. Lossy for multi-app skills. - any of: `string`, `null` - `categories` (`reimport_skill.response.200.categories`; array, optional) — Category entities assigned via the registry. Returned by the list endpoints, the single-skill reads (user and admin) and PUT /admin/skills/{skill_id}/categories; omitted by writes. - `id` (`reimport_skill.response.200.categories.id`; string, required) - `name` (`reimport_skill.response.200.categories.name`; string, required) - `use_case` (`reimport_skill.response.200.use_case`; any of, optional) — When this skill applies, in natural language. Read by skill selection. - any of: `string`, `null` - `detection_patterns` (`reimport_skill.response.200.detection_patterns`; any of, optional) — Example utterances that should invoke this skill. - any of: `array`, `null` - `license` (`reimport_skill.response.200.license`; any of, optional) — SKILL.md `license`: a license name, or the file in the skill that holds it. - any of: `string`, `null` - `compatibility` (`reimport_skill.response.200.compatibility`; any of, optional) — SKILL.md `compatibility`: what the skill needs to run (product, packages, network). - any of: `string`, `null` - `ignored_frontmatter` (`reimport_skill.response.200.ignored_frontmatter`; array, optional) — SKILL.md frontmatter keys with no Truememory field. They were not stored, and an export will not contain them. ### 422 Validation Error #### Example ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string", "input": null, "ctx": {} } ] } ``` - `detail` (`reimport_skill.response.422.detail`; array, optional) - `loc` (`reimport_skill.response.422.detail.loc`; array, required) - any of: `string`, `integer` - `msg` (`reimport_skill.response.422.detail.msg`; string, required) - `type` (`reimport_skill.response.422.detail.type`; string, required) - `input` (`reimport_skill.response.422.detail.input`; unknown, optional) - `ctx` (`reimport_skill.response.422.detail.ctx`; object, optional) # Update a skill > Update a custom (non-preset) skill owned by the authenticated user. Source: https://truememory-docs.mvstg.com/api/skills/update/ · Markdown: https://truememory-docs.mvstg.com/api/skills/update/index.md Path: AI Gateway API › Skills `PUT /skills/{skill_id}` Update a custom (non-preset) skill owned by the authenticated user. ## Authentication - `HTTPBearer`, http, header `Authorization` ## Path parameters - `skill_id` (`update_skill.path.skill_id`; string, required) ## Header parameters - `X-On-Behalf-Of` (`update_skill.header.X-On-Behalf-Of`; any of, optional) - any of: `string`, `null` - `X-External-User-Id` (`update_skill.header.X-External-User-Id`; any of, optional) - any of: `string`, `null` - `X-App-Id` (`update_skill.header.X-App-Id`; any of, optional) - any of: `string`, `null` ## Request body _Required._ - `key` (`update_skill.key`; any of, optional) - any of: `string`, `null` - `name` (`update_skill.name`; any of, optional) - any of: `string`, `null` - `description` (`update_skill.description`; any of, optional) - any of: `string`, `null` - `prompt` (`update_skill.prompt`; any of, optional) - any of: `string`, `null` - `category` (`update_skill.category`; any of, optional) - any of: `string`, `null` - `app_ids` (`update_skill.app_ids`; any of, optional) - any of: `array`, `null` - `icon` (`update_skill.icon`; any of, optional) - any of: `string`, `null` - `argument_hint` (`update_skill.argument_hint`; any of, optional) - any of: `string`, `null` - `allowed_tools` (`update_skill.allowed_tools`; any of, optional) - any of: `array`, `null` - `metadata` (`update_skill.metadata`; any of, optional) - any of: `object`, `null` - `is_active` (`update_skill.is_active`; any of, optional) - any of: `boolean`, `null` - `sort_order` (`update_skill.sort_order`; any of, optional) - any of: `integer`, `null` - `use_case` (`update_skill.use_case`; any of, optional) - any of: `string`, `null` - `detection_patterns` (`update_skill.detection_patterns`; any of, optional) - any of: `array`, `null` - `license` (`update_skill.license`; any of, optional) - any of: `string`, `null` - `compatibility` (`update_skill.compatibility`; any of, optional) - any of: `string`, `null` - `forked_from_skill_id` (`update_skill.forked_from_skill_id`; any of, optional) - any of: `string`, `null` - `parent_synced_at` (`update_skill.parent_synced_at`; any of, optional) — Fork baseline: set to the preset's updated_at to mark the fork synced (e.g. after a client-side merge). Null clears it. - any of: `string`, `null` ## Example request ```json { "key": "string", "name": "string", "description": "string", "prompt": "string", "category": "string", "app_ids": [ "string" ], "icon": "string", "argument_hint": "string", "allowed_tools": [ "string" ], "metadata": {}, "is_active": true, "sort_order": 0, "use_case": "string", "detection_patterns": [ "string" ], "license": "string", "compatibility": "string", "forked_from_skill_id": "string", "parent_synced_at": "2019-08-24T14:15:22Z" } ``` ## Code samples ### cURL ```bash curl --request PUT \ --url 'https://truememory-api.mvstg.com/skills/' \ --header 'Authorization: Bearer ' \ --header 'Content-Type: application/json' \ --data ' { "key": "string", "name": "string", "description": "string", "prompt": "string", "category": "string", "app_ids": [ "string" ], "icon": "string", "argument_hint": "string", "allowed_tools": [ "string" ], "metadata": {}, "is_active": true, "sort_order": 0, "use_case": "string", "detection_patterns": [ "string" ], "license": "string", "compatibility": "string", "forked_from_skill_id": "string", "parent_synced_at": "2019-08-24T14:15:22Z" } ' ``` ### TypeScript ```typescript const url = 'https://truememory-api.mvstg.com/skills/'; const options = { method: 'PUT', headers: {'Content-Type': 'application/json', Authorization: 'Bearer '}, body: JSON.stringify({ key: 'string', name: 'string', description: 'string', prompt: 'string', category: 'string', app_ids: ['string'], icon: 'string', argument_hint: 'string', allowed_tools: ['string'], metadata: {}, is_active: true, sort_order: 0, use_case: 'string', detection_patterns: ['string'], license: 'string', compatibility: 'string', forked_from_skill_id: 'string', parent_synced_at: '2019-08-24T14:15:22Z' }) }; fetch(url, options) .then(res => res.json()) .then(json => console.log(json)) .catch(err => console.error(err)); ``` ### Python ```python import requests url = "https://truememory-api.mvstg.com/skills/" payload = { "key": "string", "name": "string", "description": "string", "prompt": "string", "category": "string", "app_ids": ["string"], "icon": "string", "argument_hint": "string", "allowed_tools": ["string"], "metadata": {}, "is_active": True, "sort_order": 0, "use_case": "string", "detection_patterns": ["string"], "license": "string", "compatibility": "string", "forked_from_skill_id": "string", "parent_synced_at": "2019-08-24T14:15:22Z" } headers = { "Content-Type": "application/json", "Authorization": "Bearer " } response = requests.put(url, json=payload, headers=headers) print(response.text) ``` ## Responses ### 200 Successful Response #### Example ```json { "id": "string", "user_id": "string", "app_ids": [ "string" ], "key": "string", "name": "string", "description": "string", "prompt": "string", "category": "string", "categories": [ { "id": "string", "name": "string" } ], "icon": "string", "argument_hint": "string", "allowed_tools": [ "string" ], "metadata": {}, "use_case": "string", "detection_patterns": [ "string" ], "license": "string", "compatibility": "string", "is_preset": true, "is_active": true, "sort_order": 0, "forked_from_skill_id": "string", "usage_count": 0, "last_used_at": "2019-08-24T14:15:22Z", "parent_synced_at": "2019-08-24T14:15:22Z", "created_at": "2019-08-24T14:15:22Z", "updated_at": "2019-08-24T14:15:22Z", "app_id": "string" } ``` - `id` (`update_skill.response.200.id`; string, required) - `user_id` (`update_skill.response.200.user_id`; any of, required) — Owner user_id, NULL for presets. - any of: `string`, `null` - `app_ids` (`update_skill.response.200.app_ids`; any of, required) — Apps this skill is assigned to. NULL means all apps (global/unscoped). - any of: `array`, `null` - `key` (`update_skill.response.200.key`; string, required) - `name` (`update_skill.response.200.name`; string, required) - `description` (`update_skill.response.200.description`; any of, required) - any of: `string`, `null` - `prompt` (`update_skill.response.200.prompt`; string, required) - `category` (`update_skill.response.200.category`; any of, required) — Freeform grouping label (legacy string). Distinct from `categories`. - any of: `string`, `null` - `icon` (`update_skill.response.200.icon`; any of, required) - any of: `string`, `null` - `argument_hint` (`update_skill.response.200.argument_hint`; any of, required) - any of: `string`, `null` - `allowed_tools` (`update_skill.response.200.allowed_tools`; array, required) - `metadata` (`update_skill.response.200.metadata`; object, required) - `is_preset` (`update_skill.response.200.is_preset`; boolean, required) - `is_active` (`update_skill.response.200.is_active`; boolean, required) - `sort_order` (`update_skill.response.200.sort_order`; integer, required) - `forked_from_skill_id` (`update_skill.response.200.forked_from_skill_id`; any of, required) - any of: `string`, `null` - `usage_count` (`update_skill.response.200.usage_count`; integer, required) — Global invocation count, bumped via POST /skills/{id}/usage. - `last_used_at` (`update_skill.response.200.last_used_at`; any of, required) — When the skill was last invoked, NULL if never. - any of: `string`, `null` - `parent_synced_at` (`update_skill.response.200.parent_synced_at`; any of, required) — For forks: the preset updated_at this fork is synced to. The fork is stale when its preset's updated_at is newer. NULL for non-forks. - any of: `string`, `null` - `created_at` (`update_skill.response.200.created_at`; string, required) - format `date-time` - `updated_at` (`update_skill.response.200.updated_at`; string, required) - format `date-time` - `app_id` (`update_skill.response.200.app_id`; any of, required) — DEPRECATED, read app_ids instead. Legacy single-app scalar derived from app_ids for pre-multi-app consumers: the first app id, or null for all apps. Lossy for multi-app skills. - any of: `string`, `null` - `categories` (`update_skill.response.200.categories`; array, optional) — Category entities assigned via the registry. Returned by the list endpoints, the single-skill reads (user and admin) and PUT /admin/skills/{skill_id}/categories; omitted by writes. - `id` (`update_skill.response.200.categories.id`; string, required) - `name` (`update_skill.response.200.categories.name`; string, required) - `use_case` (`update_skill.response.200.use_case`; any of, optional) — When this skill applies, in natural language. Read by skill selection. - any of: `string`, `null` - `detection_patterns` (`update_skill.response.200.detection_patterns`; any of, optional) — Example utterances that should invoke this skill. - any of: `array`, `null` - `license` (`update_skill.response.200.license`; any of, optional) — SKILL.md `license`: a license name, or the file in the skill that holds it. - any of: `string`, `null` - `compatibility` (`update_skill.response.200.compatibility`; any of, optional) — SKILL.md `compatibility`: what the skill needs to run (product, packages, network). - any of: `string`, `null` ### 422 Validation Error #### Example ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string", "input": null, "ctx": {} } ] } ``` - `detail` (`update_skill.response.422.detail`; array, optional) - `loc` (`update_skill.response.422.detail.loc`; array, required) - any of: `string`, `integer` - `msg` (`update_skill.response.422.detail.msg`; string, required) - `type` (`update_skill.response.422.detail.type`; string, required) - `input` (`update_skill.response.422.detail.input`; unknown, optional) - `ctx` (`update_skill.response.422.detail.ctx`; object, optional) # Record a skill invocation > Bump the skill's global usage counter and ``last_used_at``. The caller invokes this when it routes a request through the skill — TIE has no server-side invocation surface, so usage is client-reported. Restricted to skills the user can see (preset or owned). Returns the updated skill. Source: https://truememory-docs.mvstg.com/api/skills/usage/ · Markdown: https://truememory-docs.mvstg.com/api/skills/usage/index.md Path: AI Gateway API › Skills `POST /skills/{skill_id}/usage` Bump the skill's global usage counter and ``last_used_at``. The caller invokes this when it routes a request through the skill — TIE has no server-side invocation surface, so usage is client-reported. Restricted to skills the user can see (preset or owned). Returns the updated skill. ## Authentication - `HTTPBearer`, http, header `Authorization` ## Path parameters - `skill_id` (`record_skill_usage.path.skill_id`; string, required) ## Header parameters - `X-On-Behalf-Of` (`record_skill_usage.header.X-On-Behalf-Of`; any of, optional) - any of: `string`, `null` - `X-External-User-Id` (`record_skill_usage.header.X-External-User-Id`; any of, optional) - any of: `string`, `null` - `X-App-Id` (`record_skill_usage.header.X-App-Id`; any of, optional) - any of: `string`, `null` ## Code samples ### cURL ```bash curl --request POST \ --url 'https://truememory-api.mvstg.com/skills//usage' \ --header 'Authorization: Bearer ' ``` ### TypeScript ```typescript const url = 'https://truememory-api.mvstg.com/skills//usage'; const options = {method: 'POST', headers: {Authorization: 'Bearer '}}; fetch(url, options) .then(res => res.json()) .then(json => console.log(json)) .catch(err => console.error(err)); ``` ### Python ```python import requests url = "https://truememory-api.mvstg.com/skills//usage" headers = {"Authorization": "Bearer "} response = requests.post(url, headers=headers) print(response.text) ``` ## Responses ### 200 Successful Response #### Example ```json { "id": "string", "user_id": "string", "app_ids": [ "string" ], "key": "string", "name": "string", "description": "string", "prompt": "string", "category": "string", "categories": [ { "id": "string", "name": "string" } ], "icon": "string", "argument_hint": "string", "allowed_tools": [ "string" ], "metadata": {}, "use_case": "string", "detection_patterns": [ "string" ], "license": "string", "compatibility": "string", "is_preset": true, "is_active": true, "sort_order": 0, "forked_from_skill_id": "string", "usage_count": 0, "last_used_at": "2019-08-24T14:15:22Z", "parent_synced_at": "2019-08-24T14:15:22Z", "created_at": "2019-08-24T14:15:22Z", "updated_at": "2019-08-24T14:15:22Z", "app_id": "string" } ``` - `id` (`record_skill_usage.response.200.id`; string, required) - `user_id` (`record_skill_usage.response.200.user_id`; any of, required) — Owner user_id, NULL for presets. - any of: `string`, `null` - `app_ids` (`record_skill_usage.response.200.app_ids`; any of, required) — Apps this skill is assigned to. NULL means all apps (global/unscoped). - any of: `array`, `null` - `key` (`record_skill_usage.response.200.key`; string, required) - `name` (`record_skill_usage.response.200.name`; string, required) - `description` (`record_skill_usage.response.200.description`; any of, required) - any of: `string`, `null` - `prompt` (`record_skill_usage.response.200.prompt`; string, required) - `category` (`record_skill_usage.response.200.category`; any of, required) — Freeform grouping label (legacy string). Distinct from `categories`. - any of: `string`, `null` - `icon` (`record_skill_usage.response.200.icon`; any of, required) - any of: `string`, `null` - `argument_hint` (`record_skill_usage.response.200.argument_hint`; any of, required) - any of: `string`, `null` - `allowed_tools` (`record_skill_usage.response.200.allowed_tools`; array, required) - `metadata` (`record_skill_usage.response.200.metadata`; object, required) - `is_preset` (`record_skill_usage.response.200.is_preset`; boolean, required) - `is_active` (`record_skill_usage.response.200.is_active`; boolean, required) - `sort_order` (`record_skill_usage.response.200.sort_order`; integer, required) - `forked_from_skill_id` (`record_skill_usage.response.200.forked_from_skill_id`; any of, required) - any of: `string`, `null` - `usage_count` (`record_skill_usage.response.200.usage_count`; integer, required) — Global invocation count, bumped via POST /skills/{id}/usage. - `last_used_at` (`record_skill_usage.response.200.last_used_at`; any of, required) — When the skill was last invoked, NULL if never. - any of: `string`, `null` - `parent_synced_at` (`record_skill_usage.response.200.parent_synced_at`; any of, required) — For forks: the preset updated_at this fork is synced to. The fork is stale when its preset's updated_at is newer. NULL for non-forks. - any of: `string`, `null` - `created_at` (`record_skill_usage.response.200.created_at`; string, required) - format `date-time` - `updated_at` (`record_skill_usage.response.200.updated_at`; string, required) - format `date-time` - `app_id` (`record_skill_usage.response.200.app_id`; any of, required) — DEPRECATED, read app_ids instead. Legacy single-app scalar derived from app_ids for pre-multi-app consumers: the first app id, or null for all apps. Lossy for multi-app skills. - any of: `string`, `null` - `categories` (`record_skill_usage.response.200.categories`; array, optional) — Category entities assigned via the registry. Returned by the list endpoints, the single-skill reads (user and admin) and PUT /admin/skills/{skill_id}/categories; omitted by writes. - `id` (`record_skill_usage.response.200.categories.id`; string, required) - `name` (`record_skill_usage.response.200.categories.name`; string, required) - `use_case` (`record_skill_usage.response.200.use_case`; any of, optional) — When this skill applies, in natural language. Read by skill selection. - any of: `string`, `null` - `detection_patterns` (`record_skill_usage.response.200.detection_patterns`; any of, optional) — Example utterances that should invoke this skill. - any of: `array`, `null` - `license` (`record_skill_usage.response.200.license`; any of, optional) — SKILL.md `license`: a license name, or the file in the skill that holds it. - any of: `string`, `null` - `compatibility` (`record_skill_usage.response.200.compatibility`; any of, optional) — SKILL.md `compatibility`: what the skill needs to run (product, packages, network). - any of: `string`, `null` ### 422 Validation Error #### Example ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string", "input": null, "ctx": {} } ] } ``` - `detail` (`record_skill_usage.response.422.detail`; array, optional) - `loc` (`record_skill_usage.response.422.detail.loc`; array, required) - any of: `string`, `integer` - `msg` (`record_skill_usage.response.422.detail.msg`; string, required) - `type` (`record_skill_usage.response.422.detail.type`; string, required) - `input` (`record_skill_usage.response.422.detail.input`; unknown, optional) - `ctx` (`record_skill_usage.response.422.detail.ctx`; object, optional) # List skill versions > Version history for a visible skill, newest first. Each version's ``snapshot`` is the row state *before* the edit that created it. Returns {versions, total}. Source: https://truememory-docs.mvstg.com/api/skills/versions/list/ · Markdown: https://truememory-docs.mvstg.com/api/skills/versions/list/index.md Path: AI Gateway API › Skills `GET /skills/{skill_id}/versions` Version history for a visible skill, newest first. Each version's ``snapshot`` is the row state *before* the edit that created it. Returns {versions, total}. ## Authentication - `HTTPBearer`, http, header `Authorization` ## Path parameters - `skill_id` (`list_skill_versions.path.skill_id`; string, required) ## Query parameters - `limit` (`list_skill_versions.query.limit`; integer, optional) - min 1; max 200; default `50` - `offset` (`list_skill_versions.query.offset`; integer, optional) - min 0; default `0` ## Header parameters - `X-On-Behalf-Of` (`list_skill_versions.header.X-On-Behalf-Of`; any of, optional) - any of: `string`, `null` - `X-External-User-Id` (`list_skill_versions.header.X-External-User-Id`; any of, optional) - any of: `string`, `null` - `X-App-Id` (`list_skill_versions.header.X-App-Id`; any of, optional) - any of: `string`, `null` ## Code samples ### cURL ```bash curl --request GET \ --url 'https://truememory-api.mvstg.com/skills//versions' \ --header 'Authorization: Bearer ' ``` ### TypeScript ```typescript const url = 'https://truememory-api.mvstg.com/skills//versions'; const options = {method: 'GET', headers: {Authorization: 'Bearer '}}; fetch(url, options) .then(res => res.json()) .then(json => console.log(json)) .catch(err => console.error(err)); ``` ### Python ```python import requests url = "https://truememory-api.mvstg.com/skills//versions" headers = {"Authorization": "Bearer "} response = requests.get(url, headers=headers) print(response.text) ``` ## Responses ### 200 Successful Response #### Example ```json { "versions": [ { "id": "string", "skill_id": "string", "version_number": 0, "snapshot": {}, "edited_by": "string", "created_at": "2019-08-24T14:15:22Z" } ], "total": 0 } ``` - `versions` (`list_skill_versions.response.200.versions`; array, required) - `id` (`list_skill_versions.response.200.versions.id`; string, required) - `skill_id` (`list_skill_versions.response.200.versions.skill_id`; string, required) - `version_number` (`list_skill_versions.response.200.versions.version_number`; integer, required) — 1-based, increments per edit. - `snapshot` (`list_skill_versions.response.200.versions.snapshot`; object, required) — Skill fields as they were before the edit. - `edited_by` (`list_skill_versions.response.200.versions.edited_by`; any of, required) — user_id of the editor, NULL for service edits. - any of: `string`, `null` - `created_at` (`list_skill_versions.response.200.versions.created_at`; string, required) - format `date-time` - `total` (`list_skill_versions.response.200.total`; integer, required) — Total versions before LIMIT/OFFSET. ### 422 Validation Error #### Example ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string", "input": null, "ctx": {} } ] } ``` - `detail` (`list_skill_versions.response.422.detail`; array, optional) - `loc` (`list_skill_versions.response.422.detail.loc`; array, required) - any of: `string`, `integer` - `msg` (`list_skill_versions.response.422.detail.msg`; string, required) - `type` (`list_skill_versions.response.422.detail.type`; string, required) - `input` (`list_skill_versions.response.422.detail.input`; unknown, optional) - `ctx` (`list_skill_versions.response.422.detail.ctx`; object, optional) # Restore a skill version > Restore an owned (non-preset) skill to a previous version. The pre-restore state is recorded as a new version, so a restore can itself be undone. Source: https://truememory-docs.mvstg.com/api/skills/versions/restore/ · Markdown: https://truememory-docs.mvstg.com/api/skills/versions/restore/index.md Path: AI Gateway API › Skills `POST /skills/{skill_id}/versions/{version_number}/restore` Restore an owned (non-preset) skill to a previous version. The pre-restore state is recorded as a new version, so a restore can itself be undone. ## Authentication - `HTTPBearer`, http, header `Authorization` ## Path parameters - `skill_id` (`restore_skill_version.path.skill_id`; string, required) - `version_number` (`restore_skill_version.path.version_number`; integer, required) - min 1 ## Header parameters - `X-On-Behalf-Of` (`restore_skill_version.header.X-On-Behalf-Of`; any of, optional) - any of: `string`, `null` - `X-External-User-Id` (`restore_skill_version.header.X-External-User-Id`; any of, optional) - any of: `string`, `null` - `X-App-Id` (`restore_skill_version.header.X-App-Id`; any of, optional) - any of: `string`, `null` ## Code samples ### cURL ```bash curl --request POST \ --url 'https://truememory-api.mvstg.com/skills//versions//restore' \ --header 'Authorization: Bearer ' ``` ### TypeScript ```typescript const url = 'https://truememory-api.mvstg.com/skills//versions//restore'; const options = {method: 'POST', headers: {Authorization: 'Bearer '}}; fetch(url, options) .then(res => res.json()) .then(json => console.log(json)) .catch(err => console.error(err)); ``` ### Python ```python import requests url = "https://truememory-api.mvstg.com/skills//versions//restore" headers = {"Authorization": "Bearer "} response = requests.post(url, headers=headers) print(response.text) ``` ## Responses ### 200 Successful Response #### Example ```json { "id": "string", "user_id": "string", "app_ids": [ "string" ], "key": "string", "name": "string", "description": "string", "prompt": "string", "category": "string", "categories": [ { "id": "string", "name": "string" } ], "icon": "string", "argument_hint": "string", "allowed_tools": [ "string" ], "metadata": {}, "use_case": "string", "detection_patterns": [ "string" ], "license": "string", "compatibility": "string", "is_preset": true, "is_active": true, "sort_order": 0, "forked_from_skill_id": "string", "usage_count": 0, "last_used_at": "2019-08-24T14:15:22Z", "parent_synced_at": "2019-08-24T14:15:22Z", "created_at": "2019-08-24T14:15:22Z", "updated_at": "2019-08-24T14:15:22Z", "app_id": "string" } ``` - `id` (`restore_skill_version.response.200.id`; string, required) - `user_id` (`restore_skill_version.response.200.user_id`; any of, required) — Owner user_id, NULL for presets. - any of: `string`, `null` - `app_ids` (`restore_skill_version.response.200.app_ids`; any of, required) — Apps this skill is assigned to. NULL means all apps (global/unscoped). - any of: `array`, `null` - `key` (`restore_skill_version.response.200.key`; string, required) - `name` (`restore_skill_version.response.200.name`; string, required) - `description` (`restore_skill_version.response.200.description`; any of, required) - any of: `string`, `null` - `prompt` (`restore_skill_version.response.200.prompt`; string, required) - `category` (`restore_skill_version.response.200.category`; any of, required) — Freeform grouping label (legacy string). Distinct from `categories`. - any of: `string`, `null` - `icon` (`restore_skill_version.response.200.icon`; any of, required) - any of: `string`, `null` - `argument_hint` (`restore_skill_version.response.200.argument_hint`; any of, required) - any of: `string`, `null` - `allowed_tools` (`restore_skill_version.response.200.allowed_tools`; array, required) - `metadata` (`restore_skill_version.response.200.metadata`; object, required) - `is_preset` (`restore_skill_version.response.200.is_preset`; boolean, required) - `is_active` (`restore_skill_version.response.200.is_active`; boolean, required) - `sort_order` (`restore_skill_version.response.200.sort_order`; integer, required) - `forked_from_skill_id` (`restore_skill_version.response.200.forked_from_skill_id`; any of, required) - any of: `string`, `null` - `usage_count` (`restore_skill_version.response.200.usage_count`; integer, required) — Global invocation count, bumped via POST /skills/{id}/usage. - `last_used_at` (`restore_skill_version.response.200.last_used_at`; any of, required) — When the skill was last invoked, NULL if never. - any of: `string`, `null` - `parent_synced_at` (`restore_skill_version.response.200.parent_synced_at`; any of, required) — For forks: the preset updated_at this fork is synced to. The fork is stale when its preset's updated_at is newer. NULL for non-forks. - any of: `string`, `null` - `created_at` (`restore_skill_version.response.200.created_at`; string, required) - format `date-time` - `updated_at` (`restore_skill_version.response.200.updated_at`; string, required) - format `date-time` - `app_id` (`restore_skill_version.response.200.app_id`; any of, required) — DEPRECATED, read app_ids instead. Legacy single-app scalar derived from app_ids for pre-multi-app consumers: the first app id, or null for all apps. Lossy for multi-app skills. - any of: `string`, `null` - `categories` (`restore_skill_version.response.200.categories`; array, optional) — Category entities assigned via the registry. Returned by the list endpoints, the single-skill reads (user and admin) and PUT /admin/skills/{skill_id}/categories; omitted by writes. - `id` (`restore_skill_version.response.200.categories.id`; string, required) - `name` (`restore_skill_version.response.200.categories.name`; string, required) - `use_case` (`restore_skill_version.response.200.use_case`; any of, optional) — When this skill applies, in natural language. Read by skill selection. - any of: `string`, `null` - `detection_patterns` (`restore_skill_version.response.200.detection_patterns`; any of, optional) — Example utterances that should invoke this skill. - any of: `array`, `null` - `license` (`restore_skill_version.response.200.license`; any of, optional) — SKILL.md `license`: a license name, or the file in the skill that holds it. - any of: `string`, `null` - `compatibility` (`restore_skill_version.response.200.compatibility`; any of, optional) — SKILL.md `compatibility`: what the skill needs to run (product, packages, network). - any of: `string`, `null` ### 422 Validation Error #### Example ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string", "input": null, "ctx": {} } ] } ``` - `detail` (`restore_skill_version.response.422.detail`; array, optional) - `loc` (`restore_skill_version.response.422.detail.loc`; array, required) - any of: `string`, `integer` - `msg` (`restore_skill_version.response.422.detail.msg`; string, required) - `type` (`restore_skill_version.response.422.detail.type`; string, required) - `input` (`restore_skill_version.response.422.detail.input`; unknown, optional) - `ctx` (`restore_skill_version.response.422.detail.ctx`; object, optional) # Brains > Organize knowledge packs into named collections Source: https://truememory-docs.mvstg.com/api/tags/brains/ · Markdown: https://truememory-docs.mvstg.com/api/tags/brains/index.md Path: AI Gateway API Organize knowledge packs into named collections ## Operations - [List your brains](/api/brains/list/) - [Create a brain](/api/brains/create/) - [Get a brain](/api/brains/get/) - [Delete a brain](/api/brains/delete/) - [Update a brain](/api/brains/update/) - [List a brain's knowledge packs](/api/brains/knowledge-packs/list/) - [Add a knowledge pack to a brain](/api/brains/knowledge-packs/create/) - [Remove a knowledge pack from a brain](/api/brains/knowledge-packs/delete/) # Chat > Invoke agents and stream responses Source: https://truememory-docs.mvstg.com/api/tags/chat/ · Markdown: https://truememory-docs.mvstg.com/api/tags/chat/index.md Path: AI Gateway API Invoke agents and stream responses ## Operations - [Create a chat completion](/api/chat/completions/) - [Generate speech from text](/api/audio/speech/) - [Transcribe audio](/api/audio/transcriptions/) - [Generate an image](/api/images/generations/) # History & Feedback > Conversation history and feedback Source: https://truememory-docs.mvstg.com/api/tags/history-feedback/ · Markdown: https://truememory-docs.mvstg.com/api/tags/history-feedback/index.md Path: AI Gateway API Conversation history and feedback ## Operations - [Record run feedback](/api/feedback/create/) - [Get chat history](/api/history/create/) - [List chat threads](/api/threads/list/) - [Delete a thread](/api/threads/delete/) - [Update a thread](/api/threads/update/) # Knowledge Packs > Upload PDF/Markdown files for RAG injection Source: https://truememory-docs.mvstg.com/api/tags/knowledge-packs/ · Markdown: https://truememory-docs.mvstg.com/api/tags/knowledge-packs/index.md Path: AI Gateway API Upload PDF/Markdown files for RAG injection ## Operations - [List your knowledge packs](/api/knowledge-packs/list/) - [Upload a knowledge pack](/api/knowledge-packs/create/) - [Delete a knowledge pack](/api/knowledge-packs/delete/) - [Retry a failed knowledge pack](/api/knowledge-packs/retry/) # Memory > User memory management Source: https://truememory-docs.mvstg.com/api/tags/memory/ · Markdown: https://truememory-docs.mvstg.com/api/tags/memory/index.md Path: AI Gateway API User memory management ## Operations - [Warm the user's memory cache](/api/memories/graph/warm/) - [Warm the user's memory cache](/api/context/create/) - [Get memory graph for visualization](/api/memories/graph/list/) - [Add data to the memory graph](/api/memories/graph/create/) - [Get the memory timeline](/api/memories/timeline/) - [Export the user's full memory graph](/api/memories/export/) - [Import a previously exported memory envelope](/api/memories/import/create/) - [Check status of an async memory import](/api/memories/import/get/) - [List memories](/api/memories/get/) - [Delete a memory (deprecated — use /edges/{edge_id})](/api/memories/delete/) - [Delete an edge (one fact)](/api/memories/edges/delete/) - [Delete a memory node](/api/memories/nodes/delete/) - [Merge two memories](/api/memories/merge/) - [Set a memory node's visibility](/api/memories/nodes/visibility/) - [Bulk-add episodes to memory graph (maintenance lane)](/api/memories/graph/bulk/) - [Get graph-ingest job status](/api/memories/graph/status/get/) - [Search memory graph](/api/memories/search/) - [Retrieve memory context for a message](/api/memories/retrieve/) - [Clear all memories for a user+agent pair or a shared namespace](/api/memories/clear/) # Minds Source: https://truememory-docs.mvstg.com/api/tags/minds/ · Markdown: https://truememory-docs.mvstg.com/api/tags/minds/index.md Path: AI Gateway API ## Operations - [List minds](/api/minds/list/) - [Create a mind](/api/minds/create/) - [Get a mind](/api/minds/get/) - [Delete a mind](/api/minds/delete/) - [Update a mind](/api/minds/update/) - [Restore a deleted mind](/api/minds/restore/) - [Replace a mind's skills](/api/minds/skills/update/) - [List the brains in a mind](/api/minds/brains/list/) - [Put a brain into a mind](/api/minds/brains/update/) - [Take a brain out of a mind](/api/minds/brains/delete/) - [Upload a mind banner](/api/minds/banner/) - [Delete a mind banner](/api/minds/delete-banner/) - [List who a mind is shared with](/api/minds/grants/list/) - [Share a mind with a user](/api/minds/grants/create/) - [Revoke a user's access to a mind](/api/minds/grants/delete/) - [Retrieve what a mind knows about a query](/api/minds/consult/) - [Read a file of a skill in a mind](/api/minds/skills/files/get/) # Notifications Source: https://truememory-docs.mvstg.com/api/tags/notifications/ · Markdown: https://truememory-docs.mvstg.com/api/tags/notifications/index.md Path: AI Gateway API ## Operations - [List the subject user's registered devices](/api/notifications/devices/list/) - [Register a device for push notifications](/api/notifications/devices/create/) - [Unregister a device](/api/notifications/devices/delete/) - [Send a push notification](/api/notifications/push/) - [Subscribe devices to a topic](/api/notifications/topics/subscribe/) - [Unsubscribe devices from a topic](/api/notifications/topics/unsubscribe/) # Personas > Custom persona management Source: https://truememory-docs.mvstg.com/api/tags/personas/ · Markdown: https://truememory-docs.mvstg.com/api/tags/personas/index.md Path: AI Gateway API Custom persona management ## Operations - [List personas](/api/personas/list/) - [Create a persona](/api/personas/create/) - [Get active persona](/api/personas/active/list/) - [Clear active persona](/api/personas/active/delete/) - [Set active persona](/api/personas/active/update/) - [Update a persona](/api/personas/update/) - [Delete a persona](/api/personas/delete/) # Skills > Invokable prompt-template registry Source: https://truememory-docs.mvstg.com/api/tags/skills/ · Markdown: https://truememory-docs.mvstg.com/api/tags/skills/index.md Path: AI Gateway API Invokable prompt-template registry ## Operations - [List skills](/api/skills/list/) - [Create a skill](/api/skills/create/) - [List skill categories](/api/skills/categories/) - [Get a skill by key](/api/skills/by-key/get/) - [Get a skill](/api/skills/get/) - [Update a skill](/api/skills/update/) - [Delete a skill](/api/skills/delete/) - [List skill versions](/api/skills/versions/list/) - [Restore a skill version](/api/skills/versions/restore/) - [Record a skill invocation](/api/skills/usage/) - [Import a skill folder](/api/skills/import/) - [Replace a skill from a skill folder](/api/skills/post-import/) - [List a skill's files](/api/skills/files/list/) - [Read a skill file](/api/skills/files/get/) - [Write a skill file](/api/skills/files/update/) - [Delete a skill file](/api/skills/files/delete/) - [Download a skill folder](/api/skills/export/) # System > Health checks and service metadata Source: https://truememory-docs.mvstg.com/api/tags/system/ · Markdown: https://truememory-docs.mvstg.com/api/tags/system/index.md Path: AI Gateway API Health checks and service metadata ## Operations - [Get service metadata](/api/info/list/) # Users Source: https://truememory-docs.mvstg.com/api/tags/users/ · Markdown: https://truememory-docs.mvstg.com/api/tags/users/index.md Path: AI Gateway API ## Operations - [Delete a user from every store](/api/users/delete/) - [Status of a user deletion](/api/users/deletion/) # Voice > Gemini voice-to-voice and TTS Source: https://truememory-docs.mvstg.com/api/tags/voice/ · Markdown: https://truememory-docs.mvstg.com/api/tags/voice/index.md Path: AI Gateway API Gemini voice-to-voice and TTS ## Operations - [Get voice preferences for a persona](/api/voice/preferences/get/) - [Update voice preferences for a persona](/api/voice/preferences/update/) # Delete a thread > Delete a chat thread and its transcript. Source: https://truememory-docs.mvstg.com/api/threads/delete/ · Markdown: https://truememory-docs.mvstg.com/api/threads/delete/index.md Path: AI Gateway API › History & Feedback `DELETE /threads/{thread_id}` Delete a chat thread and its transcript. ## Authentication - `HTTPBearer`, http, header `Authorization` ## Path parameters - `thread_id` (`delete_thread.path.thread_id`; string, required) ## Header parameters - `x-app-id` (`delete_thread.header.x-app-id`; any of, optional) — Application ID for scoping threads - any of: `string`, `null` - `X-On-Behalf-Of` (`delete_thread.header.X-On-Behalf-Of`; any of, optional) - any of: `string`, `null` - `X-External-User-Id` (`delete_thread.header.X-External-User-Id`; any of, optional) - any of: `string`, `null` - `X-App-Id` (`delete_thread.header.X-App-Id`; any of, optional) - any of: `string`, `null` ## Code samples ### cURL ```bash curl --request DELETE \ --url 'https://truememory-api.mvstg.com/threads/' \ --header 'Authorization: Bearer ' ``` ### TypeScript ```typescript const url = 'https://truememory-api.mvstg.com/threads/'; const options = {method: 'DELETE', headers: {Authorization: 'Bearer '}}; fetch(url, options) .then(res => res.json()) .then(json => console.log(json)) .catch(err => console.error(err)); ``` ### Python ```python import requests url = "https://truememory-api.mvstg.com/threads/" headers = {"Authorization": "Bearer "} response = requests.delete(url, headers=headers) print(response.text) ``` ## Responses ### 200 Successful Response #### Example ```json { "status": "deleted", "thread_id": "string" } ``` - `thread_id` (`delete_thread.response.200.thread_id`; string, required) - `status` (`delete_thread.response.200.status`; string, optional) - default `"deleted"` ### 422 Validation Error #### Example ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string", "input": null, "ctx": {} } ] } ``` - `detail` (`delete_thread.response.422.detail`; array, optional) - `loc` (`delete_thread.response.422.detail.loc`; array, required) - any of: `string`, `integer` - `msg` (`delete_thread.response.422.detail.msg`; string, required) - `type` (`delete_thread.response.422.detail.type`; string, required) - `input` (`delete_thread.response.422.detail.input`; unknown, optional) - `ctx` (`delete_thread.response.422.detail.ctx`; object, optional) # List chat threads > List chat threads for the user with cursor-based pagination. Optionally filter by agent_id, X-App-Id header, and created_at date range. Source: https://truememory-docs.mvstg.com/api/threads/list/ · Markdown: https://truememory-docs.mvstg.com/api/threads/list/index.md Path: AI Gateway API › History & Feedback `GET /threads` List chat threads for the user with cursor-based pagination. Optionally filter by agent_id, X-App-Id header, and created_at date range. ## Authentication - `HTTPBearer`, http, header `Authorization` ## Query parameters - `agent_id` (`list_threads.query.agent_id`; any of, optional) - any of: `string`, `null` - `cursor` (`list_threads.query.cursor`; any of, optional) - any of: `string`, `null` - `limit` (`list_threads.query.limit`; integer, optional) - default `20` - `order_by` (`list_threads.query.order_by`; string, optional) - default `"created_at"` - `asc` (`list_threads.query.asc`; boolean, optional) - default `false` - `created_after` (`list_threads.query.created_after`; any of, optional) - any of: `string`, `null` - `created_before` (`list_threads.query.created_before`; any of, optional) - any of: `string`, `null` ## Header parameters - `x-app-id` (`list_threads.header.x-app-id`; any of, optional) — Application ID for scoping threads - any of: `string`, `null` - `X-On-Behalf-Of` (`list_threads.header.X-On-Behalf-Of`; any of, optional) - any of: `string`, `null` - `X-External-User-Id` (`list_threads.header.X-External-User-Id`; any of, optional) - any of: `string`, `null` - `X-App-Id` (`list_threads.header.X-App-Id`; any of, optional) - any of: `string`, `null` ## Code samples ### cURL ```bash curl --request GET \ --url https://truememory-api.mvstg.com/threads \ --header 'Authorization: Bearer ' ``` ### TypeScript ```typescript const url = 'https://truememory-api.mvstg.com/threads'; const options = {method: 'GET', headers: {Authorization: 'Bearer '}}; fetch(url, options) .then(res => res.json()) .then(json => console.log(json)) .catch(err => console.error(err)); ``` ### Python ```python import requests url = "https://truememory-api.mvstg.com/threads" headers = {"Authorization": "Bearer "} response = requests.get(url, headers=headers) print(response.text) ``` ## Responses ### 200 Successful Response #### Example ```json { "threads": [ { "thread_id": "847c6285-8fc9-4560-a83f-4e6285809254", "agent_id": "chatbot", "app_id": "vibrantly", "title": "What is the weather in Tokyo?", "metadata": { "isPinned": false, "source": "whatsapp" }, "created_at": "string", "updated_at": "string" } ], "next_cursor": "string" } ``` - `threads` (`list_threads.response.200.threads`; array, required) - `thread_id` (`list_threads.response.200.threads.thread_id`; string, required) — Thread ID (session_key). - `agent_id` (`list_threads.response.200.threads.agent_id`; string, required) — Agent that handled this thread. - `created_at` (`list_threads.response.200.threads.created_at`; string, required) — When the thread was created (ISO 8601). - `updated_at` (`list_threads.response.200.threads.updated_at`; string, required) — When the thread was last updated (ISO 8601). - `app_id` (`list_threads.response.200.threads.app_id`; any of, optional) — Application scope for this thread. None if unscoped. - any of: `string`, `null` - `title` (`list_threads.response.200.threads.title`; any of, optional) — Thread title derived from the first user message. - any of: `string`, `null` - `metadata` (`list_threads.response.200.threads.metadata`; any of, optional) — Arbitrary metadata dict for the thread. - any of: `object`, `null` - `next_cursor` (`list_threads.response.200.next_cursor`; any of, optional) — Cursor for the next page. None if no more results. - any of: `string`, `null` ### 422 Validation Error #### Example ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string", "input": null, "ctx": {} } ] } ``` - `detail` (`list_threads.response.422.detail`; array, optional) - `loc` (`list_threads.response.422.detail.loc`; array, required) - any of: `string`, `integer` - `msg` (`list_threads.response.422.detail.msg`; string, required) - `type` (`list_threads.response.422.detail.type`; string, required) - `input` (`list_threads.response.422.detail.input`; unknown, optional) - `ctx` (`list_threads.response.422.detail.ctx`; object, optional) # Update a thread > Update a chat thread's title and/or metadata. Source: https://truememory-docs.mvstg.com/api/threads/update/ · Markdown: https://truememory-docs.mvstg.com/api/threads/update/index.md Path: AI Gateway API › History & Feedback `PATCH /threads/{thread_id}` Update a chat thread's title and/or metadata. ## Authentication - `HTTPBearer`, http, header `Authorization` ## Path parameters - `thread_id` (`update_thread.path.thread_id`; string, required) ## Header parameters - `x-app-id` (`update_thread.header.x-app-id`; any of, optional) — Application ID for scoping threads - any of: `string`, `null` - `X-On-Behalf-Of` (`update_thread.header.X-On-Behalf-Of`; any of, optional) - any of: `string`, `null` - `X-External-User-Id` (`update_thread.header.X-External-User-Id`; any of, optional) - any of: `string`, `null` - `X-App-Id` (`update_thread.header.X-App-Id`; any of, optional) - any of: `string`, `null` ## Request body _Required._ - `title` (`update_thread.title`; any of, optional) — New title for the thread. - any of: `string`, `null` - `metadata` (`update_thread.metadata`; any of, optional) — Arbitrary metadata dict for the thread (e.g. source, pageType, isPinned). - any of: `object`, `null` ## Example request ```json { "title": "My conversation about weather", "metadata": { "isPinned": false, "pageType": "whatsapp-dm", "source": "whatsapp" } } ``` ## Code samples ### cURL ```bash curl --request PATCH \ --url 'https://truememory-api.mvstg.com/threads/' \ --header 'Authorization: Bearer ' \ --header 'Content-Type: application/json' \ --data ' { "title": "My conversation about weather", "metadata": { "isPinned": false, "pageType": "whatsapp-dm", "source": "whatsapp" } } ' ``` ### TypeScript ```typescript const url = 'https://truememory-api.mvstg.com/threads/'; const options = { method: 'PATCH', headers: {'Content-Type': 'application/json', Authorization: 'Bearer '}, body: JSON.stringify({ title: 'My conversation about weather', metadata: {isPinned: false, pageType: 'whatsapp-dm', source: 'whatsapp'} }) }; fetch(url, options) .then(res => res.json()) .then(json => console.log(json)) .catch(err => console.error(err)); ``` ### Python ```python import requests url = "https://truememory-api.mvstg.com/threads/" payload = { "title": "My conversation about weather", "metadata": { "isPinned": False, "pageType": "whatsapp-dm", "source": "whatsapp" } } headers = { "Content-Type": "application/json", "Authorization": "Bearer " } response = requests.patch(url, json=payload, headers=headers) print(response.text) ``` ## Responses ### 200 Successful Response #### Example ```json { "thread_id": "847c6285-8fc9-4560-a83f-4e6285809254", "agent_id": "chatbot", "app_id": "vibrantly", "title": "What is the weather in Tokyo?", "metadata": { "isPinned": false, "source": "whatsapp" }, "created_at": "string", "updated_at": "string" } ``` - `thread_id` (`update_thread.response.200.thread_id`; string, required) — Thread ID (session_key). - `agent_id` (`update_thread.response.200.agent_id`; string, required) — Agent that handled this thread. - `created_at` (`update_thread.response.200.created_at`; string, required) — When the thread was created (ISO 8601). - `updated_at` (`update_thread.response.200.updated_at`; string, required) — When the thread was last updated (ISO 8601). - `app_id` (`update_thread.response.200.app_id`; any of, optional) — Application scope for this thread. None if unscoped. - any of: `string`, `null` - `title` (`update_thread.response.200.title`; any of, optional) — Thread title derived from the first user message. - any of: `string`, `null` - `metadata` (`update_thread.response.200.metadata`; any of, optional) — Arbitrary metadata dict for the thread. - any of: `object`, `null` ### 422 Validation Error #### Example ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string", "input": null, "ctx": {} } ] } ``` - `detail` (`update_thread.response.422.detail`; array, optional) - `loc` (`update_thread.response.422.detail.loc`; array, required) - any of: `string`, `integer` - `msg` (`update_thread.response.422.detail.msg`; string, required) - `type` (`update_thread.response.422.detail.type`; string, required) - `input` (`update_thread.response.422.detail.input`; unknown, optional) - `ctx` (`update_thread.response.422.detail.ctx`; object, optional) # Delete a user from every store > Erase a user: identity, auth row, threads, checkpoints, queued work, memory graph and overlay, owned resources, uploaded files, caches. ``user_id`` is the Truememory UUID (the ``user_id`` your other calls return). Service callers may address the user with ``X-On-Behalf-Of`` or ``X-External-User-Id`` instead, as long as it matches the path. Answers 202 as soon as the user is marked deleted and their deletion workflow is started. From the mark on, any request with the user's token or naming them in a subject header answers 410 ``USER_DELETED``. The workflow (one per user) removes the identity and every store on the maintenance lane, normally within seconds, retrying a store that fails. Poll ``GET /users/{user_id}/deletion`` or pass ``callback_url``. A second call for a deletion that completed returns ``completed``; otherwise it starts the workflow again, which is a no-op while one is in flight and a retry when the last one ended failed. ``?dry_run=true`` returns row counts per store and the memory scopes that would be removed, and writes nothing. Source: https://truememory-docs.mvstg.com/api/users/delete/ · Markdown: https://truememory-docs.mvstg.com/api/users/delete/index.md Path: AI Gateway API › Users `DELETE /users/{user_id}` Erase a user: identity, auth row, threads, checkpoints, queued work, memory graph and overlay, owned resources, uploaded files, caches. ``user_id`` is the Truememory UUID (the ``user_id`` your other calls return). Service callers may address the user with ``X-On-Behalf-Of`` or ``X-External-User-Id`` instead, as long as it matches the path. Answers 202 as soon as the user is marked deleted and their deletion workflow is started. From the mark on, any request with the user's token or naming them in a subject header answers 410 ``USER_DELETED``. The workflow (one per user) removes the identity and every store on the maintenance lane, normally within seconds, retrying a store that fails. Poll ``GET /users/{user_id}/deletion`` or pass ``callback_url``. A second call for a deletion that completed returns ``completed``; otherwise it starts the workflow again, which is a no-op while one is in flight and a retry when the last one ended failed. ``?dry_run=true`` returns row counts per store and the memory scopes that would be removed, and writes nothing. ## Authentication - `HTTPBearer`, http, header `Authorization` ## Path parameters - `user_id` (`delete_user.path.user_id`; string, required) ## Query parameters - `dry_run` (`delete_user.query.dry_run`; boolean, optional) - default `false` ## Header parameters - `X-On-Behalf-Of` (`delete_user.header.X-On-Behalf-Of`; any of, optional) - any of: `string`, `null` - `X-External-User-Id` (`delete_user.header.X-External-User-Id`; any of, optional) - any of: `string`, `null` - `X-App-Id` (`delete_user.header.X-App-Id`; any of, optional) - any of: `string`, `null` ## Request body Any of: - `UserDeletionRequest` - `null` ## Example request ```json { "reason": "gdpr_art17", "callback_url": "http://example.com", "include_linked": false } ``` ## Code samples ### cURL ```bash curl --request DELETE \ --url 'https://truememory-api.mvstg.com/users/' \ --header 'Authorization: Bearer ' \ --header 'Content-Type: application/json' \ --data ' { "reason": "gdpr_art17", "callback_url": "http://example.com", "include_linked": false } ' ``` ### TypeScript ```typescript const url = 'https://truememory-api.mvstg.com/users/'; const options = { method: 'DELETE', headers: {'Content-Type': 'application/json', Authorization: 'Bearer '}, body: JSON.stringify({ reason: 'gdpr_art17', callback_url: 'http://example.com', include_linked: false }) }; fetch(url, options) .then(res => res.json()) .then(json => console.log(json)) .catch(err => console.error(err)); ``` ### Python ```python import requests url = "https://truememory-api.mvstg.com/users/" payload = { "reason": "gdpr_art17", "callback_url": "http://example.com", "include_linked": False } headers = { "Content-Type": "application/json", "Authorization": "Bearer " } response = requests.delete(url, json=payload, headers=headers) print(response.text) ``` ## Responses ### 202 Successful Response #### Example ```json { "user_id": "string", "status": "accepted", "requested_at": "2019-08-24T14:15:22Z", "completed_at": "2019-08-24T14:15:22Z" } ``` Any of: - `UserDeletionStatus` - `UserDeletionDryRun` ### 422 Validation Error #### Example ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string", "input": null, "ctx": {} } ] } ``` - `detail` (`delete_user.response.422.detail`; array, optional) - `loc` (`delete_user.response.422.detail.loc`; array, required) - any of: `string`, `integer` - `msg` (`delete_user.response.422.detail.msg`; string, required) - `type` (`delete_user.response.422.detail.type`; string, required) - `input` (`delete_user.response.422.detail.input`; unknown, optional) - `ctx` (`delete_user.response.422.detail.ctx`; object, optional) # Status of a user deletion > ``running`` while the workflow works, ``completed`` once every store is clean, ``failed`` when the run ended without completing (call ``DELETE`` again). Details per store are in the service logs. Source: https://truememory-docs.mvstg.com/api/users/deletion/ · Markdown: https://truememory-docs.mvstg.com/api/users/deletion/index.md Path: AI Gateway API › Users `GET /users/{user_id}/deletion` ``running`` while the workflow works, ``completed`` once every store is clean, ``failed`` when the run ended without completing (call ``DELETE`` again). Details per store are in the service logs. ## Authentication - `HTTPBearer`, http, header `Authorization` ## Path parameters - `user_id` (`get_user_deletion.path.user_id`; string, required) ## Header parameters - `X-On-Behalf-Of` (`get_user_deletion.header.X-On-Behalf-Of`; any of, optional) - any of: `string`, `null` - `X-External-User-Id` (`get_user_deletion.header.X-External-User-Id`; any of, optional) - any of: `string`, `null` - `X-App-Id` (`get_user_deletion.header.X-App-Id`; any of, optional) - any of: `string`, `null` ## Code samples ### cURL ```bash curl --request GET \ --url 'https://truememory-api.mvstg.com/users//deletion' \ --header 'Authorization: Bearer ' ``` ### TypeScript ```typescript const url = 'https://truememory-api.mvstg.com/users//deletion'; const options = {method: 'GET', headers: {Authorization: 'Bearer '}}; fetch(url, options) .then(res => res.json()) .then(json => console.log(json)) .catch(err => console.error(err)); ``` ### Python ```python import requests url = "https://truememory-api.mvstg.com/users//deletion" headers = {"Authorization": "Bearer "} response = requests.get(url, headers=headers) print(response.text) ``` ## Responses ### 200 Successful Response #### Example ```json { "user_id": "string", "status": "accepted", "requested_at": "2019-08-24T14:15:22Z", "completed_at": "2019-08-24T14:15:22Z" } ``` - `user_id` (`get_user_deletion.response.200.user_id`; string, required) - `status` (`get_user_deletion.response.200.status`; string, required) - one of `"accepted"`, `"running"`, `"completed"`, `"failed"` - `requested_at` (`get_user_deletion.response.200.requested_at`; string, required) - format `date-time` - `completed_at` (`get_user_deletion.response.200.completed_at`; any of, optional) - any of: `string`, `null` ### 422 Validation Error #### Example ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string", "input": null, "ctx": {} } ] } ``` - `detail` (`get_user_deletion.response.422.detail`; array, optional) - `loc` (`get_user_deletion.response.422.detail.loc`; array, required) - any of: `string`, `integer` - `msg` (`get_user_deletion.response.422.detail.msg`; string, required) - `type` (`get_user_deletion.response.422.detail.type`; string, required) - `input` (`get_user_deletion.response.422.detail.input`; unknown, optional) - `ctx` (`get_user_deletion.response.422.detail.ctx`; object, optional) # Get voice preferences for a persona > Return the TTS provider, voice and language this user has chosen for the persona. Preferences are per user and per persona; a persona the user has never configured comes back with the defaults. Source: https://truememory-docs.mvstg.com/api/voice/preferences/get/ · Markdown: https://truememory-docs.mvstg.com/api/voice/preferences/get/index.md Path: AI Gateway API › Voice `GET /v1/voice/preferences/{persona_id}` Return the TTS provider, voice and language this user has chosen for the persona. Preferences are per user and per persona; a persona the user has never configured comes back with the defaults. ## Authentication - `HTTPBearer`, http, header `Authorization` ## Path parameters - `persona_id` (`get_voice_preferences.path.persona_id`; string, required) — Persona to scope voice preferences to - format `uuid` ## Header parameters - `X-On-Behalf-Of` (`get_voice_preferences.header.X-On-Behalf-Of`; any of, optional) - any of: `string`, `null` - `X-External-User-Id` (`get_voice_preferences.header.X-External-User-Id`; any of, optional) - any of: `string`, `null` - `X-App-Id` (`get_voice_preferences.header.X-App-Id`; any of, optional) - any of: `string`, `null` ## Code samples ### cURL ```bash curl --request GET \ --url 'https://truememory-api.mvstg.com/v1/voice/preferences/' \ --header 'Authorization: Bearer ' ``` ### TypeScript ```typescript const url = 'https://truememory-api.mvstg.com/v1/voice/preferences/'; const options = {method: 'GET', headers: {Authorization: 'Bearer '}}; fetch(url, options) .then(res => res.json()) .then(json => console.log(json)) .catch(err => console.error(err)); ``` ### Python ```python import requests url = "https://truememory-api.mvstg.com/v1/voice/preferences/" headers = {"Authorization": "Bearer "} response = requests.get(url, headers=headers) print(response.text) ``` ## Responses ### 200 Successful Response #### Example ```json { "provider": "string", "voice": "string", "language": "string" } ``` - `provider` (`get_voice_preferences.response.200.provider`; string, required) - `voice` (`get_voice_preferences.response.200.voice`; string, required) - `language` (`get_voice_preferences.response.200.language`; string, required) ### 422 Validation Error #### Example ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string", "input": null, "ctx": {} } ] } ``` - `detail` (`get_voice_preferences.response.422.detail`; array, optional) - `loc` (`get_voice_preferences.response.422.detail.loc`; array, required) - any of: `string`, `integer` - `msg` (`get_voice_preferences.response.422.detail.msg`; string, required) - `type` (`get_voice_preferences.response.422.detail.type`; string, required) - `input` (`get_voice_preferences.response.422.detail.input`; unknown, optional) - `ctx` (`get_voice_preferences.response.422.detail.ctx`; object, optional) # Update voice preferences for a persona > Set the TTS provider, voice and language for this persona. A full replace, not a merge — send all three fields. Returns the stored preferences. Source: https://truememory-docs.mvstg.com/api/voice/preferences/update/ · Markdown: https://truememory-docs.mvstg.com/api/voice/preferences/update/index.md Path: AI Gateway API › Voice `PUT /v1/voice/preferences/{persona_id}` Set the TTS provider, voice and language for this persona. A full replace, not a merge — send all three fields. Returns the stored preferences. ## Authentication - `HTTPBearer`, http, header `Authorization` ## Path parameters - `persona_id` (`put_voice_preferences.path.persona_id`; string, required) — Persona to scope voice preferences to - format `uuid` ## Header parameters - `X-On-Behalf-Of` (`put_voice_preferences.header.X-On-Behalf-Of`; any of, optional) - any of: `string`, `null` - `X-External-User-Id` (`put_voice_preferences.header.X-External-User-Id`; any of, optional) - any of: `string`, `null` - `X-App-Id` (`put_voice_preferences.header.X-App-Id`; any of, optional) - any of: `string`, `null` ## Request body _Required._ - `voice` (`put_voice_preferences.voice`; string, required) — Voice name for the provider - minLength 1 - `language` (`put_voice_preferences.language`; string, required) — BCP-47 language code - minLength 1 - `provider` (`put_voice_preferences.provider`; string, optional) — TTS provider - minLength 1; default `"gemini"` ## Example request ```json { "provider": "gemini", "voice": "string", "language": "string" } ``` ## Code samples ### cURL ```bash curl --request PUT \ --url 'https://truememory-api.mvstg.com/v1/voice/preferences/' \ --header 'Authorization: Bearer ' \ --header 'Content-Type: application/json' \ --data ' { "provider": "gemini", "voice": "string", "language": "string" } ' ``` ### TypeScript ```typescript const url = 'https://truememory-api.mvstg.com/v1/voice/preferences/'; const options = { method: 'PUT', headers: {'Content-Type': 'application/json', Authorization: 'Bearer '}, body: JSON.stringify({provider: 'gemini', voice: 'string', language: 'string'}) }; fetch(url, options) .then(res => res.json()) .then(json => console.log(json)) .catch(err => console.error(err)); ``` ### Python ```python import requests url = "https://truememory-api.mvstg.com/v1/voice/preferences/" payload = { "provider": "gemini", "voice": "string", "language": "string" } headers = { "Content-Type": "application/json", "Authorization": "Bearer " } response = requests.put(url, json=payload, headers=headers) print(response.text) ``` ## Responses ### 200 Successful Response #### Example ```json { "provider": "string", "voice": "string", "language": "string" } ``` - `provider` (`put_voice_preferences.response.200.provider`; string, required) - `voice` (`put_voice_preferences.response.200.voice`; string, required) - `language` (`put_voice_preferences.response.200.language`; string, required) ### 422 Validation Error #### Example ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string", "input": null, "ctx": {} } ] } ``` - `detail` (`put_voice_preferences.response.422.detail`; array, optional) - `loc` (`put_voice_preferences.response.422.detail.loc`; array, required) - any of: `string`, `integer` - `msg` (`put_voice_preferences.response.422.detail.msg`; string, required) - `type` (`put_voice_preferences.response.422.detail.type`; string, required) - `input` (`put_voice_preferences.response.422.detail.input`; unknown, optional) - `ctx` (`put_voice_preferences.response.422.detail.ctx`; object, optional) # Auth API > Sign-in, tokens, email verification and account management. For chat and memory, see the [AI Gateway API](/api/). Source: https://truememory-docs.mvstg.com/auth/ · Markdown: https://truememory-docs.mvstg.com/auth/index.md Sign-in, tokens, email verification and account management. For chat and memory, see the [AI Gateway API](/api/). Version: 1.0.0 ## Servers - `https://truememory-auth.mvstg.com` ## Sections - [Auth](/auth/tags/auth/) - [OAuth](/auth/tags/o-auth/) # Unlink the Auth0 account from the current Firebase user > Remove the linked account record for the authenticated Firebase user. Authenticates via the standard ``Authorization: Bearer `` header. Returns 404 if no linked account exists, mirroring the GET rather than reporting success for a link that was never there. Unlinking stops the association going forward. It does not delete the memories that were shared while the link existed. Source: https://truememory-docs.mvstg.com/auth/delete-link-mv-account/ · Markdown: https://truememory-docs.mvstg.com/auth/delete-link-mv-account/index.md Path: Auth API › Auth `DELETE /auth/link_mv_account` Remove the linked account record for the authenticated Firebase user. Authenticates via the standard ``Authorization: Bearer `` header. Returns 404 if no linked account exists, mirroring the GET rather than reporting success for a link that was never there. Unlinking stops the association going forward. It does not delete the memories that were shared while the link existed. ## Authentication - `HTTPBearer`, http, header `Authorization` ## Code samples ### cURL ```bash curl --request DELETE \ --url https://truememory-auth.mvstg.com/auth/link_mv_account \ --header 'Authorization: Bearer ' ``` ### TypeScript ```typescript const url = 'https://truememory-auth.mvstg.com/auth/link_mv_account'; const options = {method: 'DELETE', headers: {Authorization: 'Bearer '}}; fetch(url, options) .then(res => res.json()) .then(json => console.log(json)) .catch(err => console.error(err)); ``` ### Python ```python import requests url = "https://truememory-auth.mvstg.com/auth/link_mv_account" headers = {"Authorization": "Bearer "} response = requests.delete(url, headers=headers) print(response.text) ``` ## Responses ### 204 Successful Response # Check if an email is registered > Check whether a user account exists for the given email. No authentication required. The optional ``tenant_id`` scopes the lookup to a specific GIP tenant. Source: https://truememory-docs.mvstg.com/auth/email/exists/ · Markdown: https://truememory-docs.mvstg.com/auth/email/exists/index.md Path: Auth API › Auth `GET /auth/email/exists` Check whether a user account exists for the given email. No authentication required. The optional ``tenant_id`` scopes the lookup to a specific GIP tenant. ## Query parameters - `email` (`check_email_exists_auth_email_exists_get.query.email`; string, required) - format `email` - `tenant_id` (`check_email_exists_auth_email_exists_get.query.tenant_id`; any of, optional) - any of: `string`, `null` ## Code samples ### cURL ```bash curl --request GET \ --url 'https://truememory-auth.mvstg.com/auth/email/exists?email=user%40example.com' ``` ### TypeScript ```typescript const url = 'https://truememory-auth.mvstg.com/auth/email/exists?email=user%40example.com'; const options = {method: 'GET'}; fetch(url, options) .then(res => res.json()) .then(json => console.log(json)) .catch(err => console.error(err)); ``` ### Python ```python import requests url = "https://truememory-auth.mvstg.com/auth/email/exists?email=user%40example.com" response = requests.get(url) print(response.text) ``` ## Responses ### 200 Successful Response #### Example ```json { "email": "string", "exists": true } ``` - `email` (`check_email_exists_auth_email_exists_get.response.200.email`; string, required) - `exists` (`check_email_exists_auth_email_exists_get.response.200.exists`; boolean, required) ### 422 Validation Error #### Example ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string", "input": null, "ctx": {} } ] } ``` - `detail` (`check_email_exists_auth_email_exists_get.response.422.detail`; array, optional) - `loc` (`check_email_exists_auth_email_exists_get.response.422.detail.loc`; array, required) - any of: `string`, `integer` - `msg` (`check_email_exists_auth_email_exists_get.response.422.detail.msg`; string, required) - `type` (`check_email_exists_auth_email_exists_get.response.422.detail.type`; string, required) - `input` (`check_email_exists_auth_email_exists_get.response.422.detail.input`; unknown, optional) - `ctx` (`check_email_exists_auth_email_exists_get.response.422.detail.ctx`; object, optional) # Request password reset email > Send a password reset email via Resend (custom template). Firebase remains the identity backend (it mints the oobCode embedded in the link), but the email itself is delivered by Resend — never Firebase's built-in mailer. Always returns ``{"status": "ok"}`` regardless of whether the email exists to prevent email enumeration attacks. Source: https://truememory-docs.mvstg.com/auth/forgot-password/ · Markdown: https://truememory-docs.mvstg.com/auth/forgot-password/index.md Path: Auth API › Auth `POST /auth/forgot-password` Send a password reset email via Resend (custom template). Firebase remains the identity backend (it mints the oobCode embedded in the link), but the email itself is delivered by Resend — never Firebase's built-in mailer. Always returns ``{"status": "ok"}`` regardless of whether the email exists to prevent email enumeration attacks. ## Request body _Required._ - `email` (`forgot_password_auth_forgot_password_post.email`; string, required) - format `email` - `tenant_id` (`forgot_password_auth_forgot_password_post.tenant_id`; any of, optional) - any of: `string`, `null` - `app_name` (`forgot_password_auth_forgot_password_post.app_name`; any of, optional) - any of: `string`, `null` ## Example request ```json { "email": "user@example.com", "tenant_id": "string", "app_name": "string" } ``` ## Code samples ### cURL ```bash curl --request POST \ --url https://truememory-auth.mvstg.com/auth/forgot-password \ --header 'Content-Type: application/json' \ --data ' { "email": "user@example.com", "tenant_id": "string", "app_name": "string" } ' ``` ### TypeScript ```typescript const url = 'https://truememory-auth.mvstg.com/auth/forgot-password'; const options = { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({email: 'user@example.com', tenant_id: 'string', app_name: 'string'}) }; fetch(url, options) .then(res => res.json()) .then(json => console.log(json)) .catch(err => console.error(err)); ``` ### Python ```python import requests url = "https://truememory-auth.mvstg.com/auth/forgot-password" payload = { "email": "user@example.com", "tenant_id": "string", "app_name": "string" } headers = {"Content-Type": "application/json"} response = requests.post(url, json=payload, headers=headers) print(response.text) ``` ## Responses ### 200 Successful Response #### Example ```json { "status": "ok" } ``` - `status` (`forgot_password_auth_forgot_password_post.response.200.status`; string, optional) - default `"ok"` ### 422 Validation Error #### Example ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string", "input": null, "ctx": {} } ] } ``` - `detail` (`forgot_password_auth_forgot_password_post.response.422.detail`; array, optional) - `loc` (`forgot_password_auth_forgot_password_post.response.422.detail.loc`; array, required) - any of: `string`, `integer` - `msg` (`forgot_password_auth_forgot_password_post.response.422.detail.msg`; string, required) - `type` (`forgot_password_auth_forgot_password_post.response.422.detail.type`; string, required) - `input` (`forgot_password_auth_forgot_password_post.response.422.detail.input`; unknown, optional) - `ctx` (`forgot_password_auth_forgot_password_post.response.422.detail.ctx`; object, optional) # Get the linked Auth0 account for the current Firebase user > Return the linked account record for the authenticated Firebase user. Authenticates via the standard ``Authorization: Bearer `` header. Returns 404 if no linked account exists for this user. Source: https://truememory-docs.mvstg.com/auth/link-mv-account/ · Markdown: https://truememory-docs.mvstg.com/auth/link-mv-account/index.md Path: Auth API › Auth `GET /auth/link_mv_account` Return the linked account record for the authenticated Firebase user. Authenticates via the standard ``Authorization: Bearer `` header. Returns 404 if no linked account exists for this user. ## Authentication - `HTTPBearer`, http, header `Authorization` ## Code samples ### cURL ```bash curl --request GET \ --url https://truememory-auth.mvstg.com/auth/link_mv_account \ --header 'Authorization: Bearer ' ``` ### TypeScript ```typescript const url = 'https://truememory-auth.mvstg.com/auth/link_mv_account'; const options = {method: 'GET', headers: {Authorization: 'Bearer '}}; fetch(url, options) .then(res => res.json()) .then(json => console.log(json)) .catch(err => console.error(err)); ``` ### Python ```python import requests url = "https://truememory-auth.mvstg.com/auth/link_mv_account" headers = {"Authorization": "Bearer "} response = requests.get(url, headers=headers) print(response.text) ``` ## Responses ### 200 Successful Response #### Example ```json { "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "firebase_user_id": "636923ed-630d-4147-9114-1cccc26d0ae7", "auth0_user_id": "string", "email": "string", "created_at": "2019-08-24T14:15:22Z" } ``` - `id` (`get_link_mv_account_auth_link_mv_account_get.response.200.id`; string, required) - format `uuid` - `firebase_user_id` (`get_link_mv_account_auth_link_mv_account_get.response.200.firebase_user_id`; string, required) - format `uuid` - `auth0_user_id` (`get_link_mv_account_auth_link_mv_account_get.response.200.auth0_user_id`; string, required) - `email` (`get_link_mv_account_auth_link_mv_account_get.response.200.email`; string, required) - `created_at` (`get_link_mv_account_auth_link_mv_account_get.response.200.created_at`; string, required) - format `date-time` # Login with email/password > Verify email/password credentials and return tokens. Source: https://truememory-docs.mvstg.com/auth/login/ · Markdown: https://truememory-docs.mvstg.com/auth/login/index.md Path: Auth API › Auth `POST /auth/login` Verify email/password credentials and return tokens. ## Request body _Required._ - `email` (`login_auth_login_post.email`; string, required) - format `email` - `password` (`login_auth_login_post.password`; string, required) - `tenant_id` (`login_auth_login_post.tenant_id`; any of, optional) — Google Identity Platform tenant ID. Required for tenant-scoped users. Platform-wide users can omit this. - any of: `string`, `null` ## Example request ```json { "email": "user@example.com", "password": "S3cur3P@ss!", "tenant_id": "vibrantly-1234" } ``` ## Code samples ### cURL ```bash curl --request POST \ --url https://truememory-auth.mvstg.com/auth/login \ --header 'Content-Type: application/json' \ --data ' { "email": "user@example.com", "password": "S3cur3P@ss!", "tenant_id": "vibrantly-1234" } ' ``` ### TypeScript ```typescript const url = 'https://truememory-auth.mvstg.com/auth/login'; const options = { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({ email: 'user@example.com', password: 'S3cur3P@ss!', tenant_id: 'vibrantly-1234' }) }; fetch(url, options) .then(res => res.json()) .then(json => console.log(json)) .catch(err => console.error(err)); ``` ### Python ```python import requests url = "https://truememory-auth.mvstg.com/auth/login" payload = { "email": "user@example.com", "password": "S3cur3P@ss!", "tenant_id": "vibrantly-1234" } headers = {"Content-Type": "application/json"} response = requests.post(url, json=payload, headers=headers) print(response.text) ``` ## Responses ### 200 Successful Response #### Example ```json { "id_token": "string", "refresh_token": "string", "expires_in": "string", "user": { "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "tenant_id": "34f5c98e-f430-457b-a812-92637d0c6fd0", "gip_uid": "string", "email": "string", "email_verified": false, "username": "string", "display_name": "string", "photo_url": "string", "phone_number": "string", "disabled": false, "role": "user", "metadata": {}, "raw_app_meta_data": {}, "custom_claims": {}, "last_sign_in_at": "2019-08-24T14:15:22Z", "created_at": "2019-08-24T14:15:22Z", "updated_at": "2019-08-24T14:15:22Z", "deleted_at": "2019-08-24T14:15:22Z", "deletion_completed_at": "2019-08-24T14:15:22Z" } } ``` - `id_token` (`login_auth_login_post.response.200.id_token`; string, required) - `refresh_token` (`login_auth_login_post.response.200.refresh_token`; string, required) - `expires_in` (`login_auth_login_post.response.200.expires_in`; string, required) - `user` (`login_auth_login_post.response.200.user`; any of, optional) - any of: `AuthUser`, `null` ### 422 Validation Error #### Example ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string", "input": null, "ctx": {} } ] } ``` - `detail` (`login_auth_login_post.response.422.detail`; array, optional) - `loc` (`login_auth_login_post.response.422.detail.loc`; array, required) - any of: `string`, `integer` - `msg` (`login_auth_login_post.response.422.detail.msg`; string, required) - `type` (`login_auth_login_post.response.422.detail.type`; string, required) - `input` (`login_auth_login_post.response.422.detail.input`; unknown, optional) - `ctx` (`login_auth_login_post.response.422.detail.ctx`; object, optional) # Get your profile > Return the profile of the user the access token belongs to, including tenant, roles and metadata. Use this to resolve who a token represents. Source: https://truememory-docs.mvstg.com/auth/me/ · Markdown: https://truememory-docs.mvstg.com/auth/me/index.md Path: Auth API › Auth `GET /auth/me` Return the profile of the user the access token belongs to, including tenant, roles and metadata. Use this to resolve who a token represents. ## Authentication - `HTTPBearer`, http, header `Authorization` ## Code samples ### cURL ```bash curl --request GET \ --url https://truememory-auth.mvstg.com/auth/me \ --header 'Authorization: Bearer ' ``` ### TypeScript ```typescript const url = 'https://truememory-auth.mvstg.com/auth/me'; const options = {method: 'GET', headers: {Authorization: 'Bearer '}}; fetch(url, options) .then(res => res.json()) .then(json => console.log(json)) .catch(err => console.error(err)); ``` ### Python ```python import requests url = "https://truememory-auth.mvstg.com/auth/me" headers = {"Authorization": "Bearer "} response = requests.get(url, headers=headers) print(response.text) ``` ## Responses ### 200 Successful Response #### Example ```json { "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "tenant_id": "34f5c98e-f430-457b-a812-92637d0c6fd0", "gip_uid": "string", "email": "string", "email_verified": false, "username": "string", "display_name": "string", "photo_url": "string", "phone_number": "string", "disabled": false, "role": "user", "metadata": {}, "raw_app_meta_data": {}, "custom_claims": {}, "last_sign_in_at": "2019-08-24T14:15:22Z", "created_at": "2019-08-24T14:15:22Z", "updated_at": "2019-08-24T14:15:22Z", "deleted_at": "2019-08-24T14:15:22Z", "deletion_completed_at": "2019-08-24T14:15:22Z" } ``` - `id` (`get_me_auth_me_get.response.200.id`; string, required) - format `uuid` - `gip_uid` (`get_me_auth_me_get.response.200.gip_uid`; string, required) - `tenant_id` (`get_me_auth_me_get.response.200.tenant_id`; any of, optional) - any of: `string`, `null` - `email` (`get_me_auth_me_get.response.200.email`; any of, optional) - any of: `string`, `null` - `email_verified` (`get_me_auth_me_get.response.200.email_verified`; boolean, optional) - default `false` - `username` (`get_me_auth_me_get.response.200.username`; any of, optional) - any of: `string`, `null` - `display_name` (`get_me_auth_me_get.response.200.display_name`; any of, optional) - any of: `string`, `null` - `photo_url` (`get_me_auth_me_get.response.200.photo_url`; any of, optional) - any of: `string`, `null` - `phone_number` (`get_me_auth_me_get.response.200.phone_number`; any of, optional) - any of: `string`, `null` - `disabled` (`get_me_auth_me_get.response.200.disabled`; boolean, optional) - default `false` - `role` (`get_me_auth_me_get.response.200.role`; string, optional) - default `"user"` - `metadata` (`get_me_auth_me_get.response.200.metadata`; object, optional) - `raw_app_meta_data` (`get_me_auth_me_get.response.200.raw_app_meta_data`; object, optional) - `custom_claims` (`get_me_auth_me_get.response.200.custom_claims`; object, optional) - `last_sign_in_at` (`get_me_auth_me_get.response.200.last_sign_in_at`; any of, optional) - any of: `string`, `null` - `created_at` (`get_me_auth_me_get.response.200.created_at`; any of, optional) - any of: `string`, `null` - `updated_at` (`get_me_auth_me_get.response.200.updated_at`; any of, optional) - any of: `string`, `null` - `deleted_at` (`get_me_auth_me_get.response.200.deleted_at`; any of, optional) - any of: `string`, `null` - `deletion_completed_at` (`get_me_auth_me_get.response.200.deletion_completed_at`; any of, optional) - any of: `string`, `null` # Shallow-merge app-scoped metadata for the current user > Atomically shallow-merge the body into ``raw_app_meta_data[app]``. Other namespaces are untouched. Pass ``null`` as a value to delete the matching top-level key from the namespace. Source: https://truememory-docs.mvstg.com/auth/me/app-metadata/ · Markdown: https://truememory-docs.mvstg.com/auth/me/app-metadata/index.md Path: Auth API › Auth `PATCH /auth/me/app-metadata` Atomically shallow-merge the body into ``raw_app_meta_data[app]``. Other namespaces are untouched. Pass ``null`` as a value to delete the matching top-level key from the namespace. ## Authentication - `HTTPBearer`, http, header `Authorization` ## Query parameters - `app` (`patch_my_app_metadata_auth_me_app_metadata_patch.query.app`; string, required) — App namespace, e.g. blinklife ## Request body _Required._ ## Example request ```json {} ``` ## Code samples ### cURL ```bash curl --request PATCH \ --url 'https://truememory-auth.mvstg.com/auth/me/app-metadata?app=string' \ --header 'Authorization: Bearer ' \ --header 'Content-Type: application/json' \ --data '{}' ``` ### TypeScript ```typescript const url = 'https://truememory-auth.mvstg.com/auth/me/app-metadata?app=string'; const options = { method: 'PATCH', headers: {'Content-Type': 'application/json', Authorization: 'Bearer '}, body: JSON.stringify({}) }; fetch(url, options) .then(res => res.json()) .then(json => console.log(json)) .catch(err => console.error(err)); ``` ### Python ```python import requests url = "https://truememory-auth.mvstg.com/auth/me/app-metadata?app=string" payload = {} headers = { "Content-Type": "application/json", "Authorization": "Bearer " } response = requests.patch(url, json=payload, headers=headers) print(response.text) ``` ## Responses ### 200 Successful Response #### Example ```json {} ``` ### 422 Validation Error #### Example ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string", "input": null, "ctx": {} } ] } ``` - `detail` (`patch_my_app_metadata_auth_me_app_metadata_patch.response.422.detail`; array, optional) - `loc` (`patch_my_app_metadata_auth_me_app_metadata_patch.response.422.detail.loc`; array, required) - any of: `string`, `integer` - `msg` (`patch_my_app_metadata_auth_me_app_metadata_patch.response.422.detail.msg`; string, required) - `type` (`patch_my_app_metadata_auth_me_app_metadata_patch.response.422.detail.type`; string, required) - `input` (`patch_my_app_metadata_auth_me_app_metadata_patch.response.422.detail.input`; unknown, optional) - `ctx` (`patch_my_app_metadata_auth_me_app_metadata_patch.response.422.detail.ctx`; object, optional) # Disconnect a client; its access ends within 15 minutes Source: https://truememory-docs.mvstg.com/auth/oauth/connections/delete/ · Markdown: https://truememory-docs.mvstg.com/auth/oauth/connections/delete/index.md Path: Auth API › OAuth `DELETE /oauth/connections/{client_id}` ## Authentication - `HTTPBearer`, http, header `Authorization` ## Path parameters - `client_id` (`disconnect_oauth_connections__client_id__delete.path.client_id`; string, required) ## Code samples ### cURL ```bash curl --request DELETE \ --url 'https://truememory-auth.mvstg.com/oauth/connections/' \ --header 'Authorization: Bearer ' ``` ### TypeScript ```typescript const url = 'https://truememory-auth.mvstg.com/oauth/connections/'; const options = {method: 'DELETE', headers: {Authorization: 'Bearer '}}; fetch(url, options) .then(res => res.json()) .then(json => console.log(json)) .catch(err => console.error(err)); ``` ### Python ```python import requests url = "https://truememory-auth.mvstg.com/oauth/connections/" headers = {"Authorization": "Bearer "} response = requests.delete(url, headers=headers) print(response.text) ``` ## Responses ### 204 Successful Response ### 422 Validation Error #### Example ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string", "input": null, "ctx": {} } ] } ``` - `detail` (`disconnect_oauth_connections__client_id__delete.response.422.detail`; array, optional) - `loc` (`disconnect_oauth_connections__client_id__delete.response.422.detail.loc`; array, required) - any of: `string`, `integer` - `msg` (`disconnect_oauth_connections__client_id__delete.response.422.detail.msg`; string, required) - `type` (`disconnect_oauth_connections__client_id__delete.response.422.detail.type`; string, required) - `input` (`disconnect_oauth_connections__client_id__delete.response.422.detail.input`; unknown, optional) - `ctx` (`disconnect_oauth_connections__client_id__delete.response.422.detail.ctx`; object, optional) # MCP clients this user is connected to Source: https://truememory-docs.mvstg.com/auth/oauth/connections/list/ · Markdown: https://truememory-docs.mvstg.com/auth/oauth/connections/list/index.md Path: Auth API › OAuth `GET /oauth/connections` ## Authentication - `HTTPBearer`, http, header `Authorization` ## Code samples ### cURL ```bash curl --request GET \ --url https://truememory-auth.mvstg.com/oauth/connections \ --header 'Authorization: Bearer ' ``` ### TypeScript ```typescript const url = 'https://truememory-auth.mvstg.com/oauth/connections'; const options = {method: 'GET', headers: {Authorization: 'Bearer '}}; fetch(url, options) .then(res => res.json()) .then(json => console.log(json)) .catch(err => console.error(err)); ``` ### Python ```python import requests url = "https://truememory-auth.mvstg.com/oauth/connections" headers = {"Authorization": "Bearer "} response = requests.get(url, headers=headers) print(response.text) ``` ## Responses ### 200 Successful Response #### Example ```json [ { "client_id": "string", "client_name": "string", "client_uri": "string", "logo_uri": "string", "redirect_uris": [ "string" ], "scopes": [ "string" ], "connected_at": "2019-08-24T14:15:22Z", "last_active_at": "2019-08-24T14:15:22Z" } ] ``` - `client_id` (`connections_oauth_connections_get.response.200.client_id`; string, required) - `client_name` (`connections_oauth_connections_get.response.200.client_name`; any of, required) - any of: `string`, `null` - `client_uri` (`connections_oauth_connections_get.response.200.client_uri`; any of, required) - any of: `string`, `null` - `logo_uri` (`connections_oauth_connections_get.response.200.logo_uri`; any of, required) - any of: `string`, `null` - `redirect_uris` (`connections_oauth_connections_get.response.200.redirect_uris`; array, required) - `scopes` (`connections_oauth_connections_get.response.200.scopes`; array, required) - `connected_at` (`connections_oauth_connections_get.response.200.connected_at`; string, required) - format `date-time` - `last_active_at` (`connections_oauth_connections_get.response.200.last_active_at`; string, required) - format `date-time` # Signed-in user allows the client; returns where to send the browser Source: https://truememory-docs.mvstg.com/auth/oauth/requests/approve/ · Markdown: https://truememory-docs.mvstg.com/auth/oauth/requests/approve/index.md Path: Auth API › OAuth `POST /oauth/requests/{request_id}/approve` ## Authentication - `HTTPBearer`, http, header `Authorization` ## Path parameters - `request_id` (`approve_oauth_requests__request_id__approve_post.path.request_id`; string, required) - format `uuid` ## Code samples ### cURL ```bash curl --request POST \ --url 'https://truememory-auth.mvstg.com/oauth/requests//approve' \ --header 'Authorization: Bearer ' ``` ### TypeScript ```typescript const url = 'https://truememory-auth.mvstg.com/oauth/requests//approve'; const options = {method: 'POST', headers: {Authorization: 'Bearer '}}; fetch(url, options) .then(res => res.json()) .then(json => console.log(json)) .catch(err => console.error(err)); ``` ### Python ```python import requests url = "https://truememory-auth.mvstg.com/oauth/requests//approve" headers = {"Authorization": "Bearer "} response = requests.post(url, headers=headers) print(response.text) ``` ## Responses ### 200 Successful Response #### Example ```json { "redirect_url": "string" } ``` - `redirect_url` (`approve_oauth_requests__request_id__approve_post.response.200.redirect_url`; string, required) ### 422 Validation Error #### Example ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string", "input": null, "ctx": {} } ] } ``` - `detail` (`approve_oauth_requests__request_id__approve_post.response.422.detail`; array, optional) - `loc` (`approve_oauth_requests__request_id__approve_post.response.422.detail.loc`; array, required) - any of: `string`, `integer` - `msg` (`approve_oauth_requests__request_id__approve_post.response.422.detail.msg`; string, required) - `type` (`approve_oauth_requests__request_id__approve_post.response.422.detail.type`; string, required) - `input` (`approve_oauth_requests__request_id__approve_post.response.422.detail.input`; unknown, optional) - `ctx` (`approve_oauth_requests__request_id__approve_post.response.422.detail.ctx`; object, optional) # Signed-in user denies the client; returns where to send the browser Source: https://truememory-docs.mvstg.com/auth/oauth/requests/deny/ · Markdown: https://truememory-docs.mvstg.com/auth/oauth/requests/deny/index.md Path: Auth API › OAuth `POST /oauth/requests/{request_id}/deny` ## Authentication - `HTTPBearer`, http, header `Authorization` ## Path parameters - `request_id` (`deny_oauth_requests__request_id__deny_post.path.request_id`; string, required) - format `uuid` ## Code samples ### cURL ```bash curl --request POST \ --url 'https://truememory-auth.mvstg.com/oauth/requests//deny' \ --header 'Authorization: Bearer ' ``` ### TypeScript ```typescript const url = 'https://truememory-auth.mvstg.com/oauth/requests//deny'; const options = {method: 'POST', headers: {Authorization: 'Bearer '}}; fetch(url, options) .then(res => res.json()) .then(json => console.log(json)) .catch(err => console.error(err)); ``` ### Python ```python import requests url = "https://truememory-auth.mvstg.com/oauth/requests//deny" headers = {"Authorization": "Bearer "} response = requests.post(url, headers=headers) print(response.text) ``` ## Responses ### 200 Successful Response #### Example ```json { "redirect_url": "string" } ``` - `redirect_url` (`deny_oauth_requests__request_id__deny_post.response.200.redirect_url`; string, required) ### 422 Validation Error #### Example ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string", "input": null, "ctx": {} } ] } ``` - `detail` (`deny_oauth_requests__request_id__deny_post.response.422.detail`; array, optional) - `loc` (`deny_oauth_requests__request_id__deny_post.response.422.detail.loc`; array, required) - any of: `string`, `integer` - `msg` (`deny_oauth_requests__request_id__deny_post.response.422.detail.msg`; string, required) - `type` (`deny_oauth_requests__request_id__deny_post.response.422.detail.type`; string, required) - `input` (`deny_oauth_requests__request_id__deny_post.response.422.detail.input`; unknown, optional) - `ctx` (`deny_oauth_requests__request_id__deny_post.response.422.detail.ctx`; object, optional) # Describe a pending sign-in request for the consent page Source: https://truememory-docs.mvstg.com/auth/oauth/requests/get/ · Markdown: https://truememory-docs.mvstg.com/auth/oauth/requests/get/index.md Path: Auth API › OAuth `GET /oauth/requests/{request_id}` ## Authentication - `HTTPBearer`, http, header `Authorization` ## Path parameters - `request_id` (`describe_oauth_requests__request_id__get.path.request_id`; string, required) - format `uuid` ## Code samples ### cURL ```bash curl --request GET \ --url 'https://truememory-auth.mvstg.com/oauth/requests/' \ --header 'Authorization: Bearer ' ``` ### TypeScript ```typescript const url = 'https://truememory-auth.mvstg.com/oauth/requests/'; const options = {method: 'GET', headers: {Authorization: 'Bearer '}}; fetch(url, options) .then(res => res.json()) .then(json => console.log(json)) .catch(err => console.error(err)); ``` ### Python ```python import requests url = "https://truememory-auth.mvstg.com/oauth/requests/" headers = {"Authorization": "Bearer "} response = requests.get(url, headers=headers) print(response.text) ``` ## Responses ### 200 Successful Response #### Example ```json { "client_name": "string", "client_uri": "string", "logo_uri": "string", "redirect_uri": "string", "scopes": [ "string" ], "app": { "slug": "string", "name": "string" } } ``` - `client_name` (`describe_oauth_requests__request_id__get.response.200.client_name`; any of, required) - any of: `string`, `null` - `client_uri` (`describe_oauth_requests__request_id__get.response.200.client_uri`; any of, required) - any of: `string`, `null` - `logo_uri` (`describe_oauth_requests__request_id__get.response.200.logo_uri`; any of, required) - any of: `string`, `null` - `redirect_uri` (`describe_oauth_requests__request_id__get.response.200.redirect_uri`; string, required) - `scopes` (`describe_oauth_requests__request_id__get.response.200.scopes`; array, required) - `app` (`describe_oauth_requests__request_id__get.response.200.app`; any of, optional) - any of: `ConsentApp`, `null` ### 422 Validation Error #### Example ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string", "input": null, "ctx": {} } ] } ``` - `detail` (`describe_oauth_requests__request_id__get.response.422.detail`; array, optional) - `loc` (`describe_oauth_requests__request_id__get.response.422.detail.loc`; array, required) - any of: `string`, `integer` - `msg` (`describe_oauth_requests__request_id__get.response.422.detail.msg`; string, required) - `type` (`describe_oauth_requests__request_id__get.response.422.detail.type`; string, required) - `input` (`describe_oauth_requests__request_id__get.response.422.detail.input`; unknown, optional) - `ctx` (`describe_oauth_requests__request_id__get.response.422.detail.ctx`; object, optional) # Sign in with an OAuth token > Exchange a provider token for Truememory tokens. For the client-side flow: your frontend completes sign-in with the provider SDK and posts the resulting token here, which is verified with Google Identity Platform before Truememory tokens are issued. Send either ``id_token`` or ``access_token``. Source: https://truememory-docs.mvstg.com/auth/oauth/token/ · Markdown: https://truememory-docs.mvstg.com/auth/oauth/token/index.md Path: Auth API › OAuth `POST /auth/oauth/token` Exchange a provider token for Truememory tokens. For the client-side flow: your frontend completes sign-in with the provider SDK and posts the resulting token here, which is verified with Google Identity Platform before Truememory tokens are issued. Send either ``id_token`` or ``access_token``. ## Request body _Required._ - `provider` (`oauth_token_auth_oauth_token_post.provider`; string, required) — OAuth provider name. - one of `"google"`, `"apple"` - `id_token` (`oauth_token_auth_oauth_token_post.id_token`; any of, optional) — ID token from the OAuth provider (preferred over access_token). - any of: `string`, `null` - `access_token` (`oauth_token_auth_oauth_token_post.access_token`; any of, optional) — Access token from the OAuth provider (used when id_token unavailable). - any of: `string`, `null` - `nonce` (`oauth_token_auth_oauth_token_post.nonce`; any of, optional) — Raw nonce for Apple Sign-In tokens generated with a hashed nonce (e.g. expo-apple-authentication). - any of: `string`, `null` - `tenant_id` (`oauth_token_auth_oauth_token_post.tenant_id`; any of, optional) — Google Identity Platform tenant ID for tenant-scoped sign-in. - any of: `string`, `null` - `metadata` (`oauth_token_auth_oauth_token_post.metadata`; any of, optional) — Initial metadata to store on first-time OAuth signup. Max 50 keys, 16 KB. - any of: `object`, `null` ## Example request ```json { "provider": "google", "id_token": "string", "access_token": "string", "nonce": "string", "tenant_id": "vibrantly-1234", "metadata": {} } ``` ## Code samples ### cURL ```bash curl --request POST \ --url https://truememory-auth.mvstg.com/auth/oauth/token \ --header 'Content-Type: application/json' \ --data ' { "provider": "google", "id_token": "string", "access_token": "string", "nonce": "string", "tenant_id": "vibrantly-1234", "metadata": {} } ' ``` ### TypeScript ```typescript const url = 'https://truememory-auth.mvstg.com/auth/oauth/token'; const options = { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({ provider: 'google', id_token: 'string', access_token: 'string', nonce: 'string', tenant_id: 'vibrantly-1234', metadata: {} }) }; fetch(url, options) .then(res => res.json()) .then(json => console.log(json)) .catch(err => console.error(err)); ``` ### Python ```python import requests url = "https://truememory-auth.mvstg.com/auth/oauth/token" payload = { "provider": "google", "id_token": "string", "access_token": "string", "nonce": "string", "tenant_id": "vibrantly-1234", "metadata": {} } headers = {"Content-Type": "application/json"} response = requests.post(url, json=payload, headers=headers) print(response.text) ``` ## Responses ### 200 Successful Response #### Example ```json { "id_token": "string", "refresh_token": "string", "expires_in": "string", "user": { "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "tenant_id": "34f5c98e-f430-457b-a812-92637d0c6fd0", "gip_uid": "string", "email": "string", "email_verified": false, "username": "string", "display_name": "string", "photo_url": "string", "phone_number": "string", "disabled": false, "role": "user", "metadata": {}, "raw_app_meta_data": {}, "custom_claims": {}, "last_sign_in_at": "2019-08-24T14:15:22Z", "created_at": "2019-08-24T14:15:22Z", "updated_at": "2019-08-24T14:15:22Z", "deleted_at": "2019-08-24T14:15:22Z", "deletion_completed_at": "2019-08-24T14:15:22Z" } } ``` - `id_token` (`oauth_token_auth_oauth_token_post.response.200.id_token`; string, required) - `refresh_token` (`oauth_token_auth_oauth_token_post.response.200.refresh_token`; string, required) - `expires_in` (`oauth_token_auth_oauth_token_post.response.200.expires_in`; string, required) - `user` (`oauth_token_auth_oauth_token_post.response.200.user`; any of, optional) - any of: `AuthUser`, `null` ### 422 Validation Error #### Example ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string", "input": null, "ctx": {} } ] } ``` - `detail` (`oauth_token_auth_oauth_token_post.response.422.detail`; array, optional) - `loc` (`oauth_token_auth_oauth_token_post.response.422.detail.loc`; array, required) - any of: `string`, `integer` - `msg` (`oauth_token_auth_oauth_token_post.response.422.detail.msg`; string, required) - `type` (`oauth_token_auth_oauth_token_post.response.422.detail.type`; string, required) - `input` (`oauth_token_auth_oauth_token_post.response.422.detail.input`; unknown, optional) - `ctx` (`oauth_token_auth_oauth_token_post.response.422.detail.ctx`; object, optional) # Update your profile > Update your own profile. Omitted fields are left as they are, and metadata is shallow-merged rather than replaced. Returns the updated user. Source: https://truememory-docs.mvstg.com/auth/patch-me/ · Markdown: https://truememory-docs.mvstg.com/auth/patch-me/index.md Path: Auth API › Auth `PATCH /auth/me` Update your own profile. Omitted fields are left as they are, and metadata is shallow-merged rather than replaced. Returns the updated user. ## Authentication - `HTTPBearer`, http, header `Authorization` ## Request body _Required._ - `display_name` (`update_me_auth_me_patch.display_name`; any of, optional) - any of: `string`, `null` - `metadata` (`update_me_auth_me_patch.metadata`; any of, optional) — Shallow-merged into existing metadata. Set a key to null to delete it. Max 50 keys, 16 KB. - any of: `object`, `null` - `role` (`update_me_auth_me_patch.role`; any of, optional) - any of: `string`, `null` - `disabled` (`update_me_auth_me_patch.disabled`; any of, optional) - any of: `boolean`, `null` ## Example request ```json { "display_name": "string", "metadata": {}, "role": "string", "disabled": true } ``` ## Code samples ### cURL ```bash curl --request PATCH \ --url https://truememory-auth.mvstg.com/auth/me \ --header 'Authorization: Bearer ' \ --header 'Content-Type: application/json' \ --data ' { "display_name": "string", "metadata": {}, "role": "string", "disabled": true } ' ``` ### TypeScript ```typescript const url = 'https://truememory-auth.mvstg.com/auth/me'; const options = { method: 'PATCH', headers: {'Content-Type': 'application/json', Authorization: 'Bearer '}, body: JSON.stringify({display_name: 'string', metadata: {}, role: 'string', disabled: true}) }; fetch(url, options) .then(res => res.json()) .then(json => console.log(json)) .catch(err => console.error(err)); ``` ### Python ```python import requests url = "https://truememory-auth.mvstg.com/auth/me" payload = { "display_name": "string", "metadata": {}, "role": "string", "disabled": True } headers = { "Content-Type": "application/json", "Authorization": "Bearer " } response = requests.patch(url, json=payload, headers=headers) print(response.text) ``` ## Responses ### 200 Successful Response #### Example ```json { "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "tenant_id": "34f5c98e-f430-457b-a812-92637d0c6fd0", "gip_uid": "string", "email": "string", "email_verified": false, "username": "string", "display_name": "string", "photo_url": "string", "phone_number": "string", "disabled": false, "role": "user", "metadata": {}, "raw_app_meta_data": {}, "custom_claims": {}, "last_sign_in_at": "2019-08-24T14:15:22Z", "created_at": "2019-08-24T14:15:22Z", "updated_at": "2019-08-24T14:15:22Z", "deleted_at": "2019-08-24T14:15:22Z", "deletion_completed_at": "2019-08-24T14:15:22Z" } ``` - `id` (`update_me_auth_me_patch.response.200.id`; string, required) - format `uuid` - `gip_uid` (`update_me_auth_me_patch.response.200.gip_uid`; string, required) - `tenant_id` (`update_me_auth_me_patch.response.200.tenant_id`; any of, optional) - any of: `string`, `null` - `email` (`update_me_auth_me_patch.response.200.email`; any of, optional) - any of: `string`, `null` - `email_verified` (`update_me_auth_me_patch.response.200.email_verified`; boolean, optional) - default `false` - `username` (`update_me_auth_me_patch.response.200.username`; any of, optional) - any of: `string`, `null` - `display_name` (`update_me_auth_me_patch.response.200.display_name`; any of, optional) - any of: `string`, `null` - `photo_url` (`update_me_auth_me_patch.response.200.photo_url`; any of, optional) - any of: `string`, `null` - `phone_number` (`update_me_auth_me_patch.response.200.phone_number`; any of, optional) - any of: `string`, `null` - `disabled` (`update_me_auth_me_patch.response.200.disabled`; boolean, optional) - default `false` - `role` (`update_me_auth_me_patch.response.200.role`; string, optional) - default `"user"` - `metadata` (`update_me_auth_me_patch.response.200.metadata`; object, optional) - `raw_app_meta_data` (`update_me_auth_me_patch.response.200.raw_app_meta_data`; object, optional) - `custom_claims` (`update_me_auth_me_patch.response.200.custom_claims`; object, optional) - `last_sign_in_at` (`update_me_auth_me_patch.response.200.last_sign_in_at`; any of, optional) - any of: `string`, `null` - `created_at` (`update_me_auth_me_patch.response.200.created_at`; any of, optional) - any of: `string`, `null` - `updated_at` (`update_me_auth_me_patch.response.200.updated_at`; any of, optional) - any of: `string`, `null` - `deleted_at` (`update_me_auth_me_patch.response.200.deleted_at`; any of, optional) - any of: `string`, `null` - `deletion_completed_at` (`update_me_auth_me_patch.response.200.deletion_completed_at`; any of, optional) - any of: `string`, `null` ### 422 Validation Error #### Example ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string", "input": null, "ctx": {} } ] } ``` - `detail` (`update_me_auth_me_patch.response.422.detail`; array, optional) - `loc` (`update_me_auth_me_patch.response.422.detail.loc`; array, required) - any of: `string`, `integer` - `msg` (`update_me_auth_me_patch.response.422.detail.msg`; string, required) - `type` (`update_me_auth_me_patch.response.422.detail.type`; string, required) - `input` (`update_me_auth_me_patch.response.422.detail.input`; unknown, optional) - `ctx` (`update_me_auth_me_patch.response.422.detail.ctx`; object, optional) # Link a Firebase account to an Auth0 account > Validate both tokens and create a linked account record. Accepts a Firebase ID token and an Auth0 JWT. Both are validated before any record is written. Returns 409 if either account is already linked. Source: https://truememory-docs.mvstg.com/auth/post-link-mv-account/ · Markdown: https://truememory-docs.mvstg.com/auth/post-link-mv-account/index.md Path: Auth API › Auth `POST /auth/link_mv_account` Validate both tokens and create a linked account record. Accepts a Firebase ID token and an Auth0 JWT. Both are validated before any record is written. Returns 409 if either account is already linked. ## Request body _Required._ - `firebase_token` (`link_mv_account_auth_link_mv_account_post.firebase_token`; string, required) - `auth0_token` (`link_mv_account_auth_link_mv_account_post.auth0_token`; string, required) ## Example request ```json { "firebase_token": "string", "auth0_token": "string" } ``` ## Code samples ### cURL ```bash curl --request POST \ --url https://truememory-auth.mvstg.com/auth/link_mv_account \ --header 'Content-Type: application/json' \ --data ' { "firebase_token": "string", "auth0_token": "string" } ' ``` ### TypeScript ```typescript const url = 'https://truememory-auth.mvstg.com/auth/link_mv_account'; const options = { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({firebase_token: 'string', auth0_token: 'string'}) }; fetch(url, options) .then(res => res.json()) .then(json => console.log(json)) .catch(err => console.error(err)); ``` ### Python ```python import requests url = "https://truememory-auth.mvstg.com/auth/link_mv_account" payload = { "firebase_token": "string", "auth0_token": "string" } headers = {"Content-Type": "application/json"} response = requests.post(url, json=payload, headers=headers) print(response.text) ``` ## Responses ### 201 Successful Response #### Example ```json { "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "firebase_user_id": "636923ed-630d-4147-9114-1cccc26d0ae7", "auth0_user_id": "string", "email": "string", "created_at": "2019-08-24T14:15:22Z" } ``` - `id` (`link_mv_account_auth_link_mv_account_post.response.201.id`; string, required) - format `uuid` - `firebase_user_id` (`link_mv_account_auth_link_mv_account_post.response.201.firebase_user_id`; string, required) - format `uuid` - `auth0_user_id` (`link_mv_account_auth_link_mv_account_post.response.201.auth0_user_id`; string, required) - `email` (`link_mv_account_auth_link_mv_account_post.response.201.email`; string, required) - `created_at` (`link_mv_account_auth_link_mv_account_post.response.201.created_at`; string, required) - format `date-time` ### 422 Validation Error #### Example ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string", "input": null, "ctx": {} } ] } ``` - `detail` (`link_mv_account_auth_link_mv_account_post.response.422.detail`; array, optional) - `loc` (`link_mv_account_auth_link_mv_account_post.response.422.detail.loc`; array, required) - any of: `string`, `integer` - `msg` (`link_mv_account_auth_link_mv_account_post.response.422.detail.msg`; string, required) - `type` (`link_mv_account_auth_link_mv_account_post.response.422.detail.type`; string, required) - `input` (`link_mv_account_auth_link_mv_account_post.response.422.detail.input`; unknown, optional) - `ctx` (`link_mv_account_auth_link_mv_account_post.response.422.detail.ctx`; object, optional) # Refresh tokens > Exchange a refresh token for a new ID token. Source: https://truememory-docs.mvstg.com/auth/refresh/ · Markdown: https://truememory-docs.mvstg.com/auth/refresh/index.md Path: Auth API › Auth `POST /auth/refresh` Exchange a refresh token for a new ID token. ## Request body _Required._ - `refresh_token` (`refresh_auth_refresh_post.refresh_token`; string, required) ## Example request ```json { "refresh_token": "string" } ``` ## Code samples ### cURL ```bash curl --request POST \ --url https://truememory-auth.mvstg.com/auth/refresh \ --header 'Content-Type: application/json' \ --data ' { "refresh_token": "string" } ' ``` ### TypeScript ```typescript const url = 'https://truememory-auth.mvstg.com/auth/refresh'; const options = { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({refresh_token: 'string'}) }; fetch(url, options) .then(res => res.json()) .then(json => console.log(json)) .catch(err => console.error(err)); ``` ### Python ```python import requests url = "https://truememory-auth.mvstg.com/auth/refresh" payload = { "refresh_token": "string" } headers = {"Content-Type": "application/json"} response = requests.post(url, json=payload, headers=headers) print(response.text) ``` ## Responses ### 200 Successful Response #### Example ```json { "id_token": "string", "refresh_token": "string", "expires_in": "string", "user": { "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "tenant_id": "34f5c98e-f430-457b-a812-92637d0c6fd0", "gip_uid": "string", "email": "string", "email_verified": false, "username": "string", "display_name": "string", "photo_url": "string", "phone_number": "string", "disabled": false, "role": "user", "metadata": {}, "raw_app_meta_data": {}, "custom_claims": {}, "last_sign_in_at": "2019-08-24T14:15:22Z", "created_at": "2019-08-24T14:15:22Z", "updated_at": "2019-08-24T14:15:22Z", "deleted_at": "2019-08-24T14:15:22Z", "deletion_completed_at": "2019-08-24T14:15:22Z" } } ``` - `id_token` (`refresh_auth_refresh_post.response.200.id_token`; string, required) - `refresh_token` (`refresh_auth_refresh_post.response.200.refresh_token`; string, required) - `expires_in` (`refresh_auth_refresh_post.response.200.expires_in`; string, required) - `user` (`refresh_auth_refresh_post.response.200.user`; any of, optional) - any of: `AuthUser`, `null` ### 422 Validation Error #### Example ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string", "input": null, "ctx": {} } ] } ``` - `detail` (`refresh_auth_refresh_post.response.422.detail`; array, optional) - `loc` (`refresh_auth_refresh_post.response.422.detail.loc`; array, required) - any of: `string`, `integer` - `msg` (`refresh_auth_refresh_post.response.422.detail.msg`; string, required) - `type` (`refresh_auth_refresh_post.response.422.detail.type`; string, required) - `input` (`refresh_auth_refresh_post.response.422.detail.input`; unknown, optional) - `ctx` (`refresh_auth_refresh_post.response.422.detail.ctx`; object, optional) # Register with email/password > Register a new user and return tokens. When ``tenant_id`` is provided the user is scoped to that tenant and can only authenticate within it. When omitted the user is created as a *platform-wide* user who can access any tenant/app. Source: https://truememory-docs.mvstg.com/auth/register/ · Markdown: https://truememory-docs.mvstg.com/auth/register/index.md Path: Auth API › Auth `POST /auth/register` Register a new user and return tokens. When ``tenant_id`` is provided the user is scoped to that tenant and can only authenticate within it. When omitted the user is created as a *platform-wide* user who can access any tenant/app. ## Request body _Required._ - `email` (`register_auth_register_post.email`; string, required) - format `email` - `password` (`register_auth_register_post.password`; string, required) - minLength 6 - `display_name` (`register_auth_register_post.display_name`; any of, optional) - any of: `string`, `null` - `tenant_id` (`register_auth_register_post.tenant_id`; any of, optional) — Google Identity Platform tenant ID. When set, the user is scoped to this tenant and can only authenticate within it. When omitted, the user is platform-wide and can access any app. Contact your admin to obtain a tenant ID for tenant-scoped registration. - any of: `string`, `null` - `metadata` (`register_auth_register_post.metadata`; any of, optional) — Arbitrary key/value profile data stored with the user account. Max 50 keys, 16 KB. - any of: `object`, `null` - `app_name` (`register_auth_register_post.app_name`; any of, optional) - any of: `string`, `null` ## Example request ```json { "email": "user@example.com", "password": "S3cur3P@ss!", "display_name": "Jane Doe", "tenant_id": "vibrantly-1234", "metadata": {}, "app_name": "string" } ``` ## Code samples ### cURL ```bash curl --request POST \ --url https://truememory-auth.mvstg.com/auth/register \ --header 'Content-Type: application/json' \ --data ' { "email": "user@example.com", "password": "S3cur3P@ss!", "display_name": "Jane Doe", "tenant_id": "vibrantly-1234", "metadata": {}, "app_name": "string" } ' ``` ### TypeScript ```typescript const url = 'https://truememory-auth.mvstg.com/auth/register'; const options = { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({ email: 'user@example.com', password: 'S3cur3P@ss!', display_name: 'Jane Doe', tenant_id: 'vibrantly-1234', metadata: {}, app_name: 'string' }) }; fetch(url, options) .then(res => res.json()) .then(json => console.log(json)) .catch(err => console.error(err)); ``` ### Python ```python import requests url = "https://truememory-auth.mvstg.com/auth/register" payload = { "email": "user@example.com", "password": "S3cur3P@ss!", "display_name": "Jane Doe", "tenant_id": "vibrantly-1234", "metadata": {}, "app_name": "string" } headers = {"Content-Type": "application/json"} response = requests.post(url, json=payload, headers=headers) print(response.text) ``` ## Responses ### 201 Successful Response #### Example ```json { "id_token": "string", "refresh_token": "string", "expires_in": "string", "user": { "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "tenant_id": "34f5c98e-f430-457b-a812-92637d0c6fd0", "gip_uid": "string", "email": "string", "email_verified": false, "username": "string", "display_name": "string", "photo_url": "string", "phone_number": "string", "disabled": false, "role": "user", "metadata": {}, "raw_app_meta_data": {}, "custom_claims": {}, "last_sign_in_at": "2019-08-24T14:15:22Z", "created_at": "2019-08-24T14:15:22Z", "updated_at": "2019-08-24T14:15:22Z", "deleted_at": "2019-08-24T14:15:22Z", "deletion_completed_at": "2019-08-24T14:15:22Z" } } ``` - `id_token` (`register_auth_register_post.response.201.id_token`; string, required) - `refresh_token` (`register_auth_register_post.response.201.refresh_token`; string, required) - `expires_in` (`register_auth_register_post.response.201.expires_in`; string, required) - `user` (`register_auth_register_post.response.201.user`; any of, optional) - any of: `AuthUser`, `null` ### 422 Validation Error #### Example ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string", "input": null, "ctx": {} } ] } ``` - `detail` (`register_auth_register_post.response.422.detail`; array, optional) - `loc` (`register_auth_register_post.response.422.detail.loc`; array, required) - any of: `string`, `integer` - `msg` (`register_auth_register_post.response.422.detail.msg`; string, required) - `type` (`register_auth_register_post.response.422.detail.type`; string, required) - `input` (`register_auth_register_post.response.422.detail.input`; unknown, optional) - `ctx` (`register_auth_register_post.response.422.detail.ctx`; object, optional) # Confirm password reset > Confirm a password reset using the OOB code from the reset email. The caller receives the OOB code via the deep-link URL in the reset email sent by ``POST /forgot-password``. This endpoint exchanges that code plus a new password for a completed Firebase password reset. Source: https://truememory-docs.mvstg.com/auth/reset-password/ · Markdown: https://truememory-docs.mvstg.com/auth/reset-password/index.md Path: Auth API › Auth `POST /auth/reset-password` Confirm a password reset using the OOB code from the reset email. The caller receives the OOB code via the deep-link URL in the reset email sent by ``POST /forgot-password``. This endpoint exchanges that code plus a new password for a completed Firebase password reset. ## Request body _Required._ - `oob_code` (`reset_password_auth_reset_password_post.oob_code`; string, required) - `new_password` (`reset_password_auth_reset_password_post.new_password`; string, required) - `tenant_id` (`reset_password_auth_reset_password_post.tenant_id`; any of, optional) - any of: `string`, `null` ## Example request ```json { "oob_code": "string", "new_password": "string", "tenant_id": "string" } ``` ## Code samples ### cURL ```bash curl --request POST \ --url https://truememory-auth.mvstg.com/auth/reset-password \ --header 'Content-Type: application/json' \ --data ' { "oob_code": "string", "new_password": "string", "tenant_id": "string" } ' ``` ### TypeScript ```typescript const url = 'https://truememory-auth.mvstg.com/auth/reset-password'; const options = { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({oob_code: 'string', new_password: 'string', tenant_id: 'string'}) }; fetch(url, options) .then(res => res.json()) .then(json => console.log(json)) .catch(err => console.error(err)); ``` ### Python ```python import requests url = "https://truememory-auth.mvstg.com/auth/reset-password" payload = { "oob_code": "string", "new_password": "string", "tenant_id": "string" } headers = {"Content-Type": "application/json"} response = requests.post(url, json=payload, headers=headers) print(response.text) ``` ## Responses ### 200 Successful Response #### Example ```json { "status": "ok" } ``` - `status` (`reset_password_auth_reset_password_post.response.200.status`; string, optional) - default `"ok"` ### 422 Validation Error #### Example ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string", "input": null, "ctx": {} } ] } ``` - `detail` (`reset_password_auth_reset_password_post.response.422.detail`; array, optional) - `loc` (`reset_password_auth_reset_password_post.response.422.detail.loc`; array, required) - any of: `string`, `integer` - `msg` (`reset_password_auth_reset_password_post.response.422.detail.msg`; string, required) - `type` (`reset_password_auth_reset_password_post.response.422.detail.type`; string, required) - `input` (`reset_password_auth_reset_password_post.response.422.detail.input`; unknown, optional) - `ctx` (`reset_password_auth_reset_password_post.response.422.detail.ctx`; object, optional) # Send a customised email-verification email via Resend > Send a customised email-verification email via Resend. No authentication required — mirrors the forgot-password pattern (identifies the user by email). Always returns ``{"status": "ok"}`` regardless of whether the email is registered, to prevent email enumeration. Source: https://truememory-docs.mvstg.com/auth/send-verification/ · Markdown: https://truememory-docs.mvstg.com/auth/send-verification/index.md Path: Auth API › Auth `POST /auth/send-verification` Send a customised email-verification email via Resend. No authentication required — mirrors the forgot-password pattern (identifies the user by email). Always returns ``{"status": "ok"}`` regardless of whether the email is registered, to prevent email enumeration. ## Request body _Required._ - `email` (`send_verification_auth_send_verification_post.email`; string, required) - format `email` - `tenant_id` (`send_verification_auth_send_verification_post.tenant_id`; any of, optional) - any of: `string`, `null` - `app_name` (`send_verification_auth_send_verification_post.app_name`; any of, optional) - any of: `string`, `null` ## Example request ```json { "email": "user@example.com", "tenant_id": "string", "app_name": "string" } ``` ## Code samples ### cURL ```bash curl --request POST \ --url https://truememory-auth.mvstg.com/auth/send-verification \ --header 'Content-Type: application/json' \ --data ' { "email": "user@example.com", "tenant_id": "string", "app_name": "string" } ' ``` ### TypeScript ```typescript const url = 'https://truememory-auth.mvstg.com/auth/send-verification'; const options = { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({email: 'user@example.com', tenant_id: 'string', app_name: 'string'}) }; fetch(url, options) .then(res => res.json()) .then(json => console.log(json)) .catch(err => console.error(err)); ``` ### Python ```python import requests url = "https://truememory-auth.mvstg.com/auth/send-verification" payload = { "email": "user@example.com", "tenant_id": "string", "app_name": "string" } headers = {"Content-Type": "application/json"} response = requests.post(url, json=payload, headers=headers) print(response.text) ``` ## Responses ### 200 Successful Response #### Example ```json { "status": "ok" } ``` - `status` (`send_verification_auth_send_verification_post.response.200.status`; string, optional) - default `"ok"` ### 422 Validation Error #### Example ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string", "input": null, "ctx": {} } ] } ``` - `detail` (`send_verification_auth_send_verification_post.response.422.detail`; array, optional) - `loc` (`send_verification_auth_send_verification_post.response.422.detail.loc`; array, required) - any of: `string`, `integer` - `msg` (`send_verification_auth_send_verification_post.response.422.detail.msg`; string, required) - `type` (`send_verification_auth_send_verification_post.response.422.detail.type`; string, required) - `input` (`send_verification_auth_send_verification_post.response.422.detail.input`; unknown, optional) - `ctx` (`send_verification_auth_send_verification_post.response.422.detail.ctx`; object, optional) # Auth Source: https://truememory-docs.mvstg.com/auth/tags/auth/ · Markdown: https://truememory-docs.mvstg.com/auth/tags/auth/index.md Path: Auth API ## Operations - [Register with email/password](/auth/register/) - [Login with email/password](/auth/login/) - [Refresh tokens](/auth/refresh/) - [Request password reset email](/auth/forgot-password/) - [Confirm password reset](/auth/reset-password/) - [Confirm email verification](/auth/verify-email/) - [Send a customised email-verification email via Resend](/auth/send-verification/) - [Get the linked Auth0 account for the current Firebase user](/auth/link-mv-account/) - [Link a Firebase account to an Auth0 account](/auth/post-link-mv-account/) - [Unlink the Auth0 account from the current Firebase user](/auth/delete-link-mv-account/) - [Get your profile](/auth/me/) - [Update your profile](/auth/patch-me/) - [Shallow-merge app-scoped metadata for the current user](/auth/me/app-metadata/) - [Check username availability](/auth/username/available/) - [Claim a username](/auth/username/claim/) - [Change your username](/auth/username/) - [Revert to previous username](/auth/username/revert/) - [Check if an email is registered](/auth/email/exists/) # OAuth Source: https://truememory-docs.mvstg.com/auth/tags/o-auth/ · Markdown: https://truememory-docs.mvstg.com/auth/tags/o-auth/index.md Path: Auth API ## Operations - [Sign in with an OAuth token](/auth/oauth/token/) - [Signing keys for OAuth access tokens](/auth/well-known/jwks-json/) - [Describe a pending sign-in request for the consent page](/auth/oauth/requests/get/) - [Signed-in user allows the client; returns where to send the browser](/auth/oauth/requests/approve/) - [Signed-in user denies the client; returns where to send the browser](/auth/oauth/requests/deny/) - [MCP clients this user is connected to](/auth/oauth/connections/list/) - [Disconnect a client; its access ends within 15 minutes](/auth/oauth/connections/delete/) # Change your username > Change your username, at most once every 14 days. The old username is reserved for those 14 days so only you can reclaim it, and a further change is refused until the cooldown expires. You must have claimed a username first — see ``POST /auth/username/claim``. Source: https://truememory-docs.mvstg.com/auth/username/ · Markdown: https://truememory-docs.mvstg.com/auth/username/index.md Path: Auth API › Auth `PUT /auth/username` Change your username, at most once every 14 days. The old username is reserved for those 14 days so only you can reclaim it, and a further change is refused until the cooldown expires. You must have claimed a username first — see ``POST /auth/username/claim``. ## Authentication - `HTTPBearer`, http, header `Authorization` ## Request body _Required._ - `username` (`change_username_auth_username_put.username`; string, required) — New username. 3-30 chars, lowercase alphanumeric, hyphens, underscores. ## Example request ```json { "username": "new_handle" } ``` ## Code samples ### cURL ```bash curl --request PUT \ --url https://truememory-auth.mvstg.com/auth/username \ --header 'Authorization: Bearer ' \ --header 'Content-Type: application/json' \ --data ' { "username": "new_handle" } ' ``` ### TypeScript ```typescript const url = 'https://truememory-auth.mvstg.com/auth/username'; const options = { method: 'PUT', headers: {'Content-Type': 'application/json', Authorization: 'Bearer '}, body: JSON.stringify({username: 'new_handle'}) }; fetch(url, options) .then(res => res.json()) .then(json => console.log(json)) .catch(err => console.error(err)); ``` ### Python ```python import requests url = "https://truememory-auth.mvstg.com/auth/username" payload = { "username": "new_handle" } headers = { "Content-Type": "application/json", "Authorization": "Bearer " } response = requests.put(url, json=payload, headers=headers) print(response.text) ``` ## Responses ### 200 Successful Response #### Example ```json { "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "tenant_id": "34f5c98e-f430-457b-a812-92637d0c6fd0", "gip_uid": "string", "email": "string", "email_verified": false, "username": "string", "display_name": "string", "photo_url": "string", "phone_number": "string", "disabled": false, "role": "user", "metadata": {}, "raw_app_meta_data": {}, "custom_claims": {}, "last_sign_in_at": "2019-08-24T14:15:22Z", "created_at": "2019-08-24T14:15:22Z", "updated_at": "2019-08-24T14:15:22Z", "deleted_at": "2019-08-24T14:15:22Z", "deletion_completed_at": "2019-08-24T14:15:22Z" } ``` - `id` (`change_username_auth_username_put.response.200.id`; string, required) - format `uuid` - `gip_uid` (`change_username_auth_username_put.response.200.gip_uid`; string, required) - `tenant_id` (`change_username_auth_username_put.response.200.tenant_id`; any of, optional) - any of: `string`, `null` - `email` (`change_username_auth_username_put.response.200.email`; any of, optional) - any of: `string`, `null` - `email_verified` (`change_username_auth_username_put.response.200.email_verified`; boolean, optional) - default `false` - `username` (`change_username_auth_username_put.response.200.username`; any of, optional) - any of: `string`, `null` - `display_name` (`change_username_auth_username_put.response.200.display_name`; any of, optional) - any of: `string`, `null` - `photo_url` (`change_username_auth_username_put.response.200.photo_url`; any of, optional) - any of: `string`, `null` - `phone_number` (`change_username_auth_username_put.response.200.phone_number`; any of, optional) - any of: `string`, `null` - `disabled` (`change_username_auth_username_put.response.200.disabled`; boolean, optional) - default `false` - `role` (`change_username_auth_username_put.response.200.role`; string, optional) - default `"user"` - `metadata` (`change_username_auth_username_put.response.200.metadata`; object, optional) - `raw_app_meta_data` (`change_username_auth_username_put.response.200.raw_app_meta_data`; object, optional) - `custom_claims` (`change_username_auth_username_put.response.200.custom_claims`; object, optional) - `last_sign_in_at` (`change_username_auth_username_put.response.200.last_sign_in_at`; any of, optional) - any of: `string`, `null` - `created_at` (`change_username_auth_username_put.response.200.created_at`; any of, optional) - any of: `string`, `null` - `updated_at` (`change_username_auth_username_put.response.200.updated_at`; any of, optional) - any of: `string`, `null` - `deleted_at` (`change_username_auth_username_put.response.200.deleted_at`; any of, optional) - any of: `string`, `null` - `deletion_completed_at` (`change_username_auth_username_put.response.200.deletion_completed_at`; any of, optional) - any of: `string`, `null` ### 422 Validation Error #### Example ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string", "input": null, "ctx": {} } ] } ``` - `detail` (`change_username_auth_username_put.response.422.detail`; array, optional) - `loc` (`change_username_auth_username_put.response.422.detail.loc`; array, required) - any of: `string`, `integer` - `msg` (`change_username_auth_username_put.response.422.detail.msg`; string, required) - `type` (`change_username_auth_username_put.response.422.detail.type`; string, required) - `input` (`change_username_auth_username_put.response.422.detail.input`; unknown, optional) - `ctx` (`change_username_auth_username_put.response.422.detail.ctx`; object, optional) # Check username availability > Check if a username is globally available. Source: https://truememory-docs.mvstg.com/auth/username/available/ · Markdown: https://truememory-docs.mvstg.com/auth/username/available/index.md Path: Auth API › Auth `GET /auth/username/available` Check if a username is globally available. ## Query parameters - `username` (`check_username_available_auth_username_available_get.query.username`; string, required) ## Code samples ### cURL ```bash curl --request GET \ --url 'https://truememory-auth.mvstg.com/auth/username/available?username=string' ``` ### TypeScript ```typescript const url = 'https://truememory-auth.mvstg.com/auth/username/available?username=string'; const options = {method: 'GET'}; fetch(url, options) .then(res => res.json()) .then(json => console.log(json)) .catch(err => console.error(err)); ``` ### Python ```python import requests url = "https://truememory-auth.mvstg.com/auth/username/available?username=string" response = requests.get(url) print(response.text) ``` ## Responses ### 200 Successful Response #### Example ```json { "username": "string", "available": true } ``` - `username` (`check_username_available_auth_username_available_get.response.200.username`; string, required) - `available` (`check_username_available_auth_username_available_get.response.200.available`; boolean, required) ### 422 Validation Error #### Example ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string", "input": null, "ctx": {} } ] } ``` - `detail` (`check_username_available_auth_username_available_get.response.422.detail`; array, optional) - `loc` (`check_username_available_auth_username_available_get.response.422.detail.loc`; array, required) - any of: `string`, `integer` - `msg` (`check_username_available_auth_username_available_get.response.422.detail.msg`; string, required) - `type` (`check_username_available_auth_username_available_get.response.422.detail.type`; string, required) - `input` (`check_username_available_auth_username_available_get.response.422.detail.input`; unknown, optional) - `ctx` (`check_username_available_auth_username_available_get.response.422.detail.ctx`; object, optional) # Claim a username > Claim a globally unique username. Claiming is one-time: once you have a username, this endpoint returns 409. Changing it afterwards goes through ``PUT /auth/username``, which is subject to a 14-day cooldown. Source: https://truememory-docs.mvstg.com/auth/username/claim/ · Markdown: https://truememory-docs.mvstg.com/auth/username/claim/index.md Path: Auth API › Auth `POST /auth/username/claim` Claim a globally unique username. Claiming is one-time: once you have a username, this endpoint returns 409. Changing it afterwards goes through ``PUT /auth/username``, which is subject to a 14-day cooldown. ## Authentication - `HTTPBearer`, http, header `Authorization` ## Request body _Required._ - `username` (`claim_username_auth_username_claim_post.username`; string, required) — Desired username. 3-30 chars, lowercase alphanumeric, hyphens, underscores. ## Example request ```json { "username": "jane_doe" } ``` ## Code samples ### cURL ```bash curl --request POST \ --url https://truememory-auth.mvstg.com/auth/username/claim \ --header 'Authorization: Bearer ' \ --header 'Content-Type: application/json' \ --data ' { "username": "jane_doe" } ' ``` ### TypeScript ```typescript const url = 'https://truememory-auth.mvstg.com/auth/username/claim'; const options = { method: 'POST', headers: {'Content-Type': 'application/json', Authorization: 'Bearer '}, body: JSON.stringify({username: 'jane_doe'}) }; fetch(url, options) .then(res => res.json()) .then(json => console.log(json)) .catch(err => console.error(err)); ``` ### Python ```python import requests url = "https://truememory-auth.mvstg.com/auth/username/claim" payload = { "username": "jane_doe" } headers = { "Content-Type": "application/json", "Authorization": "Bearer " } response = requests.post(url, json=payload, headers=headers) print(response.text) ``` ## Responses ### 200 Successful Response #### Example ```json { "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "tenant_id": "34f5c98e-f430-457b-a812-92637d0c6fd0", "gip_uid": "string", "email": "string", "email_verified": false, "username": "string", "display_name": "string", "photo_url": "string", "phone_number": "string", "disabled": false, "role": "user", "metadata": {}, "raw_app_meta_data": {}, "custom_claims": {}, "last_sign_in_at": "2019-08-24T14:15:22Z", "created_at": "2019-08-24T14:15:22Z", "updated_at": "2019-08-24T14:15:22Z", "deleted_at": "2019-08-24T14:15:22Z", "deletion_completed_at": "2019-08-24T14:15:22Z" } ``` - `id` (`claim_username_auth_username_claim_post.response.200.id`; string, required) - format `uuid` - `gip_uid` (`claim_username_auth_username_claim_post.response.200.gip_uid`; string, required) - `tenant_id` (`claim_username_auth_username_claim_post.response.200.tenant_id`; any of, optional) - any of: `string`, `null` - `email` (`claim_username_auth_username_claim_post.response.200.email`; any of, optional) - any of: `string`, `null` - `email_verified` (`claim_username_auth_username_claim_post.response.200.email_verified`; boolean, optional) - default `false` - `username` (`claim_username_auth_username_claim_post.response.200.username`; any of, optional) - any of: `string`, `null` - `display_name` (`claim_username_auth_username_claim_post.response.200.display_name`; any of, optional) - any of: `string`, `null` - `photo_url` (`claim_username_auth_username_claim_post.response.200.photo_url`; any of, optional) - any of: `string`, `null` - `phone_number` (`claim_username_auth_username_claim_post.response.200.phone_number`; any of, optional) - any of: `string`, `null` - `disabled` (`claim_username_auth_username_claim_post.response.200.disabled`; boolean, optional) - default `false` - `role` (`claim_username_auth_username_claim_post.response.200.role`; string, optional) - default `"user"` - `metadata` (`claim_username_auth_username_claim_post.response.200.metadata`; object, optional) - `raw_app_meta_data` (`claim_username_auth_username_claim_post.response.200.raw_app_meta_data`; object, optional) - `custom_claims` (`claim_username_auth_username_claim_post.response.200.custom_claims`; object, optional) - `last_sign_in_at` (`claim_username_auth_username_claim_post.response.200.last_sign_in_at`; any of, optional) - any of: `string`, `null` - `created_at` (`claim_username_auth_username_claim_post.response.200.created_at`; any of, optional) - any of: `string`, `null` - `updated_at` (`claim_username_auth_username_claim_post.response.200.updated_at`; any of, optional) - any of: `string`, `null` - `deleted_at` (`claim_username_auth_username_claim_post.response.200.deleted_at`; any of, optional) - any of: `string`, `null` - `deletion_completed_at` (`claim_username_auth_username_claim_post.response.200.deletion_completed_at`; any of, optional) - any of: `string`, `null` ### 422 Validation Error #### Example ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string", "input": null, "ctx": {} } ] } ``` - `detail` (`claim_username_auth_username_claim_post.response.422.detail`; array, optional) - `loc` (`claim_username_auth_username_claim_post.response.422.detail.loc`; array, required) - any of: `string`, `integer` - `msg` (`claim_username_auth_username_claim_post.response.422.detail.msg`; string, required) - `type` (`claim_username_auth_username_claim_post.response.422.detail.type`; string, required) - `input` (`claim_username_auth_username_claim_post.response.422.detail.input`; unknown, optional) - `ctx` (`claim_username_auth_username_claim_post.response.422.detail.ctx`; object, optional) # Revert to previous username > Revert to the most recently released username within the 14-day grace period. Source: https://truememory-docs.mvstg.com/auth/username/revert/ · Markdown: https://truememory-docs.mvstg.com/auth/username/revert/index.md Path: Auth API › Auth `POST /auth/username/revert` Revert to the most recently released username within the 14-day grace period. ## Authentication - `HTTPBearer`, http, header `Authorization` ## Code samples ### cURL ```bash curl --request POST \ --url https://truememory-auth.mvstg.com/auth/username/revert \ --header 'Authorization: Bearer ' ``` ### TypeScript ```typescript const url = 'https://truememory-auth.mvstg.com/auth/username/revert'; const options = {method: 'POST', headers: {Authorization: 'Bearer '}}; fetch(url, options) .then(res => res.json()) .then(json => console.log(json)) .catch(err => console.error(err)); ``` ### Python ```python import requests url = "https://truememory-auth.mvstg.com/auth/username/revert" headers = {"Authorization": "Bearer "} response = requests.post(url, headers=headers) print(response.text) ``` ## Responses ### 200 Successful Response #### Example ```json { "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "tenant_id": "34f5c98e-f430-457b-a812-92637d0c6fd0", "gip_uid": "string", "email": "string", "email_verified": false, "username": "string", "display_name": "string", "photo_url": "string", "phone_number": "string", "disabled": false, "role": "user", "metadata": {}, "raw_app_meta_data": {}, "custom_claims": {}, "last_sign_in_at": "2019-08-24T14:15:22Z", "created_at": "2019-08-24T14:15:22Z", "updated_at": "2019-08-24T14:15:22Z", "deleted_at": "2019-08-24T14:15:22Z", "deletion_completed_at": "2019-08-24T14:15:22Z" } ``` - `id` (`revert_username_auth_username_revert_post.response.200.id`; string, required) - format `uuid` - `gip_uid` (`revert_username_auth_username_revert_post.response.200.gip_uid`; string, required) - `tenant_id` (`revert_username_auth_username_revert_post.response.200.tenant_id`; any of, optional) - any of: `string`, `null` - `email` (`revert_username_auth_username_revert_post.response.200.email`; any of, optional) - any of: `string`, `null` - `email_verified` (`revert_username_auth_username_revert_post.response.200.email_verified`; boolean, optional) - default `false` - `username` (`revert_username_auth_username_revert_post.response.200.username`; any of, optional) - any of: `string`, `null` - `display_name` (`revert_username_auth_username_revert_post.response.200.display_name`; any of, optional) - any of: `string`, `null` - `photo_url` (`revert_username_auth_username_revert_post.response.200.photo_url`; any of, optional) - any of: `string`, `null` - `phone_number` (`revert_username_auth_username_revert_post.response.200.phone_number`; any of, optional) - any of: `string`, `null` - `disabled` (`revert_username_auth_username_revert_post.response.200.disabled`; boolean, optional) - default `false` - `role` (`revert_username_auth_username_revert_post.response.200.role`; string, optional) - default `"user"` - `metadata` (`revert_username_auth_username_revert_post.response.200.metadata`; object, optional) - `raw_app_meta_data` (`revert_username_auth_username_revert_post.response.200.raw_app_meta_data`; object, optional) - `custom_claims` (`revert_username_auth_username_revert_post.response.200.custom_claims`; object, optional) - `last_sign_in_at` (`revert_username_auth_username_revert_post.response.200.last_sign_in_at`; any of, optional) - any of: `string`, `null` - `created_at` (`revert_username_auth_username_revert_post.response.200.created_at`; any of, optional) - any of: `string`, `null` - `updated_at` (`revert_username_auth_username_revert_post.response.200.updated_at`; any of, optional) - any of: `string`, `null` - `deleted_at` (`revert_username_auth_username_revert_post.response.200.deleted_at`; any of, optional) - any of: `string`, `null` - `deletion_completed_at` (`revert_username_auth_username_revert_post.response.200.deletion_completed_at`; any of, optional) - any of: `string`, `null` # Confirm email verification > Confirm an email verification using the OOB code from the verification email. The caller receives the OOB code via the deep-link URL in the verification email sent by ``POST /send-verification`` (custom Resend template). This is the server-side equivalent of the Firebase client SDK's ``applyActionCode`` — clients no longer talk to Firebase directly. Source: https://truememory-docs.mvstg.com/auth/verify-email/ · Markdown: https://truememory-docs.mvstg.com/auth/verify-email/index.md Path: Auth API › Auth `POST /auth/verify-email` Confirm an email verification using the OOB code from the verification email. The caller receives the OOB code via the deep-link URL in the verification email sent by ``POST /send-verification`` (custom Resend template). This is the server-side equivalent of the Firebase client SDK's ``applyActionCode`` — clients no longer talk to Firebase directly. ## Request body _Required._ - `oob_code` (`verify_email_auth_verify_email_post.oob_code`; string, required) - `tenant_id` (`verify_email_auth_verify_email_post.tenant_id`; any of, optional) - any of: `string`, `null` ## Example request ```json { "oob_code": "string", "tenant_id": "string" } ``` ## Code samples ### cURL ```bash curl --request POST \ --url https://truememory-auth.mvstg.com/auth/verify-email \ --header 'Content-Type: application/json' \ --data ' { "oob_code": "string", "tenant_id": "string" } ' ``` ### TypeScript ```typescript const url = 'https://truememory-auth.mvstg.com/auth/verify-email'; const options = { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({oob_code: 'string', tenant_id: 'string'}) }; fetch(url, options) .then(res => res.json()) .then(json => console.log(json)) .catch(err => console.error(err)); ``` ### Python ```python import requests url = "https://truememory-auth.mvstg.com/auth/verify-email" payload = { "oob_code": "string", "tenant_id": "string" } headers = {"Content-Type": "application/json"} response = requests.post(url, json=payload, headers=headers) print(response.text) ``` ## Responses ### 200 Successful Response #### Example ```json { "status": "ok" } ``` - `status` (`verify_email_auth_verify_email_post.response.200.status`; string, optional) - default `"ok"` ### 422 Validation Error #### Example ```json { "detail": [ { "loc": [ "string" ], "msg": "string", "type": "string", "input": null, "ctx": {} } ] } ``` - `detail` (`verify_email_auth_verify_email_post.response.422.detail`; array, optional) - `loc` (`verify_email_auth_verify_email_post.response.422.detail.loc`; array, required) - any of: `string`, `integer` - `msg` (`verify_email_auth_verify_email_post.response.422.detail.msg`; string, required) - `type` (`verify_email_auth_verify_email_post.response.422.detail.type`; string, required) - `input` (`verify_email_auth_verify_email_post.response.422.detail.input`; unknown, optional) - `ctx` (`verify_email_auth_verify_email_post.response.422.detail.ctx`; object, optional) # Signing keys for OAuth access tokens Source: https://truememory-docs.mvstg.com/auth/well-known/jwks-json/ · Markdown: https://truememory-docs.mvstg.com/auth/well-known/jwks-json/index.md Path: Auth API › OAuth `GET /.well-known/jwks.json` ## Code samples ### cURL ```bash curl --request GET \ --url https://truememory-auth.mvstg.com/.well-known/jwks.json ``` ### TypeScript ```typescript const url = 'https://truememory-auth.mvstg.com/.well-known/jwks.json'; const options = {method: 'GET'}; fetch(url, options) .then(res => res.json()) .then(json => console.log(json)) .catch(err => console.error(err)); ``` ### Python ```python import requests url = "https://truememory-auth.mvstg.com/.well-known/jwks.json" response = requests.get(url) print(response.text) ``` ## Responses ### 200 Successful Response #### Example ```json { "keys": [ { "kty": "string", "kid": "string", "use": "string", "alg": "string", "n": "string", "e": "string" } ] } ``` - `keys` (`jwks__well_known_jwks_json_get.response.200.keys`; array, required) - `kty` (`jwks__well_known_jwks_json_get.response.200.keys.kty`; string, required) - `kid` (`jwks__well_known_jwks_json_get.response.200.keys.kid`; string, required) - `use` (`jwks__well_known_jwks_json_get.response.200.keys.use`; string, required) - `alg` (`jwks__well_known_jwks_json_get.response.200.keys.alg`; string, required) - `n` (`jwks__well_known_jwks_json_get.response.200.keys.n`; string, required) - `e` (`jwks__well_known_jwks_json_get.response.200.keys.e`; string, required) # Delete a user > Erase a user from Truememory on your app's instruction — memory in every scope, threads, uploads, identity, traces — and prove it was done. Source: https://truememory-docs.mvstg.com/authentication/delete-user/ · Markdown: https://truememory-docs.mvstg.com/authentication/delete-user/index.md When a user asks your app to delete their account, this is the one call that removes them from Truememory. Your app owns the relationship with the user and decides when their data goes; Truememory holds what grew out of what you sent it — the memories it extracted, chat threads, uploads, the identity record it keeps for the user — and erases all of it on your instruction. In privacy-law terms your app is the *controller* and Truememory the *processor*; this endpoint is how the controller's "delete" reaches the processor. It is broader than [`clear`](/memory/deleting-data/#clear-all-memories): `clear` empties one memory scope (one user × one agent, or one namespace), while this erases the person — memory in **every** agent scope, chat threads and their checkpoints, queued background work, uploaded knowledge packs, minds they own, their identity record and tokens, and their traces in observability. ```bash DELETE https://your-tie-host/users/{user_id} Authorization: Bearer $APP_TOKEN Content-Type: application/json { "reason": "gdpr_art17", "callback_url": "https://your-app/hooks/truememory" } ``` | Field | In | Required | Description | |-------|------|----------|-------------| | `user_id` | path | Yes | The Truememory user UUID (the `user_id` your other calls return). Service callers may send `X-On-Behalf-Of` or `X-External-User-Id` instead, as long as it matches the path (see [Service accounts](/authentication/service-accounts/#service-acting-as-a-user)). | | `reason` | body | No | Free text for the service logs, e.g. `gdpr_art17`. | | `callback_url` | body | No | Receives one `POST` with the outcome (payload below). | | `include_linked` | body | No | Also erase the Mindvalley-linked namespace. Default `false`: that data belongs to a different controller. | | `dry_run` | query | No | `true` returns the row counts and memory scopes a deletion would remove, and writes nothing. It does not cover identity, checkpoints, traces or files. | The caller must be an app token or an admin; user tokens are refused. ## What happens The request does two things and answers at once. It marks the user deleted and starts the user's deletion job. From the mark on, any request that reaches Truememory with the user's [ID token](/authentication/tokens/), or names them in `X-On-Behalf-Of` or `X-External-User-Id`, answers `410 USER_DELETED`, and work that was already queued for the user is dropped instead of run. The job runs on Truememory's maintenance workers, normally within seconds: it removes the identity-provider account and revokes its tokens, then empties every store — threads and their checkpoints, queued work, memory in every scope, member records in shared graphs, owned resources, uploaded files, traces, caches. It is one job per user, so a second `DELETE` by path while it runs is a no-op and answers `202` again. While the id is still blocked (until the deletion completed, plus one hour), a `DELETE` for a completed user answers `202` with `status: completed` and starts nothing; after that hour the same call starts a new deletion. A request that names the deleted user in `X-On-Behalf-Of` or `X-External-User-Id` is refused with `410` before it reaches this route, so address a deleted user by the path alone. A store that cannot be verified clean makes the job run again, up to three times with growing pauses; if it still fails, the status is `failed` and a new `DELETE` starts a new job. Think of it like a ticket in a durable job queue with the user's id as the ticket number: the request only files the ticket, the worker does the work and retries it, and the same number cannot be filed twice while it is open. Where the comparison breaks down: there is no "cancel". Once the user is marked deleted the data cannot be restored, even while the job is still running. **Response `202`** as soon as the ticket is filed: ```json { "user_id": "22c78ed6-…", "status": "accepted", "requested_at": "2026-10-09T09:26:11Z", "completed_at": null } ``` The user is locked out of Truememory when this response arrives. Watch the status for `completed` before you tell the user their data is gone, or let the callback tell you. ## Check the status ```bash GET https://your-tie-host/users/{user_id}/deletion Authorization: Bearer $APP_TOKEN ``` `status` is `running` while the job works, `completed` once every store is clean, and `failed` when the job ended without completing — call `DELETE` again and a new job starts. Any app token or an admin may call it. It answers `404` when no deletion was ever requested for that user, and keeps answering afterwards, so you can show the completion as proof of erasure later. What was removed per store is in Truememory's service logs, not in this response. Requests made by an admin also leave a row in the admin audit trail. If you passed `callback_url`, Truememory also sends one `POST` to it with the outcome (none if the job could not be started): ```json { "event": "user.deletion", "user_id": "22c78ed6-…", "status": "completed", "completed_at": "2026-10-09T09:26:14.265080+00:00" } ``` The callback is best-effort and is not retried; the status endpoint is the source of truth. What happens afterwards: - **The id is blocked until the deletion completed, plus one hour.** Any request that names the user — a still-valid ID token, `X-On-Behalf-Of`, `X-External-User-Id` — answers `410 USER_DELETED`. The hour covers an ID token issued before the delete; after it the same external id registers again as a fresh, empty user, and nothing of the old user comes back. If your app wants a waiting period before the same person can return, that is your rule to keep: Truememory does not hold the id longer. Langfuse deletes traces asynchronously on its side, so a trace can be visible there for up to about 15 minutes after `completed`. - **The user still counts.** Truememory keeps the user's row in its identity table with every personal field emptied, so "how many users did we ever have" stays answerable. Nothing on that row can name a person. - **Backups and logs expire on their own schedules.** Truememory does not restore deleted users; the retention windows are documented in the processing agreement. :::note[What is not removed] Mentions of the person *by name* inside other members' memories in a shared graph are not keyed by id and are the other members' data; they stay. If your app lets members write about each other, say so in your privacy notice. ::: # Sign in with email > Registration, login, password reset, and email verification flows. Source: https://truememory-docs.mvstg.com/authentication/email-password/ · Markdown: https://truememory-docs.mvstg.com/authentication/email-password/index.md All requests go to the **Truememory Auth base URL** (see [Before you start](/authentication/overview/#before-you-start) for prerequisites: base URL, CORS whitelisting, OAuth client IDs). ## Registration (Email/Password) Create a new user account. Password must be at least 6 characters. ```bash POST https://your-auth-host/auth/register Content-Type: application/json { "email": "user@example.com", "password": "SecurePassword123!", "display_name": "Jane Doe", "app_name": "Vibrantly" } ``` `app_name` is optional — it brands the verification email sent at signup (see [Email Verification](#email-verification)); omitted, the email uses Truememory branding. **Response** (`201 Created`): ```json { "id_token": "eyJhbGciOiJSUzI1NiIs...", "refresh_token": "AMf-xB...", "expires_in": "3600", "user": { "id": "4b822c30-1a07-40d6-8d59-321ef99037f6", "gip_uid": "5iexdslxx7hFwmKPIdlssLRkREA3", "email": "user@example.com", "email_verified": false, "username": null, "display_name": "Jane Doe", "photo_url": null, "phone_number": null, "disabled": false, "role": "user", "metadata": {}, "created_at": "2026-03-25T10:00:00Z", "updated_at": "2026-03-25T10:00:00Z" } } ``` All auth endpoints (`/register`, `/login`, `/oauth/token`) return this same response shape: an `id_token` for authenticating API calls, a `refresh_token` for getting new tokens when the current one expires, and a `user` object with the account details. **Error responses:** | Status | Code | When | |--------|------|------| | `409 Conflict` | `AUTH_EMAIL_EXISTS` | A user with this email is already registered | | `400 Bad Request` | `AUTH_REGISTRATION_FAILED` | Other registration failures (e.g. provider error) | | `422 Unprocessable Entity` | `VALIDATION_ERROR` | Invalid email format or password too short | ### User IDs - `user.id` — Truememory's canonical user UUID. Deterministic — the same Firebase user always produces the same UUID, across all services, without a central registry or database lookup. - `user.gip_uid` — The raw Firebase/GIP UID. Also appears as `sub` in the JWT. :::tip[Store `user.id` in your database] All Truememory services (memory, personas, agents) reference users by `user.id`. Use it as your primary key for seamless cross-service integration. Backend services also need this UUID when calling Truememory [on behalf of a user](/authentication/service-accounts/#service-acting-as-a-user). ::: ## Login (Email/Password) ```bash POST https://your-auth-host/auth/login Content-Type: application/json { "email": "user@example.com", "password": "SecurePassword123!" } ``` Returns the same `{ id_token, refresh_token, expires_in, user }` shape as registration. ## Password Reset The reset flow is two steps: request the email, then confirm the new password using the code from the link. ### Step 1 — Request reset email ```bash POST https://your-auth-host/auth/forgot-password Content-Type: application/json { "email": "user@example.com", "app_name": "Vibrantly" } ``` | Field | Required | Description | |-------|----------|-------------| | `email` | Yes | The account's email address | | `app_name` | No | Your app's display name. When set, the reset email **and** the hosted reset page use your app's branding; when omitted, both use Truememory branding. | Always returns `{ "status": "ok" }` regardless of whether the email exists (prevents email enumeration). Firebase sends a password reset email if the account exists. :::note[App branding must be registered first] Per-app branding is managed by the Truememory team — notify them and provide your logo and branding details before using `app_name`, so the email and reset page render them. Unregistered names fall back to Truememory branding. ::: ### Step 2 — User completes the reset on the hosted page The emailed link opens **Truememory's hosted reset page** (branded per `app_name`), where the user chooses a new password — your app doesn't need to build a reset screen or handle the link at all. If you run a custom flow instead (e.g. a native in-app reset screen that intercepts the link), extract the `oobCode` parameter from the link and exchange it yourself — this is the same endpoint the hosted page uses: ```bash POST https://your-auth-host/auth/reset-password Content-Type: application/json { "oob_code": "", "new_password": "NewSecurePassword123!" } ``` **Response** (`200 OK`): ```json { "status": "ok" } ``` **Error responses:** | Status | Code | When | |--------|------|------| | `400 Bad Request` | `AUTH_INVALID_OOB_CODE` | The code is invalid or has expired — ask the user to request a new reset email | | `400 Bad Request` | `AUTH_PASSWORD_RESET_FAILED` | Other failure from Firebase | :::tip[After a successful reset] The user's existing sessions are not invalidated automatically. If you want to sign the user in immediately after the reset, call `POST /auth/login` with their email and new password to get fresh tokens. ::: ## Email Verification When a user registers with email/password, their `email_verified` field starts as `false`. Truememory Auth automatically sends a verification email on registration — no extra API call needed. The `user` object in all auth responses includes `email_verified`, so your app can check it and show a verification prompt when it's `false`. ### Re-send verification email If the user did not receive the email or the link expired, they can request a new one. Pass `app_name` (optional) to use your app's branding on the email and the hosted verification page — same rules as [password reset](#password-reset), including the branding registration requirement: ```bash POST https://your-auth-host/auth/send-verification-email Authorization: Bearer Content-Type: application/json { "email": "user@example.com", "app_name": "Vibrantly" } ``` **Response** (`200 OK`): ```json { "status": "ok" } ``` Returns `400` with code `AUTH_VERIFICATION_FAILED` if the email is already verified. ### Checking verification status after the user clicks the link When the user clicks the verification link, Google Identity Platform marks their email as verified on Google's side. Your app's token still carries the old `email_verified: false` until you refresh it. :::note[This is Google's recommended approach] Google Identity Platform does not push a real-time event when a user verifies their email. The standard pattern — recommended by Google — is for the client to refresh the token after the user returns from the verification link. The fresh token carries the updated `email_verified: true` claim. ::: **The client-side flow:** ```mermaid sequenceDiagram accTitle: Email Verification Refresh Flow accDescr: Shows how a client detects email verification after the user clicks the link participant User participant App as Your App participant Truememory as Truememory Auth participant GIP as Google Identity Platform App->>App: Show "Verify your email" banner User->>User: Open email, click verification link User->>GIP: Browser opens verification URL GIP->>GIP: Mark email as verified User->>App: Return to app App->>Truememory: POST /auth/refresh { refresh_token } Truememory-->>App: New id_token (email_verified: true) App->>Truememory: GET /auth/me Truememory-->>App: user.email_verified = true App->>App: Hide verification banner ``` **When to trigger the refresh** — the client should call `POST /auth/refresh` when it detects the user has returned to the app after clicking the verification link: ### Why OAuth users skip verification Users who sign in with Google or Apple already have a verified email — the provider confirms it during sign-in. Their `email_verified` is `true` from the first login, so no verification email is sent. # Sign in with Google & Apple > Client-side OAuth sign-in with Google and Apple, exchanged for Truememory tokens. Source: https://truememory-docs.mvstg.com/authentication/oauth/ · Markdown: https://truememory-docs.mvstg.com/authentication/oauth/index.md All requests go to the **Truememory Auth base URL** (see [Before you start](/authentication/overview/#before-you-start) for prerequisites: base URL, CORS whitelisting, OAuth client IDs). ## OAuth Sign-In (Google / Apple) For OAuth, the frontend handles the provider interaction (consent screen, token retrieval) and the resulting provider token is exchanged with Truememory Auth for Truememory tokens. Truememory Auth creates or links the user account automatically on first sign-in — no separate registration step needed. The examples below route that exchange through **your own backend**, so the returned `refresh_token` never reaches browser JavaScript — see [Suggested implementation](/authentication/token-handling/) for why, and for the route handler these examples call. A frontend with no backend can call `/auth/oauth/token` directly; that trade-off is documented on the same page. ### Flow ```mermaid sequenceDiagram accTitle: OAuth Sign-In Flow accDescr: Shows how a frontend app authenticates a user via Google or Apple through Truememory Auth participant User participant Frontend participant Provider as Google / Apple participant Backend as Your backend participant Truememory as Truememory Auth participant GIP as Google Identity Platform User->>Frontend: Click "Sign in with Google/Apple" Frontend->>Provider: Open consent screen Provider-->>Frontend: id_token (JWT) Frontend->>Backend: POST /api/auth/oauth { provider, id_token } Backend->>Truememory: POST /auth/oauth/token { provider, id_token } Truememory->>GIP: Verify token GIP-->>Truememory: Verified user info Truememory->>Truememory: Create or link user account Truememory-->>Backend: { id_token, refresh_token, expires_in, user } Backend-->>Frontend: id_token in body, refresh_token in httpOnly cookie ``` ### Endpoint ```bash POST https://your-auth-host/auth/oauth/token Content-Type: application/json { "provider": "google", "id_token": "" } ``` | Provider | `provider` value | |----------|-----------------| | Google | `google` | | Apple | `apple` | Response shape is the same as registration. ### Google Sign-In (Frontend — React) ```bash npm install @react-oauth/google ``` Wrap your app root with the Google OAuth provider. The `GOOGLE_CLIENT_ID` is the OAuth 2.0 Client ID from your GCP project: ```tsx // providers.tsx import { GoogleOAuthProvider } from "@react-oauth/google"; const GOOGLE_CLIENT_ID = process.env.NEXT_PUBLIC_GOOGLE_CLIENT_ID || ""; export function Providers({ children }: { children: React.ReactNode }) { return ( {children} ); } ``` Add the login button. It posts the Google credential to your own backend route, which exchanges it with Truememory Auth and sets the refresh-token cookie: ```tsx // login-page.tsx import { GoogleLogin, type CredentialResponse } from "@react-oauth/google"; function LoginPage() { const handleGoogleSuccess = async (response: CredentialResponse) => { if (!response.credential) return; const res = await fetch("/api/auth/oauth", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ provider: "google", id_token: response.credential, }), }); if (!res.ok) { console.error("Sign-in failed"); return; } // Your route returns { id_token, expires_in, user } and keeps the // refresh token in an httpOnly cookie. Hold the ID token in memory only. const data = await res.json(); setIdToken(data.id_token, Number(data.expires_in)); }; return ( console.error("Google sign-in failed")} size="large" theme="outline" /> ); } ``` ### Apple Sign-In (Frontend — React) ```bash npm install react-apple-signin-auth ``` Replace `APPLE_CLIENT_ID` with the Service ID from your Apple Developer account. As with Google, the provider token goes to your own backend route: ```tsx import AppleSignin from "react-apple-signin-auth"; function LoginPage() { const handleAppleSuccess = async (response: any) => { const idToken = response.authorization?.id_token; if (!idToken) return; const res = await fetch("/api/auth/oauth", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ provider: "apple", id_token: idToken, }), }); if (!res.ok) { console.error("Sign-in failed"); return; } const data = await res.json(); setIdToken(data.id_token, Number(data.expires_in)); }; return ( ); } ``` # Overview Source: https://truememory-docs.mvstg.com/authentication/overview/ · Markdown: https://truememory-docs.mvstg.com/authentication/overview/index.md Authentication is how TIE verifies who is making a request. Every call to TIE — whether it comes from a user's browser, a mobile app, or a backend service — must include a credential that proves the caller's identity. TIE supports two authentication methods. **User tokens** let real people sign in through email/password or OAuth providers (Google, Apple). **Service accounts** let backend services call TIE directly using a shared secret, without going through the user sign-in flow. Both methods result in a Bearer token passed in the `Authorization` header. Which method you choose depends on who is calling and whether the request needs a user's context (memory, persona, conversation history). This page helps you pick the right one. ## What are you trying to do? | I want to... | Method | Guide | |-------------|--------|-------| | Let users sign in from my **web or mobile app** | User tokens | [User tokens](/authentication/email-password/) | | Call TIE from my **backend service on behalf of a user** (e.g. WhatsApp webhook, cron job) | Service account + `X-On-Behalf-Of` | [Service accounts](/authentication/service-accounts/#service-acting-as-a-user) | | Call TIE from my **backend for a one-shot task** (e.g. title generation, summarization) | Service account (stateless) | [Service accounts](/authentication/service-accounts/#service-only-stateless) | | **Verify a TIE token** in my own backend | Token verification | [Verifying tokens](/authentication/tokens/) | ## Choose the right authentication method ```mermaid flowchart TD Start["Who is making the request?"] Start -->|"A real user
(browser or mobile)"| UserToken["Use user tokens
User signs in via email/password or OAuth"] Start -->|"A backend service"| ServiceQ["Does it need a specific user's
memory and persona?"] ServiceQ -->|Yes| ServiceUser["Use service account + user
Pass AUTH_SECRET + X-On-Behalf-Of"] ServiceQ -->|No| ServiceOnly["Use service account (stateless)
Pass AUTH_SECRET only"] UserToken --> UserLink["User tokens guide →"] ServiceUser --> ServiceLink["Service + user guide →"] ServiceOnly --> StatelessLink["Stateless guide →"] style Start fill:#fff7ed,stroke:#F68220,color:#181818 style UserToken fill:#f0f9ff,stroke:#3b82f6,color:#181818 style ServiceQ fill:#fff7ed,stroke:#F68220,color:#181818 style ServiceUser fill:#f0f9ff,stroke:#3b82f6,color:#181818 style ServiceOnly fill:#f0f9ff,stroke:#3b82f6,color:#181818 style UserLink fill:#ffffff,stroke:#d1d5db,color:#181818 style ServiceLink fill:#ffffff,stroke:#d1d5db,color:#181818 style StatelessLink fill:#ffffff,stroke:#d1d5db,color:#181818 ``` ## Authentication methods | Method | Authorization header | Features | Use case | |--------|---------------------|----------|----------| | [**User tokens**](/authentication/email-password/) | `Bearer ` | Memory, persona, threads, profile | Browser and mobile apps where a user signs in | | [**Service + user**](/authentication/service-accounts/#service-acting-as-a-user) | `Bearer ` + `X-On-Behalf-Of` | Memory, persona, threads (same as user token) | Backend acting on behalf of a specific user | | [**Service only**](/authentication/service-accounts/#service-only-stateless) | `Bearer ` | Threads only — no memory, no persona | Stateless backend tasks with no user context | ## Key concepts - **Bearer token** — A string passed in the `Authorization: Bearer ` header. For user tokens, this is a JWT signed by Firebase. For service accounts, this is the `AUTH_SECRET` value. - **User UUID** — TIE's canonical user identifier (`user.id`). Deterministic — the same Firebase user always produces the same UUID across all services. Backend services pass this in `X-On-Behalf-Of` to act on behalf of a user. - **AUTH_SECRET** — A shared secret issued by the TIE team for backend service authentication. Never expose it to browsers or mobile clients. ## What other pages assume All other docs pages assume you already have a Bearer token. When you see `Authorization: Bearer $TOKEN` in an example, it means either a user JWT or `AUTH_SECRET` depending on your integration type. Refer back to this page if you need to know how to get one. # Profile & usernames > Reading and updating the user profile, and managing usernames. Source: https://truememory-docs.mvstg.com/authentication/profile/ · Markdown: https://truememory-docs.mvstg.com/authentication/profile/index.md All requests go to the **Truememory Auth base URL** (see [Before you start](/authentication/overview/#before-you-start) for prerequisites: base URL, CORS whitelisting, OAuth client IDs). ## User Profile **Get current user:** ```bash GET https://your-auth-host/auth/me Authorization: Bearer ``` Returns the same `user` object from the login response. **Update profile:** ```bash PATCH https://your-auth-host/auth/me Authorization: Bearer Content-Type: application/json { "display_name": "Updated Name", "metadata": {"preferred_model": "anthropic/claude-sonnet-4-5"} } ``` Metadata is shallow-merged: set a key to `null` to delete it. Max 50 keys, 16 KB total. ## Usernames Users can claim a unique username after registration. Usernames are **globally unique** — the same username cannot be held by two different users, regardless of tenant. ### Rules | Rule | Value | |------|-------| | **Length** | 3-30 characters | | **Allowed characters** | Lowercase letters, numbers, hyphens (`-`), underscores (`_`) | | **Case** | Auto-lowercased (e.g. `JaneDoe` becomes `janedoe`) | | **Reserved words** | `admin`, `system`, `support`, `root`, `me`, `user`, `login`, `register`, `signup`, `logout`, `settings`, `help`, `info`, `api`, `null`, `undefined`, `moderator`, `administrator`, `username` | ### Check availability Check if a username is available before claiming. No authentication required. ```bash GET https://your-auth-host/auth/username/available?username=janedoe ``` **Response** (`200 OK`): ```json { "username": "janedoe", "available": true } ``` ### Claim a username Claim a username for the authenticated user. A username must be claimed before it can be changed. ```bash POST https://your-auth-host/auth/username/claim Authorization: Bearer Content-Type: application/json { "username": "janedoe" } ``` **Response** (`200 OK`): Returns the full user object with the `username` field set: ```json { "id": "4b822c30-1a07-40d6-8d59-321ef99037f6", "username": "janedoe", "email": "jane@example.com", "display_name": "Jane Doe", ... } ``` **Error cases:** | Code | Status | When | |------|--------|------| | `USER_USERNAME_TAKEN` | 409 | Another user already has this username | | `USER_USERNAME_ALREADY_CLAIMED` | 409 | You already have a username — use `PUT /auth/username` to change it | | `USER_USERNAME_INVALID` | 422 | Username fails validation (too short, invalid characters, reserved word) | ### Change username Change your username after claiming one. Subject to a **14-day cooldown** — you can only change your username once every 14 days. When you change your username, the old one is **reserved for 14 days** (grace period). During this window, no one else can take it, and you can revert to it. After 14 days, the old username becomes available to other users. ```bash PUT https://your-auth-host/auth/username Authorization: Bearer Content-Type: application/json { "username": "new_handle" } ``` **Response** (`200 OK`): Returns the full user object with the updated `username`. **Error cases:** | Code | Status | When | |------|--------|------| | `USER_USERNAME_TAKEN` | 409 | Another user already has this username, or it is reserved in a grace period | | `USER_USERNAME_COOLDOWN` | 400 | You changed your username within the last 14 days — wait for the cooldown to expire | | `USER_USERNAME_INVALID` | 409 | You haven't claimed a username yet — use `POST /auth/username/claim` first | ### Revert to previous username During the 14-day grace period after a username change, you can revert to your previous username: ```bash POST https://your-auth-host/auth/username/revert Authorization: Bearer ``` **Response** (`200 OK`): Returns the full user object with the reverted `username`. **Error cases:** | Code | Status | When | |------|--------|------| | `USER_USERNAME_NO_REVERTIBLE` | 404 | No previous username to revert to (grace period may have expired) | :::tip[Grace period behavior] - The old username is reserved for **14 days** after a change — no one else can claim it - You can revert at any time during those 14 days - After reverting, the cooldown is cleared and you can change again - The availability check (`GET /auth/username/available`) accounts for grace-period reservations ::: ### Reading the username The `username` field is included in all user responses: `GET /auth/me`, login, registration, and OAuth sign-in responses. It is `null` until claimed. # Service accounts Source: https://truememory-docs.mvstg.com/authentication/service-accounts/ · Markdown: https://truememory-docs.mvstg.com/authentication/service-accounts/index.md Service account authentication is for backend services that call TIE programmatically — webhooks, cron jobs, internal microservices, or any server-side code. Instead of going through the user sign-in flow, your service authenticates with a shared secret called `AUTH_SECRET`. :::tip[Building a browser or mobile app?] Use [User tokens](/authentication/email-password/) instead. `AUTH_SECRET` is a backend-only credential and must never be exposed to clients. ::: ## Before You Start | What | Where to get it | |------|----------------| | **AUTH_SECRET** | Issued by the TIE team. Contact them to request one for your service. | | **TIE AI Gateway URL** | The base URL for TIE's API (e.g. `https://your-tie-host`). | | **User UUID** (if acting on behalf of users) | The `user.id` from [TIE Auth responses](/authentication/service-accounts/). Store this in your database when users sign up. | ## Authentication Modes There are three ways to use service auth, depending on whether your service needs a user context and where your user IDs come from. | Mode | Headers | Memory & Persona | Use case | |------|---------|------------------|----------| | **Service + TIE user** | `Authorization: Bearer ` + `X-On-Behalf-Of: ` | Yes | Backend acting as a TIE-registered user | | **Service + external user** | `Authorization: Bearer ` + `X-External-User-Id: ` | Yes | 3rd-party app with its own user IDs — no TIE sign-up needed | | **Service only** | `Authorization: Bearer ` | No | Stateless one-shot calls | ## Service acting as a user Your service calls TIE on behalf of a specific user. TIE loads that user's persona and memory, and records the conversation — the same as if the user called directly. The service does not need to go through Firebase auth; it just needs to know the user's TIE UUID. Pass `AUTH_SECRET` as the bearer token and the user's UUID in the `X-On-Behalf-Of` header: ```bash curl -X POST https://your-tie-host/v1/chat/completions \ -H "Authorization: Bearer $AUTH_SECRET" \ -H "X-On-Behalf-Of: 550e8400-e29b-41d4-a716-446655440000" \ -H "X-App-Id: whatsapp-webhook" \ -H "Content-Type: application/json" \ -d '{"model": "claude-haiku-4-5", "messages": [{"role": "user", "content": "What did we discuss yesterday?"}]}' ``` | Header | Required | Description | |--------|----------|-------------| | `Authorization` | Yes | `Bearer ` | | `X-On-Behalf-Of` | Yes | The user's UUID (from `user.id` in [auth responses](/authentication/service-accounts/)) | | `X-App-Id` | Recommended | Identifies the calling app and [scopes threads](/ai-gateway/chat-completions/#per-app-thread-isolation) | ### Where to get the user UUID When a user signs up through [TIE Auth](/authentication/email-password/), the response includes `user.id` — a deterministic UUID derived from their Firebase UID. Store this in your database alongside the user record. When your backend needs to call TIE on that user's behalf, pass it in `X-On-Behalf-Of`. ```mermaid sequenceDiagram accTitle: Service-as-user authentication flow accDescr: Shows how a backend service calls TIE on behalf of a user using AUTH_SECRET participant User participant App as Your App participant Backend as Your Backend participant TIE as TIE AI Gateway User->>App: Sends message (e.g. WhatsApp) App->>Backend: Forward message + user identifier Backend->>Backend: Look up user's TIE UUID from database Backend->>TIE: POST /v1/chat/completions
Authorization: Bearer AUTH_SECRET
X-On-Behalf-Of: user-uuid TIE->>TIE: Load user's memory & persona TIE-->>Backend: AI response Backend-->>App: Forward response to user ``` ## Acting for users with external IDs If your users never sign in to TIE, you don't need to create TIE accounts for them. Pass any stable string identifier from your own system in the `X-External-User-Id` header — TIE deterministically derives a user UUID from it (`UUID5` of `ext:`), so the same ID always maps to the same memory graph. No registration call, no ID mapping table. ```bash # Write a memory for your user "crm-user-8841" curl -X POST https://your-tie-host/memories/graph \ -H "Authorization: Bearer $AUTH_SECRET" \ -H "X-External-User-Id: crm-user-8841" \ -H "Content-Type: application/json" \ -d '{"data": "User prefers morning meetings and is based in Kuala Lumpur.", "type": "text", "agent_id": "chatbot"}' # Retrieve memory context for the same user curl -X POST https://your-tie-host/memories/retrieve \ -H "Authorization: Bearer $AUTH_SECRET" \ -H "X-External-User-Id: crm-user-8841" \ -H "Content-Type: application/json" \ -d '{"query": "when should I schedule the call?", "agent_id": "chatbot"}' ``` **Rules for `X-External-User-Id` values:** - Non-empty, max 256 characters, no control characters — otherwise `400 AUTH_INVALID_USER_ID`. - **Case-sensitive**: `Alice` and `alice` derive different users. Pick one canonical form and always send it. - Cannot be combined with `X-On-Behalf-Of` — sending both returns `400 AUTH_SUBJECT_CONFLICT`. - Requires service auth — sending it with a user token returns `400 AUTH_SUBJECT_CONFLICT`. :::caution[One ID, one memory graph — across all apps] The derived user is global: two apps sending the same external ID read and write the **same** memory graph. That's intentional when the ID refers to the same human (one person, one memory). If your IDs are app-local (e.g. auto-increment integers like `42`), namespace them yourself — `myapp:42` — to avoid colliding with another app's user `42`. ::: ## Service only (stateless) Your service makes a call with no user context. No memory, no persona, no turn recording. This is useful for utility tasks like title generation, text summarization, or content classification where there is no meaningful user to attach to. ```bash curl -X POST https://your-tie-host/v1/chat/completions \ -H "Authorization: Bearer $AUTH_SECRET" \ -H "X-App-Id: title-generator" \ -H "Content-Type: application/json" \ -d '{"model": "gemini-2.5-flash", "messages": [{"role": "user", "content": "Generate a title for: ..."}]}' ``` **Restrictions in service-only mode:** - `persona_id` is not allowed (returns `400`) - Memory tools (`memory_search`, `memory_write`) are disabled - No conversation turns are recorded - Threads still work for multi-turn within the same request chain, but have no user association ## Security :::danger[AUTH_SECRET is a backend secret] Never expose `AUTH_SECRET` to browsers or mobile clients. It grants access to any user's context via `X-On-Behalf-Of` or `X-External-User-Id` — anyone with the secret can impersonate any user. ::: - Store `AUTH_SECRET` in environment variables or a secret manager (e.g. GCP Secret Manager), not in code. - Rotate the secret by coordinating with the TIE team. During rotation, both old and new values can be active briefly. - If you suspect the secret has been compromised, contact the TIE team immediately to revoke and reissue it. ## Error Codes | Code | Status | When | |------|--------|------| | `AUTH_TOKEN_MISSING` | 401 | No `Authorization` header provided | | `AUTH_TOKEN_INVALID` | 401 | `AUTH_SECRET` value does not match | | `AUTH_INVALID_USER_ID` | 400 | `X-On-Behalf-Of` is not a valid UUID, or `X-External-User-Id` is empty, longer than 256 characters, or contains control characters | | `AUTH_SUBJECT_CONFLICT` | 400 | Both `X-On-Behalf-Of` and `X-External-User-Id` sent, or `X-External-User-Id` sent with a user token | | `VALIDATION_ERROR` | 400 | `persona_id` used in service-only mode (no subject user) | # Suggested implementation > Where to store refresh tokens, where to use ID tokens, and how to refresh safely — recommended patterns for web and mobile clients. Source: https://truememory-docs.mvstg.com/authentication/token-handling/ · Markdown: https://truememory-docs.mvstg.com/authentication/token-handling/index.md [Tokens & refresh](/authentication/tokens/) describes the mechanics: what the tokens are and which endpoints issue them. This page is a recommendation for how to wire them into a real client — specifically **where each token should live**. This is guidance, not enforcement. Truememory Auth accepts a valid refresh token from anywhere; nothing here is checked server-side. The recommendations exist because the two tokens have very different blast radii. ## The two tokens are not equivalent | Token | Lifetime | What it can do | Where it should live | |-------|----------|----------------|---------------------| | `id_token` | 1 hour | Authenticate API calls as the user | **In memory** on the client. Never persisted. | | `refresh_token` | Until revoked — effectively indefinite | Mint unlimited fresh `id_token`s | **Server-side** (web) or **platform keystore** (mobile). Never in `localStorage` if you have any alternative. | An ID token that leaks is bad for at most an hour. A refresh token that leaks is a **persistent account takeover** — the attacker can keep minting ID tokens indefinitely, and Truememory Auth currently exposes no way for your app to revoke it (see [Sign-out and revocation](#sign-out-and-revocation)). That asymmetry drives every recommendation below: treat the ID token as a disposable credential and the refresh token as a long-lived secret. :::caution[Truememory Auth returns the refresh token in the response body] `/auth/register`, `/auth/login`, `/auth/oauth/token`, and `/auth/refresh` all return `refresh_token` as JSON. Truememory Auth sets no cookies and has no httpOnly-cookie mode. If you want the refresh token out of JavaScript's reach, **your backend has to put it there** — that's what the recommended pattern below does. ::: ## Recommended: keep the refresh token in your backend For any web app that has a server — Next.js, Nuxt, Remix, or an SPA with its own API — proxy the auth calls through your backend and never let the refresh token reach the browser. ```mermaid flowchart LR Browser["Browser
id_token in memory"] Backend["Your backend
(BFF)"] Auth["Truememory Auth"] Gateway["Truememory
AI Gateway"] Browser -->|"POST /api/auth/login
credentials"| Backend Backend -->|"POST /auth/login"| Auth Auth -->|"id_token + refresh_token"| Backend Backend -->|"id_token in body
refresh_token in httpOnly cookie"| Browser Browser -->|"Authorization: Bearer id_token"| Gateway style Browser fill:#f0f9ff,stroke:#3b82f6,color:#181818 style Backend fill:#fff7ed,stroke:#F68220,color:#181818 style Auth fill:#ffffff,stroke:#d1d5db,color:#181818 style Gateway fill:#ffffff,stroke:#d1d5db,color:#181818 ``` The browser holds a short-lived ID token in memory and an opaque httpOnly cookie it cannot read. XSS can still steal the in-memory ID token — but it expires in an hour and cannot be used to mint more. ### Sign-in route Keep the auth base URL in a **server-only** variable. A `NEXT_PUBLIC_`-prefixed value is inlined into the client bundle, which defeats the purpose of proxying. ```typescript // app/api/auth/login/route.ts import { cookies } from "next/headers"; import { NextResponse } from "next/server"; // Server-only — no NEXT_PUBLIC_ prefix. const AUTH_BASE_URL = process.env.TRUEMEMORY_AUTH_BASE_URL!; const REFRESH_COOKIE = "tm_refresh"; const cookieOptions = { httpOnly: true, secure: true, sameSite: "lax", // Scope the cookie to the auth routes so it isn't attached to every request. path: "/api/auth", maxAge: 60 * 60 * 24 * 30, } as const; export async function POST(request: Request) { const { email, password } = await request.json(); const res = await fetch(`${AUTH_BASE_URL}/auth/login`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ email, password }), }); if (!res.ok) { // Don't forward Truememory's error body verbatim — it can distinguish // "no such account" from "wrong password", which enables user enumeration. return NextResponse.json({ error: "Invalid credentials" }, { status: 401 }); } const { id_token, refresh_token, expires_in, user } = await res.json(); const jar = await cookies(); jar.set(REFRESH_COOKIE, refresh_token, cookieOptions); // The ID token goes back in the body. The browser keeps it in memory only. return NextResponse.json({ id_token, expires_in, user }); } ``` ### Refresh route The browser calls this with no body — the cookie *is* the credential. ```typescript // app/api/auth/refresh/route.ts import { cookies } from "next/headers"; import { NextResponse } from "next/server"; const AUTH_BASE_URL = process.env.TRUEMEMORY_AUTH_BASE_URL!; export async function POST() { const jar = await cookies(); const refreshToken = jar.get("tm_refresh")?.value; if (!refreshToken) { return NextResponse.json({ error: "Not authenticated" }, { status: 401 }); } const res = await fetch(`${AUTH_BASE_URL}/auth/refresh`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ refresh_token: refreshToken }), }); if (!res.ok) { jar.delete({ name: "tm_refresh", path: "/api/auth" }); return NextResponse.json({ error: "Session expired" }, { status: 401 }); } const { id_token, refresh_token, expires_in } = await res.json(); // Truememory returns the same refresh token value today rather than rotating // it. Re-setting the cookie is still correct: it extends the cookie's own // expiry and keeps working unchanged if rotation is introduced later. jar.set("tm_refresh", refresh_token, { httpOnly: true, secure: true, sameSite: "lax", path: "/api/auth", maxAge: 60 * 60 * 24 * 30, }); return NextResponse.json({ id_token, expires_in }); } ``` ### OAuth route OAuth sign-in follows the same shape — the browser gets a token from Google or Apple, your route exchanges it. This is the endpoint the examples in [Sign in with Google & Apple](/authentication/oauth/) post to. ```typescript // app/api/auth/oauth/route.ts import { cookies } from "next/headers"; import { NextResponse } from "next/server"; const AUTH_BASE_URL = process.env.TRUEMEMORY_AUTH_BASE_URL!; export async function POST(request: Request) { const { provider, id_token: providerToken } = await request.json(); if (provider !== "google" && provider !== "apple") { return NextResponse.json({ error: "Unsupported provider" }, { status: 400 }); } const res = await fetch(`${AUTH_BASE_URL}/auth/oauth/token`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ provider, id_token: providerToken }), }); if (!res.ok) { return NextResponse.json({ error: "Sign-in failed" }, { status: 401 }); } const { id_token, refresh_token, expires_in, user } = await res.json(); const jar = await cookies(); jar.set("tm_refresh", refresh_token, { httpOnly: true, secure: true, sameSite: "lax", path: "/api/auth", maxAge: 60 * 60 * 24 * 30, }); return NextResponse.json({ id_token, expires_in, user }); } ``` ### Sign-out route ```typescript // app/api/auth/logout/route.ts import { cookies } from "next/headers"; import { NextResponse } from "next/server"; export async function POST() { const jar = await cookies(); jar.delete({ name: "tm_refresh", path: "/api/auth" }); return NextResponse.json({ ok: true }); } ``` ### Client-side token store Hold the ID token in a module-level variable — not `localStorage`, not `sessionStorage`, not a non-httpOnly cookie. It is lost on reload, which is correct: the refresh route restores the session from the cookie. Two details matter here. **Refresh early**, so a request never leaves with a token that expires mid-flight. And **de-duplicate concurrent refreshes** — without the `inFlight` guard, ten parallel requests hitting an expired token fire ten refresh calls. ```typescript let idToken: string | null = null; let expiresAt = 0; let inFlight: Promise | null = null; const REFRESH_MARGIN_MS = 5 * 60 * 1000; async function refreshIdToken(): Promise { const res = await fetch("/api/auth/refresh", { method: "POST" }); if (!res.ok) { idToken = null; expiresAt = 0; throw new Error("Session expired"); } const data = await res.json(); idToken = data.id_token; expiresAt = Date.now() + Number(data.expires_in) * 1000; return data.id_token; } /** Called after sign-in, with the ID token your auth routes returned. */ export function setIdToken(token: string, expiresInSeconds: number) { idToken = token; expiresAt = Date.now() + expiresInSeconds * 1000; } export function clearIdToken() { idToken = null; expiresAt = 0; } export async function getIdToken(): Promise { if (idToken && Date.now() < expiresAt - REFRESH_MARGIN_MS) { return idToken; } // Single-flight: concurrent callers await the same refresh. inFlight ??= refreshIdToken().finally(() => { inFlight = null; }); return inFlight; } export async function fetchWithAuth(url: string, options: RequestInit = {}) { let token = await getIdToken(); let response = await fetch(url, { ...options, headers: { ...options.headers, Authorization: `Bearer ${token}` }, }); if (response.status === 401) { // Token rejected despite looking fresh (e.g. revoked). Refresh once, retry // once — never loop, or a genuinely dead session becomes an infinite retry. idToken = null; token = await getIdToken(); response = await fetch(url, { ...options, headers: { ...options.headers, Authorization: `Bearer ${token}` }, }); } return response; } ``` ### Should you proxy the AI Gateway too? No — call the gateway directly from the browser with the in-memory ID token. Proxying Server-Sent Events through your backend adds latency and a second streaming hop to debug for no security gain: the ID token is the only credential involved either way. This does mean your browser origin must be CORS-whitelisted on the **gateway** host (see [Before you start](/authentication/overview/#before-you-start)). With the proxy pattern, the browser never calls the auth host directly, so only the gateway origin matters. ## Native mobile: use the platform keystore Mobile apps don't need a backend proxy. iOS and Android both ship a hardware-backed secret store, and that's the right home for the refresh token. | Platform | Store the refresh token in | Never in | |----------|---------------------------|----------| | iOS / macOS | Keychain (`kSecClassGenericPassword`) | `UserDefaults`, plist files, `@AppStorage` | | Android | `EncryptedSharedPreferences`, or DataStore encrypted with an Android Keystore key | `SharedPreferences`, app files, `strings.xml` | The ID token stays in memory on mobile too — an in-process property, not a persisted preference. ### iOS ```swift import Security enum RefreshTokenStore { private static let account = "truememory.refresh_token" private static var baseQuery: [String: Any] { [ kSecClass as String: kSecClassGenericPassword, kSecAttrAccount as String: account, ] } static func save(_ token: String) { SecItemDelete(baseQuery as CFDictionary) var query = baseQuery query[kSecValueData as String] = Data(token.utf8) // Readable after first unlock, never synced to iCloud or backups. query[kSecAttrAccessible as String] = kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly SecItemAdd(query as CFDictionary, nil) } static func load() -> String? { var query = baseQuery query[kSecReturnData as String] = true query[kSecMatchLimit as String] = kSecMatchLimitOne var item: CFTypeRef? guard SecItemCopyMatching(query as CFDictionary, &item) == errSecSuccess, let data = item as? Data else { return nil } return String(decoding: data, as: UTF8.self) } static func clear() { SecItemDelete(baseQuery as CFDictionary) } } ``` `kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly` is the important part: the `ThisDeviceOnly` suffix keeps the token out of iCloud Keychain and encrypted backups, so a restore onto a different device doesn't carry the session with it. For high-value apps, add `kSecAttrAccessControl` with `.biometryCurrentSet` to gate reads behind Face ID. ### Android ```kotlin import androidx.security.crypto.EncryptedSharedPreferences import androidx.security.crypto.MasterKey private val masterKey = MasterKey.Builder(context) .setKeyScheme(MasterKey.KeyScheme.AES256_GCM) .build() private val prefs = EncryptedSharedPreferences.create( context, "truememory_auth", masterKey, EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV, EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM, ) fun saveRefreshToken(token: String) = prefs.edit().putString("refresh_token", token).apply() fun loadRefreshToken(): String? = prefs.getString("refresh_token", null) fun clearRefreshToken() = prefs.edit().remove("refresh_token").apply() ``` :::note[Check your Jetpack Security version] `EncryptedSharedPreferences` ships in `androidx.security:security-crypto`, whose maintenance status has shifted across releases. If your toolchain flags it as deprecated, the replacement is the same idea assembled by hand: a key from Android Keystore (`AndroidKeyStore` provider, AES/GCM) used to encrypt values before writing them to DataStore. Don't respond to the deprecation warning by falling back to plain `SharedPreferences`. ::: ## Accepted fallback: browser-only, no backend Sometimes there's no server — a static SPA, a prototype, an internal tool. Then the refresh token has to live in the browser, and `localStorage` is the usual choice. This is a real trade-off, not a non-issue: - **Any XSS is a full account takeover.** One injected script — yours or a compromised dependency's — reads `localStorage` and exfiltrates a credential that works indefinitely. - **You cannot revoke it.** There's no logout endpoint, so a stolen refresh token stays valid until the user's password is reset. If you accept that, reduce the exposure: - Prefer `sessionStorage` over `localStorage` when a per-tab session is acceptable — it dies with the tab and isn't shared across them. - Keep the ID token in memory even here. There's no reason to persist a token you can always re-mint. - Ship a strict `Content-Security-Policy`. It's the control that actually addresses the threat; storage choice barely moves the needle by comparison. - Audit your dependency tree — every script on the page can read the token. - Never interpolate the token into a URL, query string, or `` src. It lands in server logs, browser history, and `Referer` headers. If the app will ever hold anything worth stealing, add the backend proxy. It is roughly the three route handlers shown above. ## Sign-out and revocation Truememory Auth has **no logout or token-revocation endpoint** today. Signing out is entirely a client-side operation: 1. Delete the refresh token — clear the httpOnly cookie, or the Keychain / `EncryptedSharedPreferences` entry. 2. Drop the in-memory ID token. 3. Clear any cached user state in your app. Already-issued ID tokens remain valid until they expire (up to an hour). Google Identity Platform invalidates refresh tokens on password change or reset, account disable, and account deletion. :::danger[Treat a leaked refresh token as an incident] Because your app can't revoke one, the practical remediation is to force the user through [password reset](/authentication/email-password/), which invalidates existing refresh tokens at the identity-provider level. Plan for this before you need it. ::: ## Quick checklist - [ ] Refresh token never reaches browser JavaScript (or the trade-off is a documented decision). - [ ] ID token is in memory only — not `localStorage`, `sessionStorage`, or a readable cookie. - [ ] Auth base URL is a server-only env var in the proxy pattern (no `NEXT_PUBLIC_`). - [ ] Cookie flags: `httpOnly`, `secure`, `sameSite`, narrow `path`. - [ ] Refresh is single-flight and happens before expiry, not only on `401`. - [ ] `401` triggers at most one retry — no unbounded loop. - [ ] Tokens never appear in URLs, logs, analytics events, or error reports. - [ ] Sign-out clears the refresh token from its store, not just the ID token. - [ ] `AUTH_SECRET` appears in no client bundle — it is [backend-only](/authentication/service-accounts/). # Tokens & refresh > Using tokens on requests, refreshing them, and verifying them in your own backend. Source: https://truememory-docs.mvstg.com/authentication/tokens/ · Markdown: https://truememory-docs.mvstg.com/authentication/tokens/index.md All requests go to the **Truememory Auth base URL** (see [Before you start](/authentication/overview/#before-you-start) for prerequisites: base URL, CORS whitelisting, OAuth client IDs). ## Using Tokens Include the `id_token` as a Bearer token in all requests to Truememory services. Note that the AI Gateway (`your-tie-host`) is a separate service from Truememory Auth (`your-auth-host`): ```bash curl -X POST https://your-tie-host/v1/chat/completions \ -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIs..." \ -H "Content-Type: application/json" \ -d '{"model": "anthropic/claude-sonnet-4-5", "messages": [{"role": "user", "content": "Hello"}]}' ``` The token is a JWT signed by Firebase. Truememory verifies the signature, checks expiry, and extracts the user identity. ## Token Refresh ID tokens expire after **1 hour**. Use the refresh token to get a new one without re-authenticating: ```bash POST https://your-auth-host/auth/refresh Content-Type: application/json { "refresh_token": "AMf-xB..." } ``` **Response:** ```json { "id_token": "eyJhbGciOiJSUzI1NiIs...", "refresh_token": "AMf-xB...", "expires_in": "3600" } ``` The `refresh_token` in the response is the **same value you sent** — Truememory does not rotate refresh tokens. Re-reading it from the response is still the right habit, so your client keeps working unchanged if rotation is introduced later. :::caution[The refresh token is a long-lived credential] It mints ID tokens until it is revoked, and Truememory Auth exposes no revocation endpoint. Where you store it decides whether an XSS bug costs you an hour or the whole account — see [Suggested implementation](/authentication/token-handling/) for the recommended split (refresh token server-side, ID token in memory). ::: Implement automatic refresh in your API client. Refresh **before** expiry rather than only on `401`, and de-duplicate concurrent refreshes so N parallel requests don't fire N refresh calls: ```typescript let idToken: string | null = null; let expiresAt = 0; let inFlight: Promise | null = null; async function refreshIdToken(): Promise { // Posts to your own backend, which holds the refresh token in an httpOnly // cookie. See "Suggested implementation" for the route handler. const res = await fetch("/api/auth/refresh", { method: "POST" }); if (!res.ok) { idToken = null; throw new Error("Session expired"); } const data = await res.json(); idToken = data.id_token; expiresAt = Date.now() + Number(data.expires_in) * 1000; return data.id_token; } async function getIdToken(): Promise { // Refresh 5 minutes early so a request never expires mid-flight. if (idToken && Date.now() < expiresAt - 5 * 60 * 1000) return idToken; inFlight ??= refreshIdToken().finally(() => { inFlight = null; }); return inFlight; } async function fetchWithAuth(url: string, options: RequestInit = {}) { let token = await getIdToken(); let response = await fetch(url, { ...options, headers: { ...options.headers, Authorization: `Bearer ${token}` }, }); if (response.status === 401) { // Refresh once, retry once — never loop. idToken = null; token = await getIdToken(); response = await fetch(url, { ...options, headers: { ...options.headers, Authorization: `Bearer ${token}` }, }); } return response; } ``` ## Verifying Tokens in Your Backend If your app has its own backend that needs to verify Truememory tokens (e.g. protecting your own API routes), this section explains how. Truememory Auth issues **Firebase ID tokens** — JWTs signed by Firebase's service account keys with the GCP project ID as the audience. These are **not** standard Google OAuth2 tokens. ### Token structure ```json { "iss": "https://securetoken.google.com/", "aud": "", "sub": "5iexdslxx...", "tie_user_id": "4b822c30-...", "email": "user@example.com", "email_verified": false, "firebase": { "sign_in_provider": "password" } } ``` Key claims: - `aud` — Always the GCP project ID (e.g. `mv-stg-tie-core`). This is Firebase's design, not configurable. - `sub` — Firebase/GIP UID. - `tie_user_id` — Truememory's canonical user UUID (custom claim). Use this when calling Truememory APIs. Set automatically on register, login, and OAuth sign-in. ### Verification approaches There are two valid approaches depending on your security requirements: | Approach | Library | Signature verified | When to use | |----------|---------|-------------------|-------------| | **Full verification** | `firebase-admin` | Yes | Public-facing APIs, production backends | | **Trusted decode** | Manual JWT decode | No | Internal services that trust Truememory over HTTPS | :::note[`google-auth-library` does not work for Truememory tokens] `google-auth-library`'s `OAuth2Client.verifyIdToken()` validates against Google Sign-In tokens (`accounts.google.com` issuer, OAuth Client ID audience). Truememory issues Firebase ID tokens which use a different issuer (`securetoken.google.com/`), audience (GCP project ID), and signing keys. Use `firebase-admin` or manual JWT decode instead. ::: # Cache token counts in /v1/chat/completions usage > The usage object now returns cache_read_tokens and cache_creation_tokens so callers can compute accurate billing when prompt caching is active. Source: https://truememory-docs.mvstg.com/changelog/tie-ai-gateway/2026-04-17-cache-token-passthrough/ · Markdown: https://truememory-docs.mvstg.com/changelog/tie-ai-gateway/2026-04-17-cache-token-passthrough/index.md The `usage` object on `/v1/chat/completions` now includes two extra fields: ```json { "usage": { "prompt_tokens": 4532, "completion_tokens": 187, "total_tokens": 4719, "cache_read_tokens": 4200, "cache_creation_tokens": 0 } } ``` | Field | What it tells you | |-------|-------------------| | `cache_read_tokens` | How many of your `prompt_tokens` were reused from the provider's prompt cache. On Anthropic these are roughly **10× cheaper** than regular input tokens. | | `cache_creation_tokens` | How many tokens the provider wrote into its cache for next time. Slightly pricier than a normal input token, but pays for itself on the next hit. | ## Why this matters Without these fields, you had no way to tell a 10,000-token cached conversation apart from a 10,000-token fresh conversation — so the bill you computed was always the worst-case number. That could overstate input costs by up to **67%** on long, repeat conversations (Anthropic's standard input is **$3.00/MTok** vs cache reads at **$0.30/MTok**). Now you can compute what you're actually being charged: ``` fresh_input = prompt_tokens - cache_read_tokens - cache_creation_tokens real_cost = (fresh_input × standard_rate) + (cache_read_tokens × cache_read_rate) + (cache_creation_tokens × cache_write_rate) + (completion_tokens × output_rate) ``` ## Supported providers | Provider | `cache_read_tokens` | `cache_creation_tokens` | |----------|---------------------|-------------------------| | Anthropic (Claude) | Yes | Yes | | OpenAI (GPT) | Yes | Always `0` — OpenAI doesn't report cache writes separately | | Vertex AI, Gemini, Groq, Mistral, etc. | Always `0` | Always `0` | If a provider doesn't support prompt caching, both fields are `0`. Nothing breaks — you just don't see savings where there are none to see. ## Backward compatibility - Existing callers that ignore the new fields are unaffected — they're purely additive. - Both fields default to `0`, so old clients that deserialize into strict schemas continue to work as long as they allow unknown or zero-default fields. - Streaming responses (`stream_options.include_usage: true`) include the new fields in the final usage chunk. See the full field reference at [Understanding the `usage` object](/ai-gateway/chat-completions/#understanding-the-usage-object). # Personas: archetype field for unified agent profile > Personas now carry a structured archetype field so any app can read and render the agent's personality type without parsing the compiled system prompt. Source: https://truememory-docs.mvstg.com/changelog/tie-ai-gateway/2026-04-23-persona-archetype-field/ · Markdown: https://truememory-docs.mvstg.com/changelog/tie-ai-gateway/2026-04-23-persona-archetype-field/index.md Added `archetype` as a structured field on the persona object. Apps can now store and retrieve the agent's personality type (e.g. `SAGE`, `EXPLORER`) independently of the compiled `system_prompt`. **What changed:** - `POST /personas` and `PUT /personas/{id}` accept an optional `archetype` string (max 100 chars) - All persona responses now include `archetype` (nullable) - `GET /personas/active` returns `archetype` so apps can pattern-match to their own avatar assets without parsing prose **Why:** TIE is the single source of truth for a user's agent across all apps. `archetype` is the canonical identifier that lets any app read and render the agent correctly after onboarding in a different app. Avatar images are intentionally not stored in TIE — each app renders its own visual for the archetype. **Migration:** No action required. Existing personas return `archetype: null` until updated. # Merge two entity memories into one > New POST /memories/{agent_id}/merge endpoint collapses duplicate entity nodes (e.g. "Fauzaan" and "Mohamed Fauzaan") into a single node, repointing every live relationship at the primary. Source: https://truememory-docs.mvstg.com/changelog/tie-ai-gateway/2026-05-04-merge-memories/ · Markdown: https://truememory-docs.mvstg.com/changelog/tie-ai-gateway/2026-05-04-merge-memories/index.md When the graph ends up with two entity nodes that represent the same real-world thing — `Fauzaan` and `Mohamed Fauzaan`, or `Acme` and `Acme Inc.` — you can now collapse them in one call: ```bash POST /memories/{agent_id}/merge { "primary_id": "node-uuid-keep", "duplicate_id": "node-uuid-fold-in" } ``` ## What it does - Every live edge incident to `duplicate_id` is repointed at `primary_id`. The original edge is soft-deleted so the timeline is preserved for undo. - The duplicate node is marked with `merged_into` and disappears from List Memories, Search, and the Memory Graph response. - An `IS_DUPLICATE_OF` audit edge is written from duplicate → primary for traceability. - The primary's `summary`, `attributes`, and `labels` are rewritten by the LLM to combine both records into one canonical view. Its `name` is kept as-is, so pick the more specific or canonical node as `primary_id`. - LLM enrichment is best-effort — on a model outage the merge falls back to a blind rewire and the response sets `enriched: false`. ## Caveats - **Edge fact text is not rewritten.** If an old edge said "Fauzaan likes tea", that text persists; only the endpoint pointer changes. - **Duplicate facts are not deduped.** If both nodes had similar relationships, you will see two live edges. The next memory-ingestion pass usually collapses them. - **Same-scope only.** Both nodes must share the same user and `agent_id`. ## Why this matters Before this endpoint, the only way to recover from a misextracted entity was to delete every memory tied to it and let the graph rebuild over time — costly and lossy. Merge gives you a non-destructive fix that preserves history and stays reversible. See [Merge Two Memories](/memory/building-uis/#merge-two-memories) for the full reference. # Personas: soul_document, human_profile, and avatar fields > Personas now carry the agent's structured personality, its understanding of the user, and an avatar URL — making TIE the single source of truth for all agent state across apps. Source: https://truememory-docs.mvstg.com/changelog/tie-ai-gateway/2026-05-07-persona-soul-and-human-profile/ · Markdown: https://truememory-docs.mvstg.com/changelog/tie-ai-gateway/2026-05-07-persona-soul-and-human-profile/index.md Added `soul_document`, `human_profile`, and `avatar` as structured fields on the persona object. Apps no longer need to maintain local copies of personality data, user relationship context, or avatar URLs. **What changed:** - `POST /personas` and `PUT /personas/{id}` accept all three new fields (optional) - All persona responses now include `soul_document` (nullable JSON), `human_profile` (nullable text), and `avatar` (nullable text) - `GET /personas/active` returns all three fields so apps can reconstruct the full agent state without a local database **Field semantics:** - **`soul_document`** — The agent's structured personality document. JSON with keys `voiceAndStyle`, `antiPatterns`, `learnedAdaptations`, `boundaries`, `customInstructions`. Each is an array of `{ "text": string, "source": string }` entries. This is persona-level state (same for all sessions) — apps write this when the user edits their AI's personality. - **`human_profile`** — Free-form text the AI writes about the specific user it talks to: goals, communication preferences, values, context. Since each user has exactly one non-preset persona, this is effectively user-scoped. Apps write this when the AI learns something meaningful about the user during conversation. - **`avatar`** — URL of the persona's avatar image. Shared across all apps using this persona so every app shows the same avatar without maintaining a local copy. **Why:** TIE is the single source of truth for a user's AI agent across all platform apps. Previously, personality data, user profiles, and avatar URLs lived in each app's local database, causing drift between apps. Now any app that shares a persona sees the same state without a sync step. **Migration:** No action required. Existing personas return `null` for all three fields until updated. Apps can migrate by pushing their local values to TIE on next sync. # Add Z.ai (GLM) models > Route requests to Zhipu AI GLM models, including GLM-4.6, GLM-4.7, and GLM-5.2, through the OpenAI-compatible chat completions endpoint. Source: https://truememory-docs.mvstg.com/changelog/tie-ai-gateway/2026-06-24-zai-glm-models/ · Markdown: https://truememory-docs.mvstg.com/changelog/tie-ai-gateway/2026-06-24-zai-glm-models/index.md TIE now routes requests to Zhipu AI (Z.ai) GLM models. Select a model by passing a `zai/`-prefixed name to `/v1/chat/completions`, the same way you select any other provider. ```json { "model": "zai/glm-4.6", "messages": [{ "role": "user", "content": "Hello" }] } ``` ## Available models | Model | Notes | |-------|-------| | `zai/glm-5.2` | Latest flagship | | `zai/glm-5.1` | Previous flagship | | `zai/glm-5-turbo` | Faster, lower-cost tier | | `zai/glm-5` | General-purpose | | `zai/glm-4.7` | Strong agentic coding and reasoning | | `zai/glm-4.7-flash` | Lightweight, low-latency | | `zai/glm-4.6` | 200K context window | | `zai/glm-4.5` | Prior generation | GLM models support tool calling and a thinking reasoning mode. They run through Z.ai's OpenAI-compatible API, so existing request and response handling needs no changes. ## Configuration Set `ZAI_API_KEY` to enable the provider. When Z.ai is the only configured provider, `glm-4.6` becomes the default model. Override the default with `DEFAULT_MODEL=zai/glm-5.2`. Query `GET /info` to confirm which models are active on your instance. See the [chat completions reference](/ai-gateway/chat-completions/#supported-providers) for the full provider list. # Skills: assign one skill to multiple apps > Skills now carry an app_ids array, so a single skill can be scoped to several apps at once or shared with every app. Source: https://truememory-docs.mvstg.com/changelog/tie-ai-gateway/2026-06-26-skills-multi-app-scoping/ · Markdown: https://truememory-docs.mvstg.com/changelog/tie-ai-gateway/2026-06-26-skills-multi-app-scoping/index.md Skills now carry an `app_ids` array instead of a single `app_id`. One skill can serve several apps at once, or every app on the platform. Set `app_ids` to the list of apps that should see the skill. Leave it `null` (or omit it) to share the skill with every app. ```json { "key": "summarize-thread", "name": "Summarize Thread", "prompt": "Summarize the conversation so far in 3 short bullet points.", "app_ids": ["vibrantly", "blinklife"] } ``` A `null` `app_ids` marks the skill as global — visible to all apps: ```json { "key": "translate", "name": "Translate", "prompt": "Translate to formal English.", "app_ids": null } ``` ## Why this matters Before this change, a shared skill had to be duplicated once per app, and each copy drifted on its own. A single record assigned to many apps keeps one source of truth and removes the duplicates. ## Backward compatibility The legacy scalar `app_id` still works on create and update. TIE folds it into a one-element `app_ids` list, so existing single-app callers need no changes. Responses now return `app_ids`. The `app_id` query filter on `GET /skills` is unchanged. It still names the requesting app and returns skills assigned to that app plus global skills. See the [app scoping section of the Skills guide](/ai-gateway/skills/#app-scoping) for the full contract. # Graph namespacing — blend shared knowledge graphs into memory search > New graph_ids field on POST /memories/retrieve and POST /memories/search blends named, user-independent knowledge graphs into memory retrieval concurrently with the user's own memory, with per-result provenance. Source: https://truememory-docs.mvstg.com/changelog/tie-ai-gateway/2026-07-08-graph-namespacing/ · Markdown: https://truememory-docs.mvstg.com/changelog/tie-ai-gateway/2026-07-08-graph-namespacing/index.md Memory retrieval can now draw on knowledge that belongs to no user. A **graph namespace** is a named knowledge graph — a product catalog, company policies, a coaching methodology — identified by a freeform `graph_id` slug. Pass up to four of them and TIE blends their facts with the user's own memory: ```bash POST /memories/retrieve { "query": "what running program fits a beginner", "agent_id": "chatbot", "graph_ids": ["acme__coaching_kb"] } ``` ## What it does - The user's graph and each namespace are searched **concurrently**, then fused into one ranked list. When a user fact and a namespace fact are equally relevant, the user fact wins. - Every result carries provenance: `scope: "user"`, or `scope: "graph"` plus the `graph_id` it came from. Existing callers are unaffected — omit `graph_ids` and responses are unchanged. - The same field works on `POST /memories/search` for raw ranked fact edges without the context block. - A namespace that is empty, unknown, or unreachable contributes nothing; user results always come back. Malformed ids fail fast with `400 VALIDATION_ERROR`. ## Populate a namespace `POST /memories/graph` accepts a `graph_id` target instead of `agent_id` — provide exactly one. Namespace writes require a **service token**; ingestion is asynchronous with the existing `job_id` polling contract, and writers to one namespace serialize cluster-wide. Namespaces are never written automatically — chat auto-ingest and the `memory_write` agent tool only touch the user's own graph. ## Use a namespace in chat `POST /v1/chat/completions` accepts `graph_ids` — TIE blends namespace facts into the injected memory context for that turn. Read-only: conversation ingestion still targets the user's graph. ## Inspect and manage a namespace - `GET /memories/graph?graph_id=…` and `GET /memories/timeline?graph_id=…` show one namespace on its own — no user data, no subject user required, so service-only tokens work. Omit `graph_id` for the user's own view; `graph_id` and `agent_id` are mutually exclusive. This maps directly onto a dashboard filter: "my memories" versus "organization". - `DELETE /memories/{agent_id}/{memory_id}?graph_id=…` and merge with a body `graph_id` operate on namespace facts. Both require a **service token** — namespace changes affect every reader; end-user tokens receive `403 AUTH_SERVICE_REQUIRED`. ## Naming rules `graph_id`s are lowercase slugs (letters, digits, underscores, hyphens; 3–64 characters; starts with a letter). A bare leading UUID is rejected — that shape is reserved for user memory scopes. For UUID-based uniqueness, prefix it: `org_9f8e4b2a-…`. ## Caveats - **Whole-namespace lifecycle ships separately.** Listing all namespaces and exporting or deleting an entire namespace are not in this release; individual fact delete/merge are. - **Never written automatically.** Chat auto-ingest and the `memory_write` agent tool only touch the user's own graph. See [Graph Namespacing](/memory/graph-namespacing/) for naming best practices, the multi-tenant pattern, and full request and response shapes. # Brains: group knowledge packs into one collection > A brain bundles knowledge packs under one ID, so a chat request can attach the whole set with brain_id. Source: https://truememory-docs.mvstg.com/changelog/tie-ai-gateway/2026-07-15-brains/ · Markdown: https://truememory-docs.mvstg.com/changelog/tie-ai-gateway/2026-07-15-brains/index.md A **brain** is a named collection of [knowledge packs](/ai-gateway/knowledge-packs/). Group packs once under a brain, then ground a chat request in the whole set with a single `brain_id`. This replaces listing every pack ID on each request. Manage brains through a new REST surface. Create a brain, then add or remove packs. ```bash # Create a brain POST /brains { "name": "Company Handbook", "description": "HR policies and SOPs" } # Add a pack to it (idempotent) POST /brains/{brain_id}/knowledge-packs { "pack_id": "7f3c..." } ``` Attach the brain at chat time with `brain_id`. TIE resolves it to the packs it holds, then runs the same retrieval as `knowledge_pack_ids`. ```bash POST /v1/chat/completions { "model": "anthropic/claude-sonnet-4-5", "messages": [{ "role": "user", "content": "What is our refund window?" }], "brain_id": "b1a2c3..." } ``` A brain points at its packs rather than holding copies. Deleting a brain removes only the collection, never the packs. A pack deleted after you add it simply drops out of retrieval. An explicit `knowledge_pack_ids` on the request wins, and the brain is ignored. Administrators manage brains across users through `/admin/brains`. Admin create accepts a `user_id`; leave it null to create an app-owned or global brain with no owner. Brains require the PostgreSQL memory backend. See the [Brains guide](/ai-gateway/brains/) for the full contract. # Graph namespaces: full entity graph on inspection, service token required > GET /memories/graph with a graph_id now returns the namespace's deduplicated entity graph instead of an empty entity layer, and requires a service token. Source: https://truememory-docs.mvstg.com/changelog/tie-ai-gateway/2026-07-27-namespace-graph-entities/ · Markdown: https://truememory-docs.mvstg.com/changelog/tie-ai-gateway/2026-07-27-namespace-graph-entities/index.md Inspecting a graph namespace returned only `Note` nodes and zero entities. The endpoint now returns the namespace's full deduplicated entity graph, as the documentation always described. ```bash GET /memories/graph?graph_id=acme__product_kb Authorization: Bearer $SERVICE_TOKEN ``` ## Why the entity layer was empty The entity layer hides entities that no *non-note* episode mentions. That rule keeps per-note artifacts out of a note-heavy graph. It defined "non-note" as two internal episode tags, written by chat flushes and agent-tool writes. Namespaces receive neither. API ingest populates them entirely, so no episode ever qualified. The filter blanked the entity layer for every namespace. It now applies to user graphs only. The entities were always present in the graph. [Search and retrieval](/memory/retrieval/) always blended them in, and only this visualization endpoint hid them. No re-ingest or backfill is required. ## Breaking: namespace inspection requires a service token `GET /memories/graph` and `GET /memories/timeline` with a `graph_id` now return `403 AUTH_SERVICE_REQUIRED` for end-user tokens. This matches the existing policy for namespace writes, deletes, and merges. Both endpoints expose the same namespace entities, so gating one alone would leave the other as a bypass. A namespace holds cross-user data scoped to no caller in particular, so an end-user token has no principled claim on its contents. Returning the unfiltered entity graph widens that gap. Service tokens with no user attached continue to work. **A browser client calling either endpoint directly with an end-user token will break.** Proxy the call through your backend with a service token instead. Reading a user's own graph, by omitting `graph_id`, is unchanged and still works with an end-user token. ## Also documented `source_description_prefix` on `GET /memories/graph` is now documented. It redefines which episodes count as notes, and therefore which entities pass the filter described above. Read [Memory Graph visualization](/memory/building-uis/#memory-graph-visualization) for the parameter reference and the two-layer response model, or [Inspecting a Namespace](/memory/graph-namespacing/#inspecting-a-namespace) for the token requirement. # Id-aware record resolution for shared graphs > Member records keep their identity — same-name members stay distinct, corrections find the right node, and id references become edges. Source: https://truememory-docs.mvstg.com/changelog/tie-ai-gateway/2026-08-19-id-aware-record-resolution/ · Markdown: https://truememory-docs.mvstg.com/changelog/tie-ai-gateway/2026-08-19-id-aware-record-resolution/index.md Record ingestion (`type: "json"`) now resolves entities by the caller's record identity, not by name alone. Two members who share a name keep two separate nodes. A record re-sent with the same `source_description` stamp finds its existing node, even after a display-name change. Three pieces work together. A stable id field on the record (for example `tieUserId`) lands on the subject's node as `external_id`. The `source_description` stamp carries the same id and drives resolution on corrections. Field values that equal another node's `external_id` become real edges — an event's `hostTieUserId` produces a `HOST` edge to that member. ```json { "data": "{\"tieUserId\": \"0f4b9c21-...\", \"name\": \"Dana Ferreira\", \"city\": \"Kuala Lumpur\"}", "type": "json", "graph_id": "acme__members", "source_description": "acme-member-profile:0f4b9c21-..." } ``` Extraction also improved for records: every field produces a fact, including list fields such as `languages` (a new `SPEAKS` relationship) and free-text intent fields (`WANTS_TO_MEET` now applies to `Person` subjects, not only the conversation user). Callers that ingest third-party records should follow the new conventions: [Ingesting Records](/memory/add-data/#ingesting-records) and [Records About People](/memory/graph-namespacing/#records-about-people). # Memory graph: one graph, every entity visible > GET /memories/graph no longer splits episodes into a notes layer or hides entities that only an API ingest mentioned, and a `document` ingest type replaces `note`. Source: https://truememory-docs.mvstg.com/changelog/tie-ai-gateway/2026-09-22-single-graph-visualization/ · Markdown: https://truememory-docs.mvstg.com/changelog/tie-ai-gateway/2026-09-22-single-graph-visualization/index.md `GET /memories/graph` now returns one graph: entity nodes and their `RELATES_TO` facts. Every entity with a live fact in scope appears, whatever kind of episode produced it. Reading a user's own graph after an API ingest returns the extracted entities, not a `Note` bubble with nothing attached. ```bash GET /memories/graph?agent_id=eliza Authorization: Bearer $USER_TOKEN ``` ## What changed The endpoint used to carry two layers. Any episode whose `source_description` was not one of two internal stamps became a `Note` node, and an entity appeared only if a chat exchange or an agent-tool write also mentioned it. `Document` entities were hidden outright. Together these rules turned every record an application ingested into an isolated `Note` with its entities missing, in a user graph and in a namespace alike. All three rules are gone. `source_description` is a provenance label and never decides visibility. No entity type is hidden. Episodes are not nodes in this response. They stay stored verbatim and are returned by `GET /memories/export?include_episodes=true`; search and the timeline always returned the extracted facts. ## Removed - `Note` nodes and their `related` edges no longer appear in the response. - The `source_description_prefix` query parameter is removed. Callers that still send it are not rejected; it has no effect. Remove any sentinel prefix you passed to suppress the notes layer. - `Document` entities are no longer filtered from the visualization. A client that skipped nodes labelled `Note`, or dropped `related` edges, keeps working with nothing to skip. A client that grouped `Note` nodes under a heading of their own now shows that group empty; the same notes' extracted entities appear under their own types. ## Ingest type `document` `POST /memories/graph` accepts `type: "document"` for a note or document the user brought in whole. `note` is accepted as its older name and behaves identically. The type selects extraction rules and nothing else; it does not change how the graph is read back. ```json { "data": "Read Atomic Habits this week. The two-minute rule is the part I want to keep.", "type": "document", "agent_id": "eliza" } ``` No re-ingest or backfill is required. The entities were always in the graph; only this endpoint hid them. # Return 409 Conflict for duplicate email registration > Duplicate email registration now returns 409 with AUTH_EMAIL_EXISTS instead of 400 with AUTH_REGISTRATION_FAILED. Source: https://truememory-docs.mvstg.com/changelog/tie-auth/2026-04-16-duplicate-email-409-conflict/ · Markdown: https://truememory-docs.mvstg.com/changelog/tie-auth/2026-04-16-duplicate-email-409-conflict/index.md Duplicate email registration now returns `409 Conflict` with a dedicated `AUTH_EMAIL_EXISTS` error code. Previously, all registration failures returned `400 Bad Request` with `AUTH_REGISTRATION_FAILED`, forcing callers to pattern-match on the `details` string to detect duplicates. Callers can now match on the `code` field directly: ```json { "code": "AUTH_EMAIL_EXISTS", "message": "A user with this email already exists.", "details": null, "hint": "Try logging in instead." } ``` Other registration failures continue to return `400` with `AUTH_REGISTRATION_FAILED`. For the full error code reference, see [User Tokens — Error Codes](/authentication/overview/). # Add POST /auth/reset-password to confirm password reset > New endpoint to complete the password reset flow using the oobCode from the reset email. Source: https://truememory-docs.mvstg.com/changelog/tie-auth/2026-05-06-reset-password-endpoint/ · Markdown: https://truememory-docs.mvstg.com/changelog/tie-auth/2026-05-06-reset-password-endpoint/index.md A new `POST /auth/reset-password` endpoint completes the two-step password reset flow. Previously, callers had to call Firebase's Identity Toolkit REST API directly to exchange an `oobCode` for a new password. Now TIE Auth handles this as a first-class endpoint. **Request:** ```bash POST /auth/reset-password Content-Type: application/json { "oob_code": "", "new_password": "NewSecurePassword123!" } ``` **Response** (`200 OK`): `{ "status": "ok" }` **New error codes:** | Code | Status | When | |------|--------|------| | `AUTH_INVALID_OOB_CODE` | 400 | The code is invalid or has expired | | `AUTH_PASSWORD_RESET_FAILED` | 400 | Other Firebase-level failure | The existing `POST /auth/forgot-password` endpoint is unchanged — it still sends the reset email as step 1. See [Password Reset](/authentication/email-password/#password-reset) for the full flow. # Adding data > Push external data — CRM records, onboarding answers, notes — into a user's knowledge graph. Source: https://truememory-docs.mvstg.com/memory/add-data/ · Markdown: https://truememory-docs.mvstg.com/memory/add-data/index.md Truememory automatically learns from conversations, but sometimes you need to feed it information from outside the chat — a CRM profile, user preferences from onboarding, health metrics, or notes from another system. The Graph API lets you push arbitrary data into a user's knowledge graph so the LLM can reference it in future conversations. ```bash POST https://your-tie-host/memories/graph Authorization: Bearer $TOKEN Content-Type: application/json ``` **Request body:** ```json { "data": "User is a senior engineer at Mindvalley. Prefers dark mode. Timezone is GMT+8.", "type": "text", "agent_id": "chatbot", "source_description": "Onboarding profile import", "created_at": "2026-04-01T00:00:00Z" } ``` | Field | Type | Required | Description | |-------|------|----------|-------------| | `data` | string | Yes | The content to ingest (max 50,000 characters) | | `type` | `"text"` \| `"json"` \| `"message"` \| `"document"` | Yes | Shape of the data (see below). Selects the extraction rules and nothing else. | | `agent_id` | string | One of | Targets the calling user's memory, determining its [scope](/memory/overview/#memory-scoping). Provide exactly one of `agent_id` or `graph_id`. | | `graph_id` | string | One of | Targets a [graph namespace](/memory/graph-namespacing/) instead of user memory. Service token required. | | `source_description` | string | No | Label describing where the data came from (max 500 characters). For per-record ingests this is the record's identity stamp — see [Ingesting Records](#ingesting-records) | | `created_at` | ISO 8601 datetime | No | When the data was originally created (defaults to now) | | `wait` | boolean | No | When `true`, the request blocks until ingestion completes and returns `200` with `{"status": "completed"}` instead of `202`. Useful for tests and small writes; expect multi-second latency. | **Data types:** | Type | Use for | Example | |------|---------|---------| | `text` | Free-form statements, profile descriptions, paragraphs | `"User enjoys hiking and lives in Kuala Lumpur"` | | `json` | Structured records from APIs or databases | `"{\"name\": \"Alice\", \"role\": \"engineer\"}"` | | `message` | Conversation-style exchanges | `"user: I love dark mode\nassistant: Noted!"` | | `document` | A note or document the user brought in whole — a journal entry, an article, an upload | `"Read Atomic Habits this week. The two-minute rule is the part I want to keep."` | `note` is accepted as the older name for `document` and behaves identically. Whatever the type, the graph is read back the same way: the type never decides what [the visualization](/memory/building-uis/#memory-graph-visualization) shows. **Response** (`202 Accepted`): ```json { "status": "processing" } ``` The response returns immediately. Entity extraction and relationship building happen in the background — processing time varies depending on data size and system load. After processing completes, the new data will appear in [cache-warm](/memory/retrieval/#warming-the-user-cache) results and [timeline](/memory/building-uis/#memory-timeline) responses. :::caution This endpoint processes data asynchronously. A `202` response means the data was accepted for processing, not that it has been fully ingested into the graph. Wait a few seconds before querying memories to see the results. ::: ## Formatting Data for Extraction Truememory uses Graphiti to extract entities, relationships, and facts from the text you send. The quality of extraction depends heavily on how you format the data. Graphiti needs **declarative statements** — not questions, single words, or ambiguous fragments. **Write facts, not questions.** Graphiti extracts meaning from factual statements. A question like "What do you want to do?" contains no information to store. | Format | Extractable? | Why | |--------|-------------|-----| | `"User wants to focus on improving their health"` | Yes | Clear entity (user) + fact (goal is health) | | `"What's something you want to pull off? — Life"` | No | A question with a one-word answer — no extractable relationship | | `"User's goal for the next few months is related to Life"` | Marginal | Better, but "Life" is too vague to create a useful entity | **For onboarding or survey data**, rephrase question-answer pairs into declarative statements on the client side before sending: ```json { "data": "The user's top priority for the next few months is improving their fitness and running a half marathon.", "type": "text", "agent_id": "chatbot", "source_description": "Onboarding survey response" } ``` If the user's answer is too vague to form a useful statement (e.g. a single word like "Life"), consider either skipping ingestion or wrapping it with the question for context: ```json { "data": "When asked about their goals for the next few months, the user answered: Life.", "type": "text", "agent_id": "chatbot" } ``` **For conversation-style data**, use the `message` type with role prefixes. This is the same format Truememory uses internally when flushing conversation history to memory: ```json { "data": "user: I've been meditating every morning for the past month\nassistant: That's great progress! How long are your sessions?", "type": "message", "agent_id": "chatbot" } ``` ## Ingesting Records **For structured records** from APIs or databases, use the `json` type. Extraction parses the structure and creates a fact for every field, including list fields (one fact per item) and identifier fields: ```json { "data": "{\"name\": \"Alice Chen\", \"role\": \"Senior Engineer\", \"team\": \"Platform\", \"timezone\": \"GMT+8\"}", "type": "json", "agent_id": "chatbot", "source_description": "CRM profile sync" } ``` Three conventions make record ingestion reliable. They matter most for [namespace](/memory/graph-namespacing/) records that describe many different people. **Stamp every record, and embed the record's subject id in the stamp.** Give each record its own `source_description` (`acme-member-profile:`) and re-use the exact stamp when you re-send the record as a correction. The stamp is not just a label: entity resolution reads the subject id out of it to find the record's existing node on a correction — even after a display-name change — and to keep two different subjects with the same name on two separate nodes. One constant stamp for a whole import collapses every record into one subject. **Put the subject's stable id in the document.** An id field on the record (for example `tieUserId`) lands on the subject's node as `external_id`, giving you a deterministic lookup key. Field values that equal *another* node's `external_id` become real edges automatically — a `hostTieUserId` on an event record produces a `HOST` edge to that member's node. **Say who the document is about when the subject is not the app user.** Extraction treats a profile-shaped document as the speaking user by default. When the record describes a third party (a member of a community, a CRM contact), state that inside the document itself — the key names are yours, only the content matters: ```json { "data": "{\"documentKind\": \"third_party_member_profile\", \"subjectNote\": \"This document describes a member of the community. The member is not the speaker and not the app user.\", \"tieUserId\": \"0f4b9c21-...\", \"name\": \"Dana Ferreira\", \"city\": \"Kuala Lumpur\"}", "type": "json", "graph_id": "acme__members", "source_description": "acme-member-profile:0f4b9c21-..." } ``` Without this framing the subject is labelled `User` — a protected, singleton label that cannot be deleted or made public. With it, the subject becomes a normal `Person` node carrying `external_id`. **Corrections are re-writes.** To update or cancel a record, send the full document again with the same stamp. The subject resolves to its existing node, changed facts supersede the old ones (the old fact is marked invalid, not deleted), and unchanged facts are not duplicated. # Building memory UIs > Endpoints shaped for user-facing memory features — graph rendering, a memories feed, merge, and delete. Source: https://truememory-docs.mvstg.com/memory/building-uis/ · Markdown: https://truememory-docs.mvstg.com/memory/building-uis/index.md These endpoints are shaped for building user-facing memory features: an interactive graph view, a "Memories" tab, and the management actions (merge duplicates, delete) that go with them. ## Memory Graph (Visualization) Returns the full knowledge graph — entities (nodes) and their relationships (edges) — for a user. Designed for rendering an interactive graph in your frontend. ```bash GET https://your-tie-host/memories/graph Authorization: Bearer $TOKEN ``` | Parameter | In | Required | Description | |-----------|------|----------|-------------| | `agent_id` | query | No | Scope to a specific agent. Omit to get memories across all agents. | | `graph_id` | query | No | Render a [graph namespace](/memory/graph-namespacing/) instead of the user's graph. Requires a service token. Mutually exclusive with `agent_id`; no subject user needed. | | `limit` | query | No | Max edges per page (default 200, max 500) | | `cursor` | query | No | Opaque keyset cursor from the previous page's `next_cursor`, passed back verbatim (`\|`). Malformed values return `400 VALIDATION_ERROR`. | ### Which entities appear The response is one graph: the deduplicated entity nodes (`User` / `Person` / `Topic` / `HealthMetric` / `Document` / …) and the `RELATES_TO` facts between them. Every entity with a live fact in scope appears, whatever produced it — a chat exchange, an agent-tool write, or an API ingest of any `type`. Two things never affect what is shown. `source_description` is a provenance label for your own debugging and record resolution; it does not classify episodes. And no entity type is filtered out, so a book, article, or report the user brought in shows as a `Document` node like any other entity. Episodes themselves are not nodes in this response. They stay stored verbatim; `GET /memories/export?include_episodes=true` returns them. A UI that grouped nodes labelled `Note` (for example a "Notes" section) now finds that group empty — the notes' extracted entities appear under their own types instead. **Response:** ```json { "nodes": [ { "id": "node-uuid-1", "name": "Fauzaan", "summary": "You work as a Staff Engineer at Mindvalley and have a cat named Lucy.", "summary_raw": "Fauzaan is a user.\nFauzaan was promoted to Staff Engineer at Mindvalley.\nFauzaan has a cat named Lucy.", "labels": ["User"], "attributes": {}, "created_at": "2026-04-07T10:30:00+00:00" }, { "id": "node-uuid-2", "name": "Mindvalley", "summary": "You were promoted to Staff Engineer at Mindvalley.", "summary_raw": "Mindvalley is the company where Fauzaan works.\nFauzaan was promoted to Staff Engineer at Mindvalley.", "labels": ["Topic"], "attributes": {}, "created_at": "2026-04-07T10:30:00+00:00" } ], "edges": [ { "id": "edge-uuid-1", "source": "node-uuid-1", "target": "node-uuid-2", "label": "works_at", "fact": "Fauzaan works at Mindvalley", "created_at": "2026-04-07T10:30:00+00:00", "valid_at": "2026-04-07T10:30:00+00:00" } ], "next_cursor": null } ``` Each **node** is an entity (person, place, project, concept) with: - `id` — unique identifier, use as the node key in your graph - `name` — display label - `summary` — display-ready prose. Humanized rewrite when the background humanizer has run for this node (addresses the user as "you/your" when relevant), with automatic fallback to the raw graphiti string otherwise. Good for tooltips, popovers, or the entity-detail panel. - `summary_raw` — graphiti's untouched newline-joined edge facts (third-person, dossier-style). Useful for a "show original" toggle in the UI or for debugging extraction quality. - `labels` — entity types like `User`, `Person`, `Topic`, `Goal`, `Insight` - `attributes` — extracted structured attributes (varies per entity type; may be empty) - `created_at` — when the entity was first written to the graph Each **edge** is a fact connecting two nodes: - `source` / `target` — node IDs this edge connects - `label` — relationship type (e.g. `works_at`, `knows`, `prefers`) - `fact` — the full natural-language statement (third-person; edge-fact humanization is on the roadmap) - `created_at` / `valid_at` — when the fact was recorded / became true **Pagination:** When `next_cursor` is not `null`, pass it as `?cursor={value}` to fetch the next page. Each page returns up to `limit` edges and all nodes referenced by those edges. **Example with pagination:** ```bash # First page GET /memories/graph?limit=100 # Next page (using next_cursor from previous response) GET /memories/graph?limit=100&cursor=2026-04-28T08:34:00+00:00|edge-uuid-100 ``` **Example scoped to one agent:** ```bash GET /memories/graph?agent_id=chatbot&limit=50 ``` :::tip[Frontend integration] The response shape maps directly to graph visualization libraries like [D3 Force Graph](https://d3js.org/d3-force), [vis.js](https://visjs.org/), or [React Flow](https://reactflow.dev/). Use `nodes` as vertices and `edges` with `source`/`target` as connections. ::: ## Memory Timeline Recently active entities first — **one card per entity**, never per fact. Each item is an entity with its humanized summary as the body, and its connected edge facts inlined for an "N facts" disclosure. This is the right shape for any "Memories" tab or memory browser UI. ```bash GET https://your-tie-host/memories/timeline Authorization: Bearer $TOKEN ``` | Parameter | In | Required | Description | |-----------|------|----------|-------------| | `agent_id` | query | No | Scope to one agent. Omit for memories across all agents. | | `graph_id` | query | No | Feed from a [graph namespace](/memory/graph-namespacing/) instead of the user's graph. Mutually exclusive with `agent_id`; no subject user needed. | | `limit` | query | No | Items (entities) per page (default 20, max 200) | | `cursor` | query | No | Opaque keyset cursor from the previous page's `next_cursor`, passed back verbatim (`\|`). Malformed values return `400 VALIDATION_ERROR`. | | `sort` | query | No | `last_updated` (default) or `created` | **Response:** ```json { "items": [ { "id": "node-uuid-1", "name": "Mindvalley", "labels": ["Topic"], "summary": "You were promoted to Staff Engineer at Mindvalley about five months ago and have been working on AI there.", "summary_raw": "Mindvalley is the company where Fauzaan works.\nFauzaan was promoted to Staff Engineer at Mindvalley.\nFauzaan works on AI at Mindvalley.", "first_seen_at": "2026-03-12T14:20:00+00:00", "last_updated_at": "2026-04-28T08:34:00+00:00", "fact_count": 3, "facts": [ { "id": "edge-uuid-1", "label": "works_at", "fact": "Fauzaan works at Mindvalley", "created_at": "2026-04-28T08:34:00+00:00", "valid_at": "2026-04-28T08:34:00+00:00", "direction": "incoming", "other": { "id": "node-uuid-2", "name": "Fauzaan", "labels": ["User"] } } ] } ], "next_cursor": "2026-04-28T08:34:00+00:00|node-uuid-1", "sort": "last_updated" } ``` Each item is one entity with: - `id` / `name` / `labels` — entity identity (use `labels[0]` as the entity-type chip) - `summary` — display-ready prose. Humanized rewrite when the background humanizer has run (addresses the user as "you/your" when the user appears in the entity's facts), with automatic fallback to the raw graphiti string otherwise. Render this as the card body. - `summary_raw` — graphiti's untouched newline-joined edge facts. Useful for a "show original" toggle. - `first_seen_at` — when this entity first entered the graph - `last_updated_at` — `max(edge.created_at)` across the entity's live edges; the most recent activity touching this entity - `fact_count` — number of live edges. Render as "N facts" before the disclosure expands. - `facts[]` — each connected edge inlined with its other endpoint: - `label` — relationship type (e.g. `works_at`, `prefers`) - `fact` — full natural-language statement - `created_at` / `valid_at` — when the edge was written / is real-world-true - `direction` — `"outgoing"` if this entity is the source of the edge, `"incoming"` if the target. Helps render directional UIs ("you → works_at → Mindvalley" vs "Mindvalley ← works_at ← you"). - `other` — the entity on the other side of the edge Entities with zero live (non-expired) edges are excluded from the response — there's nothing to render. **Pagination:** When `next_cursor` is not `null`, pass it as `?cursor={value}` to fetch the next page. Treat the value as **opaque** and send it back byte-for-byte — it carries both a timestamp and the last item's id, because timestamps are not unique (one ingest batch stamps many facts with the same second) and the id is what keeps tied items from being skipped. Pages never overlap and never drop items. `next_cursor` is `null` when you've reached the end; a page that happens to fill exactly still returns a cursor, so the final request may come back with an empty `items` array. Keep requesting until `next_cursor` is `null` rather than stopping on a short page. There is no `offset` or `page` parameter — cursor is the only pagination mechanism, and unknown query parameters are ignored rather than rejected. **Picking a `sort` mode:** - **`last_updated`** (default) — entities ordered by their most recent fact. New conversations push entities to the top of the feed. Good for "what's been happening" UIs. - **`created`** — entities ordered by when they first entered the graph. Stable order regardless of recent activity. Good for "show me the things I've told you about" archival views. **Example: first page across all agents:** ```bash GET /memories/timeline?limit=20 ``` **Example: paginating:** ```bash # Next page (using next_cursor from previous response) GET /memories/timeline?limit=20&cursor=2026-04-28T08:34:00+00:00|node-uuid-20 ``` **Example: entities in graph-creation order, scoped to one agent:** ```bash GET /memories/timeline?agent_id=chatbot&sort=created&limit=50 ``` :::tip[One card per entity, never per fact] A common antipattern is to render each edge (each `RELATES_TO` row in Neo4j) as a separate memory card. That produces dozens of triple-fragment cards from a single conversation. The timeline endpoint deliberately groups by entity so the card count stays low and each card carries the full prose summary — render `summary` as the body, and put `facts[]` behind a "N facts" disclosure that expands inline. ::: :::tip[Why not `/memories/{agent_id}`?] The older `GET /memories/{agent_id}` endpoint (below) is relevance-ranked against a fixed prompt, capped at 50 results, and not paginated — fine for seeding agent context, wrong shape for a user-facing browser. `/memories/timeline` is what to use for any list-of-memories UI. ::: ## Merge Two Memories Sometimes the graph ends up with two entity nodes that represent the same real-world thing — `Mohamed Fauzaan` and `Fauzaan`, or `Acme Inc.` and `Acme`. Merge collapses them into one: every live relationship that pointed at the duplicate is repointed at the primary, the LLM rewrites the primary's summary and attributes to combine both records, and the duplicate is hidden from search. ```bash POST https://your-tie-host/memories/{agent_id}/merge Authorization: Bearer $TOKEN Content-Type: application/json ``` **Request body:** ```json { "primary_id": "node-uuid-keep", "duplicate_id": "node-uuid-fold-in" } ``` | Field | Type | Required | Description | |-------|------|----------|-------------| | `primary_id` | string | Yes | UUID of the entity node to keep. Its name, summary, attributes, and embedding are preserved. | | `duplicate_id` | string | Yes | UUID of the entity node to fold into the primary. Must differ from `primary_id`. | | `graph_id` | string | No | Merge within a [graph namespace](/memory/graph-namespacing/) instead of the user's memory. Service token required; `agent_id` is ignored. | Both UUIDs come from the `id` field of nodes in [Memory Graph (Visualization)](#memory-graph-visualization). **Response:** ```json { "primary_id": "node-uuid-keep", "duplicate_id": "node-uuid-fold-in", "rewired_outgoing": 3, "rewired_incoming": 2, "audit_edge_id": "audit-uuid", "merged_at": "2026-05-04T10:30:00+00:00", "enriched": true, "enriched_summary": "Mohamed Fauzaan is a senior engineer at Mindvalley working on the Truememory platform. Previously known in the graph as 'Fauzaan'.", "enriched_labels": ["Person", "Engineer"] } ``` - `rewired_outgoing` / `rewired_incoming` — counts of live edges repointed from the duplicate to the primary. - `audit_edge_id` — UUID of the `IS_DUPLICATE_OF` edge written from duplicate → primary for traceability. - `merged_at` — timestamp the merge committed. - `enriched` — `true` when the LLM successfully rewrote the primary's summary and attributes; `false` if the LLM call failed and the merge fell back to a blind rewire. - `enriched_summary` / `enriched_labels` — the new summary and unioned labels written onto the primary. `null` when `enriched` is `false`. **What changes after a merge:** - Every live edge incident to the duplicate is replaced with an equivalent edge incident to the primary. The original edge is soft-deleted (its timeline is preserved for undo). - The duplicate node is marked with `merged_into` and disappears from List Memories, Search, and the Memory Graph response. - The primary node's `summary`, `attributes`, and `labels` are **rewritten by the LLM** to combine both records into one canonical view. Its `name` and `name_embedding` are kept as-is — pick the more specific or canonical node as `primary_id`. Order matters. :::note[The merge always calls an LLM] Smart enrichment runs on every merge, using the same model Truememory configures for entity extraction. Expect the call to add **1–3 seconds** of latency on top of the rewire. If the model errors or returns malformed output, the merge falls back to a blind rewire and `enriched` is set to `false` — the user-confirmed merge is never blocked by a model outage. ::: :::caution[Edge fact text is not rewritten] If the original edge text said "Fauzaan likes tea", the `fact` field still literally says "Fauzaan likes tea" after the merge — only the endpoint pointer changed. The Memory Graph response will show the enriched primary node alongside the old fact text. If you need the fact text rewritten, delete the edge and re-ingest a corrected statement. ::: :::note[Duplicate facts are not deduped] If both nodes had similar relationships (e.g. both said "likes coffee"), you will end up with two live edges. The next memory-ingestion pass usually collapses them, or you can delete one manually with [Delete a Memory](#delete-a-memory). ::: **Constraints:** - Both nodes must live in the same scope — same user, same `agent_id`. Cross-agent merges are rejected. **Error cases:** | Status | Code | When | |--------|------|------| | `400` | `MEMORY_MERGE_INVALID` | `primary_id` equals `duplicate_id`, or one of the UUIDs is not in the requested scope | | `501` | `SERVICE_UNAVAILABLE` | The memory backend is disabled for this deployment | ## Delete a Memory :::caution[Deprecated route — see Deleting data] This route deletes one edge but its path doesn't say so. Use [`DELETE /memories/{agent_id}/edges/{edge_id}`](/memory/deleting-data/#delete-an-edge) in new code — same behavior, explicit name. For topic-level deletion (a node and all its facts in one call) see [Delete a node](/memory/deleting-data/#delete-a-node). This route keeps serving unchanged until existing callers migrate. ::: ```bash DELETE https://your-tie-host/memories/{agent_id}/{memory_id} Authorization: Bearer $TOKEN ``` | Parameter | In | Required | Description | |-----------|------|----------|-------------| | `graph_id` | query | No | Delete from a [graph namespace](/memory/graph-namespacing/) instead of the user's memory. Service token required; `agent_id` is ignored. | **Response:** ```json { "status": "deleted", "memory_id": "edge-abc123" } ``` Returns `404` if the memory is not found. :::note In the List Memories response, `source` indicates the graph element type (always `"edge"`, representing a relationship between entities). `memory_type` is always `"long_term"` for knowledge graph memories. ::: ## List Memories (deprecated) :::caution[Use `/memories/timeline` instead] This endpoint is **deprecated** and will be removed in a future release. It is relevance-ranked against a fixed prompt (not time-ordered), hard-capped at 50 results, has no pagination, and pays an LLM embed + vector + reranker cost on every call. For user-facing memory lists, use [`GET /memories/timeline`](#memory-timeline) — it returns flat, time-ordered, cursor-paginated facts with selectable `sort`. ::: ```bash GET https://your-tie-host/memories/{agent_id} Authorization: Bearer $TOKEN ``` **Response:** ```json [ { "id": "edge-abc123", "title": "Works on Truememory Auth", "name": "Truememory Auth integration", "content": "User is integrating Truememory Auth with Finerminds", "memory_type": "long_term", "source": "edge" } ] ``` # Deleting data > Remove memories from a user's knowledge graph — one fact, one topic, or everything — and know exactly what each delete does and does not remove. Source: https://truememory-docs.mvstg.com/memory/deleting-data/ · Markdown: https://truememory-docs.mvstg.com/memory/deleting-data/index.md The knowledge graph stores memories at three levels: **edges** (one fact each), **nodes** (an entity — a person, habit, or topic — that facts connect to), and **episodes** (the raw ingested source text). Deletion works at matching levels, and each level has a different blast radius. This page covers all of them, including what each delete deliberately leaves behind. | You want to | Use | Removes | |-------------|-----|---------| | Remove one incorrect fact | [Delete an edge](#delete-an-edge) | That edge, both stores | | Remove a whole topic ("forget my ex") | [Delete a node](#delete-a-node) | The node and every edge touching it, both stores | | Remove everything for a user or a namespace | [Clear all memories](#clear-all-memories) | Edges, nodes, **and episodes** — full erase | | Erase a user entirely (GDPR / CCPA request) | [Delete a user](/authentication/delete-user/) | Memory in every scope, threads, identity, uploads, traces — the account is gone. An account operation, documented under Authentication | All deletes are **hard deletes** — the data is removed from the graph database and the search overlay, not flagged. There is no undo. (System-side fact *invalidation* — when a new fact contradicts an old one — is a separate, soft mechanism and is not affected by anything on this page.) ## Delete an edge One edge carries one fact. Deleting the edge deletes the fact. ```bash DELETE https://your-tie-host/memories/{agent_id}/edges/{edge_id} Authorization: Bearer $TOKEN ``` | Parameter | In | Required | Description | |-----------|------|----------|-------------| | `edge_id` | path | Yes | The edge UUID — `facts[].id` in the [Memory Timeline](/memory/building-uis/#memory-timeline) response | | `graph_id` | query | No | Delete from a [graph namespace](/memory/graph-namespacing/) instead of the user's memory. Service token required; `agent_id` is ignored. | **Response:** ```json { "status": "deleted", "edge_id": "edge-abc123" } ``` Deleting an edge never deletes the nodes at either end. A node whose last live edge is deleted stays in the graph but disappears from the timeline (entities with zero live edges are not rendered). The lookup is scoped to the calling user, not to `agent_id` — you don't need to know which agent produced the fact, and one user can never delete another user's data. :::note[This route replaces `DELETE /memories/{agent_id}/{memory_id}`] The old route deleted edges too, but its path didn't say so. It is now **deprecated** — it keeps working unchanged, but new code should call `/edges/{edge_id}`. Same scoping, same cascade; the response names the field `edge_id` instead of `memory_id`. ::: ## Delete a node Deletes an entity and **every edge connected to it, in both directions** — the card-level "forget this topic" operation. One call, atomic, instead of a client-side loop over the entity's facts. ```bash DELETE https://your-tie-host/memories/{agent_id}/nodes/{node_id} Authorization: Bearer $TOKEN ``` | Parameter | In | Required | Description | |-----------|------|----------|-------------| | `node_id` | path | Yes | The node UUID — the top-level `id` of a [Memory Timeline](/memory/building-uis/#memory-timeline) item | | `graph_id` | query | No | Delete from a [graph namespace](/memory/graph-namespacing/) instead of the user's memory. Service token required; `agent_id` is ignored. | **Response:** ```json { "status": "deleted", "node_id": "node-def456", "facts_deleted": 7 } ``` `facts_deleted` counts the removed edges — log it so deletions are verifiable. The node's search-overlay row (name, summary, embedding) is removed along with the edge rows, so nothing about the entity keeps surfacing in retrieval. **Error cases:** | Status | Code | When | |--------|------|------| | `404` | `MEMORY_NOT_FOUND` | The node UUID is not in the requested scope | | `422` | `MEMORY_NODE_PROTECTED` | The node is structural and can never be deleted | Three kinds of node are **protected** and always return `422`: - The user's own `User` node — it anchors every relationship in the graph. - `Topic` nodes — abstract subject areas ("fitness", "AI") used for organization. - `Value` nodes — taxonomy scaffolding, not memories. :::caution[Shared facts disappear from other cards] An edge belongs to *two* nodes. "Fauzaan runs with Maya" hangs off both the **Running** node and the **Maya** node. Deleting Running removes that edge from Maya's card too, and Maya's `fact_count` drops. This is inherent to graph deletion — surface it in your UX if users will notice. ::: ## Clear all memories Erases everything in one scope — a user+agent pair, or a whole [graph namespace](/memory/graph-namespacing/): edges, nodes, **and episodic source content**, from both stores. This is the one true erase and the right backend for a "delete all memories" button or a data-removal request. ```bash POST https://your-tie-host/memories/{agent_id}/clear Authorization: Bearer $TOKEN ``` | Parameter | In | Required | Description | |-----------|------|----------|-------------| | `graph_id` | query | No | Empty a [graph namespace](/memory/graph-namespacing/) instead of the user's memory. Service token required; `agent_id` is ignored. | **Response:** ```json { "status": "cleared", "scope": "user", "deleted_count": 42 } ``` `scope` names what was actually cleared — `"user"` for a pair, or `"graph"` plus a `graph_id` field for a namespace — so a caller can verify the blast radius before trusting a `deleted_count`. A clear of an already-empty scope succeeds with `deleted_count: 0`. ## What edge and node deletes do NOT remove Edge and node deletes remove *derived* knowledge. They deliberately do **not** touch **episodes** — the raw source text (chat exchanges, ingested documents) the knowledge was extracted from. Two consequences worth designing for: 1. **The source remains stored.** If your product's delete must also erase the underlying record, use `clear` (everything) or delete the source conversation through your own retention flow. Per-episode deletion is not currently exposed. 2. **Deleted knowledge can be re-learned.** If a later conversation or ingest states the same information again, extraction recreates the entity and its facts. For conversational input this is expected (a user who re-tells you about their cat wants the cat remembered). For **batch ingestion this is a trap**: pushing a cumulative summary through [`POST /memories/graph`](/memory/add-data/) every day restates old information and resurrects whatever the user deleted, usually within a day. Ingest deltas — only what's new — and deletions stick. :::note[Where the IDs come from] One [Memory Timeline](/memory/building-uis/#memory-timeline) response carries everything the delete routes need: each item's top-level `id` is the node UUID, and each entry in its `facts[]` array carries the edge UUID. Treat both as opaque strings — list them, show them, send them back. ::: # Export & import > Move a user's entire knowledge graph — account migration, backup, and data portability. Source: https://truememory-docs.mvstg.com/memory/export-import/ · Markdown: https://truememory-docs.mvstg.com/memory/export-import/index.md Memory is something a user accumulates over time — facts, preferences, relationships, and context that an agent has learned about them. There are three common reasons to move that memory around: - **Account migration** — a user is switching to a new account and wants to take their memory with them - **Backup** — capture a snapshot now so you can restore if something goes wrong later - **Data portability** — give the user a copy of what Truememory has learned about them (useful for GDPR-style "export my data" requests) The export endpoint produces a single JSON document. The import endpoint consumes that same document and writes it into the caller's graph. Both sides span every agent scope for the user in one call — you do not loop per-agent. Think of it like `git clone` for a user's knowledge graph: one command pulls the whole repository, one command pushes it back somewhere else. Where this metaphor breaks down: embeddings (the vector representations Graphiti uses for semantic search) are not re-generated automatically on export, and they are rebuilt asynchronously on import — so searches against freshly imported data can return empty for a few minutes while the backfill runs. ## Export Memory ```bash GET https://your-tie-host/memories/export Authorization: Bearer $TOKEN ``` | Parameter | In | Required | Description | |-----------|------|----------|-------------| | `include_episodes` | query | No | Include raw `:Episodic` nodes (conversation chunks the graph was extracted from). Defaults to `false` — episodes are much larger than the extracted graph and contain raw PII. | | `include_expired` | query | No | Include edges the user previously deleted (soft-deleted via `expired_at`). Defaults to `false`. Useful for full archival; skip for normal use. | | `include_embeddings` | query | No | Include the vector embeddings on nodes and edges. Defaults to `false`. Embeddings are regeneratable and inflate payload size roughly 10x. | **Response:** ```json { "schema_version": "1", "exported_at": "2026-04-20T08:15:42.123456+00:00", "source_user_id": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee", "counts": { "entity_nodes": 142, "entity_edges": 389, "episodes": 0 }, "entity_nodes": [ { "uuid": "n-abc", "labels": ["User"], "properties": { "name": "Alice", "summary": "Senior engineer at Mindvalley", "created_at": "2026-01-12T09:00:00+00:00" } } ], "entity_edges": [ { "uuid": "e-xyz", "type": "RELATES_TO", "source_uuid": "n-abc", "target_uuid": "n-def", "properties": { "fact": "Alice works at Mindvalley", "valid_at": "2026-01-12T09:00:00+00:00" } } ], "episodes": [] } ``` Each envelope field: - `schema_version` — envelope format version. The import endpoint only accepts `"1"` today. - `source_user_id` — the user the export was taken from. Import uses this to rewrite scoping on the target user. - `counts` — record totals, mirrored by the lengths of the arrays below. - `entity_nodes` — extracted entities (people, places, projects, topics) with their attributes. - `entity_edges` — facts connecting two entities. `fact` is the human-readable statement; `source_uuid` / `target_uuid` reference entries in `entity_nodes`. - `episodes` — raw conversation chunks, populated only when `include_episodes=true`. **Size cap:** exports above **50,000 total records** return `413 Payload Too Large`. If you hit this, export per-agent (`?agent_id=...`) is not currently supported on `/memories/export` — contact the Truememory team to raise the cap. :::note[Embeddings are stripped by default] Vector embeddings (`name_embedding` on nodes, `fact_embedding` on edges) are excluded from the response unless you pass `include_embeddings=true`. On import, Truememory regenerates them automatically — so leaving them out is almost always the right choice. ::: ## Import Memory Accept an exported envelope and write it into the caller's graph. Intended for the same account (migration, restore) or across accounts (data transfer between users). ```bash POST https://your-tie-host/memories/import Authorization: Bearer $TOKEN Content-Type: application/json ``` **Request body** — a JSON envelope produced by `GET /memories/export`. Unchanged envelopes can be re-sent; see the idempotency note below. **Response** (`202 Accepted`): ```json { "status": "processing", "import_id": "b7a1c3ef9e5c4d80b6ab1b3b5b5c2c9d", "nodes_written": 142, "edges_written": 387, "episodes_written": 0, "edges_skipped": ["e-orphaned-1", "e-orphaned-2"] } ``` Response fields: - `import_id` — pass to `GET /memories/import/{import_id}` to poll for embedding regeneration status. - `nodes_written` / `edges_written` / `episodes_written` — counts of records freshly created in the target graph. Records whose UUID already exists are skipped (see below). - `edges_skipped` — UUIDs of edges whose `source_uuid` or `target_uuid` did not match any node in the target graph. These are dropped silently; fix the source export or ignore them. **Scoping — what happens to `group_id`.** Memory in Truememory is scoped by `group_id`, which looks like `{user_id}_{agent_id}`. On import, the envelope's `source_user_id` is stripped from every record's `group_id` and the caller's user ID is substituted. The agent suffix (e.g. `__shared__`, `chatbot`) is preserved. A record originally under `source-user_chatbot` becomes `caller-user_chatbot` in the target graph. **Idempotency — skip-if-exists by UUID.** Import is safe to re-run. Any node, edge, or episode whose UUID already exists in the target graph is left untouched — nothing is overwritten, and no duplicates are created. This means: - Re-sending the same envelope produces `nodes_written: 0` on the second call. - Facts the user has accumulated *since* the export are not clobbered. - There is no partial-state concern: a crashed import can be retried safely. :::caution[Embeddings regenerate asynchronously] The import endpoint returns `202` once the graph records are written. Vector embeddings are regenerated in a background task that can take several minutes for a large import. **Semantic search (`POST /memories/search`, `/memories/graph/{user_id}/warm`) will return empty results for freshly imported facts until the backfill finishes.** Poll `GET /memories/import/{import_id}` to know when search is ready. ::: **Error cases:** | Status | When | |--------|------| | `400` | `schema_version` is unsupported, `source_user_id` is missing, or the envelope is malformed | | `413` | Total records exceed the import cap (50,000) | | `501` | The memory backend is disabled for this deployment | ## Check Import Status Poll the status of an async import. Useful for showing a progress indicator or gating the UI until semantic search is ready. ```bash GET https://your-tie-host/memories/import/{import_id} Authorization: Bearer $TOKEN ``` **Response:** ```json { "import_id": "b7a1c3ef9e5c4d80b6ab1b3b5b5c2c9d", "status": "ready", "nodes_written": 142, "edges_written": 387, "episodes_written": 0, "nodes_embedded": 142, "edges_embedded": 387, "edges_skipped_count": 2, "started_at": "2026-04-20T08:15:42.123456+00:00", "finished_at": "2026-04-20T08:18:07.456789+00:00", "error": null } ``` Status values: - `processing` — the embedding backfill is still running. Search will miss freshly imported data. - `ready` — backfill complete, semantic search fully covers the imported records. - `failed` — backfill hit an error. Inspect `error` for details. The graph records themselves are still written; only embeddings are missing. Contact support or re-trigger by importing the same envelope again (idempotent). `nodes_embedded` and `edges_embedded` count only records from *this* import, not pre-existing records in the user's graph. They will stop incrementing once `status` is `ready` or `failed`. :::note[Status is in-memory only] Import status is held in server memory and cleaned up 10 minutes after completion. If Truememory restarts between your POST and your GET, the status endpoint returns `404` — but the imported data is already committed to Neo4j, so no action is needed. ::: # Graph Namespacing Source: https://truememory-docs.mvstg.com/memory/graph-namespacing/ · Markdown: https://truememory-docs.mvstg.com/memory/graph-namespacing/index.md Truememory supports graph namespacing through `graph_id`s — named, isolated knowledge graphs that live alongside user memory in the same Truememory instance. Namespaces hold knowledge that belongs to no user — a product catalog, company policies, a coaching methodology, a support playbook — and can be blended into any user's memory retrieval. Graph namespacing is particularly useful for: - **Multi-tenant applications**: Isolate curated knowledge between different customers or organizations - **Domain-specific knowledge**: Create specialized graphs for different domains or use cases - **Team collaboration**: Allow different teams or apps to maintain their own knowledge spaces - **Testing environments**: Keep experimental knowledge separate from what production agents see ## How Namespacing Works In Truememory, every node and edge in the knowledge graph is associated with a group id. Each user's memory already lives in its own automatically managed namespace, keyed by the user's identity. A `graph_id` names an additional namespace: nodes and edges written under the same `graph_id` form a cohesive, isolated graph that can be queried, inspected, and curated independently of every user's memory and of other namespaces. Namespaces meet user memory at retrieval time. When a request includes `graph_ids`, Truememory searches the user's own graph and each named namespace **concurrently**, then fuses the rankings into a single result list: 1. Each graph is searched independently with the same query 2. Rankings are merged — when a user fact and a namespace fact are equally relevant, the user fact wins 3. Every result carries its origin: `scope: "user"`, or `scope: "graph"` with the `graph_id` it came from A namespace that is empty, nonexistent, or temporarily unreachable contributes no results — the user's own results always come back. A missing namespace is not an error. ## Key Benefits - **Data isolation**: A request only touches the namespaces it names — no leakage between namespaces or into user memory - **Simplified management**: Related knowledge lives together, inspectable and curatable as one unit - **Curated by design**: Namespaces are never written automatically — chat auto-ingest and the agent's `memory_write` tool only touch the user's own graph - **Flexible architecture**: Per-tenant graphs, per-domain graphs, and user memory coexist in one Truememory instance Namespaces store facts and entities in a knowledge graph, retrieved and ranked like memory. To give an agent passages quoted from an uploaded document instead, use [Brains (knowledge packs)](/ai-gateway/knowledge-packs/) — the two work well together. ## Naming Namespaces A `graph_id` is a freeform slug. No registration step is required — a namespace exists once data lands in it. Ids must match: - Lowercase letters, digits, underscores, or hyphens - Start with a letter, 3–64 characters long - Must **not** begin with a bare UUID — that shape is reserved for user memory namespaces. Ids that need UUID-based uniqueness (an org id, a tenant id) are fully supported — put a short prefix in front: `org_9f8e4b2a-…` Invalid ids are rejected with `400 VALIDATION_ERROR` before any operation runs. | graph_id | Verdict | |---|---| | `acme__product_kb` | ✅ Recommended shape | | `kb__sales_playbook` | ✅ Recommended shape | | `org_9f8e4b2a-e89b-12d3-a456-426614174000` | ✅ UUID-based uniqueness, prefixed | | `Sales-KB` | ❌ Uppercase not allowed | | `kb` | ❌ Too short (min 3 characters) | | `123_team_docs` | ❌ Must start with a letter | | `9f8e4b2a-e89b-12d3-a456-426614174000` | ❌ Bare UUID — reserved for user memory | ## Adding Data to a Namespace Populate a namespace through [Add Data to Memory](/memory/add-data/) with a `graph_id` target instead of `agent_id`. Writing to a namespace requires a **service token** — end-user tokens receive `403 AUTH_SERVICE_REQUIRED`: ```bash POST https://your-tie-host/memories/graph Authorization: Bearer $SERVICE_TOKEN Content-Type: application/json ``` ```json { "graph_id": "acme__coaching_kb", "data": "The beginner running program has three phases: base, build, peak.", "type": "text", "source_description": "Coaching methodology import" } ``` Provide exactly one of `agent_id` (user memory) or `graph_id` (namespace). Ingestion is asynchronous — the `202` response includes a `job_id` to poll via [`GET /memories/graph/status/{job_id}`](/memory/add-data/), or pass `wait: true` for synchronous writes. Writers to one namespace serialize cluster-wide, so concurrent imports never race entity extraction. ## Records About People A namespace often holds one profile record per member of a community. Follow the [record-ingestion conventions](/memory/add-data/#ingesting-records): stamp each record with the member's id, put the id in the document, and frame the document as third-party. Each member then becomes one `Person` node carrying the member's id as `external_id`: ```cypher MATCH (p:Person {external_id: $memberId}) RETURN p ``` Identity comes from the id, not the name — two members who share a name keep two separate nodes, and a re-sent record finds its member even after a name change. Records that reference members by id (an event's host, a group's owner) get real edges to those members automatically. ## Using a Namespace in Chat For agents running through Truememory's chat, pass `graph_ids` directly on the [chat completions](/ai-gateway/chat-completions/) request — Truememory blends namespace facts into the memory context it injects, with no prompt composition on your side: ```json POST /v1/chat/completions { "model": "chatbot", "messages": [{"role": "user", "content": "what running program fits a beginner"}], "graph_ids": ["acme__coaching_kb"] } ``` The conversation itself still only ingests into the user's graph — `graph_ids` affects what the agent *reads*, never what gets written. If you run your **own** LLM, specify `graph_ids` on [Retrieve Memory Context](/memory/retrieval/#retrieve-memory-context) instead — the endpoint that returns exactly what Truememory injects during a chat turn. Namespace facts blend into the per-message retrieval; the cached session context block remains user-only. Everything else about the endpoint is unchanged, and omitting the field gives the existing user-only behavior. ```bash POST https://your-tie-host/memories/retrieve Authorization: Bearer $TOKEN Content-Type: application/json ``` **Request body:** ```json { "query": "what running program fits a beginner", "agent_id": "chatbot", "graph_ids": ["acme__coaching_kb"] } ``` | Field | Type | Required | Description | |-------|------|----------|-------------| | `graph_ids` | string[] | No | Up to 4 namespaces to blend into the retrieval. Duplicates are deduped. | **Response** (trimmed to the retrieval half) — one ranked list; `scope` and `graph_id` identify each fact's origin: ```json { "retrieval": { "results": [ { "id": "edge-uuid-1", "name": "TRAINS_FOR", "content": "User is training for a marathon in October", "source_name": "User", "target_name": "Marathon", "scope": "user", "graph_id": null }, { "id": "edge-uuid-2", "name": "HAS_PHASE", "content": "The beginner running program has three phases: base, build, peak", "source_name": "Beginner running program", "target_name": "Training phases", "scope": "graph", "graph_id": "acme__coaching_kb" } ], "prompt": "## Relevant memories for this message\n..." } } ``` The rest of the payload — the context block, timings, cache diagnostics — is unchanged; see [Retrieve Memory Context](/memory/retrieval/#retrieve-memory-context). The same field works on [Search Memory](/memory/retrieval/#search-memory) when you want raw ranked fact edges without the context block or rendered prompts — results carry the same `scope` and `graph_id` tags. :::note `graph_ids` on chat, retrieve, and search always **blends** — the user's own graph is searched too, and these endpoints require a subject user. There is no namespace-only mode on them; to view a namespace by itself, use the inspection endpoints below. ::: ## Inspecting a Namespace Inspection endpoints target exactly one graph per call — the user's own, or a namespace via `graph_id`. No blending: pass `graph_id` to see the namespace, omit it for the user's graph. `graph_id` is mutually exclusive with `agent_id`. ```bash GET https://your-tie-host/memories/graph?graph_id=acme__product_kb GET https://your-tie-host/memories/timeline?graph_id=acme__product_kb Authorization: Bearer $TOKEN ``` - [Memory Graph](/memory/building-uis/#memory-graph-visualization) renders the namespace's entities and facts for a graph UI. - [Memory Timeline](/memory/building-uis/#memory-timeline) returns an entity feed, recently active first — useful for reviewing what a namespace contains. Inspecting a namespace requires a **service token** — end-user tokens receive `403 AUTH_SERVICE_REQUIRED` from both endpoints. A namespace holds cross-user data that is scoped to no caller in particular, so its inspection views are gated exactly like namespace writes. Service tokens **with no user attached** work fine: curation tooling and org-level dashboards do not need to impersonate a user. Reading the user's own graph or timeline (omitting `graph_id`) is unaffected and still works with an end-user token. :::caution[Changed behaviour] Namespace inspection was previously open to any authenticated caller. **Both `GET /memories/graph?graph_id=…` and `GET /memories/timeline?graph_id=…` now return `403 AUTH_SERVICE_REQUIRED` for end-user tokens.** If a browser client calls either one directly with an end-user token, proxy the call through your backend with a service token instead. ::: These views are single-scope — one call shows one graph, never a mix. A memories dashboard maps this onto a filter: a "my memories" tab calls the endpoint without `graph_id`, an "organization" tab calls it with one (through your backend). Same response shape, same cursor pagination, same rendering components. There is no server-side blended feed (the two graphs have independent cursors); if you need an interleaved "all memories" scroll, merge the two cursor streams client-side by timestamp and badge each item by origin. ## Managing Namespace Memories Deleting or merging namespace facts affects every reader, so both operations require a **service token**. End-user tokens receive `403 AUTH_SERVICE_REQUIRED`. Delete (invalidate) one fact edge — the `agent_id` path segment is ignored when `graph_id` is present: ```bash DELETE https://your-tie-host/memories/{agent_id}/{memory_id}?graph_id=acme__product_kb Authorization: Bearer $SERVICE_TOKEN ``` Merge duplicate entities within a namespace — same semantics as a [user-scoped merge](/memory/building-uis/#merge-two-memories): ```json POST /memories/{agent_id}/merge { "primary_id": "node-uuid-keep", "duplicate_id": "node-uuid-fold-in", "graph_id": "acme__product_kb" } ``` Empty an entire namespace — a hard delete of every node, edge, and episode in it, from both stores: ```bash POST https://your-tie-host/memories/{agent_id}/clear?graph_id=acme__product_kb Authorization: Bearer $SERVICE_TOKEN ``` The response's `scope` field names what was cleared (`"graph"` plus the `graph_id`, vs `"user"` for a pair clear), so a caller can verify the blast radius. See [Clear all memories](/memory/deleting-data/#clear-all-memories) for details. Export and import remain user-scoped — the rest of the namespace lifecycle (list, export) ships separately. ## Best Practices for Graph Namespacing - **Consistent naming**: Use a consistent naming convention for your `graph_id` values — we recommend `{org_or_app}__{topic}`, with underscores over hyphens. A namespace *is* its id (there is no rename), so choose descriptive, permanent names: `acme__support_kb` ages better than `kb2`. When ids must be globally unique rather than readable, derive them from your own identifiers: `org_{org_uuid}`. - **Documentation**: Maintain documentation of your namespace structure and purpose — which namespaces exist, what each contains, and who writes to it. Truememory does not maintain a registry. - **Granularity**: Choose an appropriate level of granularity for your namespaces - Too many namespaces can lead to fragmented data and extra search fan-out per request - Too few namespaces may not provide sufficient isolation, mixing unrelated domains into every result list - One namespace per coherent knowledge domain is a good default - **Cross-namespace queries**: Retrieval blends the user's graph with up to 4 namespaces in one call. When you need more than that — or a combined view of inspection feeds — perform multiple queries across namespaces and combine the results in your application logic. ## A Multi-Tenant Example Map each tenant to its own namespace prefix, and pass the tenant's namespaces on every retrieval for that tenant's users: ```json { "query": "what is the refund policy", "agent_id": "support-agent", "graph_ids": ["acme__support_kb", "acme__product_kb"] } ``` Users of tenant `acme` get Acme's policies blended with their personal memory; tenant `globex` passes `globex__support_kb` and never sees Acme's data. Isolation is structural — a request only touches the namespaces it names. # Overview > Persistent user memory on a knowledge graph — usable with the AI Gateway or standalone. Source: https://truememory-docs.mvstg.com/memory/overview/ · Markdown: https://truememory-docs.mvstg.com/memory/overview/index.md Truememory stores user memories in a knowledge graph (powered by [Graphiti](https://github.com/getzep/graphiti) and Neo4j). It automatically extracts entities, relationships, and facts from conversations and uses them to give the LLM context about the user in future requests. Memory works two ways: - **With the AI Gateway** — memory is injected into every [chat completions](/ai-gateway/chat-completions/) request automatically. Nothing to integrate. - **Standalone** — you run your own LLM and call the memory endpoints directly: [add data](/memory/add-data/), [retrieve context](/memory/retrieval/), and manage what's stored. All memory endpoints require a Bearer token from [Truememory Auth](/authentication/overview/). Backend services can instead use [service auth](/authentication/service-accounts/) with `X-On-Behalf-Of` (Truememory user UUID) or `X-External-User-Id` (your own user IDs). ## How memory works Every chat turn runs **two memory fetches concurrently**, and both land in the LLM's context: - **Session context** — the "Known Entities / Known Facts" block. Cached 5 minutes per user; falls back to a fast database query when cold. - **Per-message retrieval** — a hybrid graph search (BM25 + cosine similarity + graph traversal + reranking) against this specific message. Skipped for low-information messages ("hi", "ok"). On top of the automatic injection, the LLM can call three **server-side memory tools** mid-conversation — invisible to the client: `memory_search`, `memory_write`, and `memory_get`. After each turn, Truememory records the exchange and extracts new entities and facts from it in the background. No extra API calls are needed — injection, tools, and recording are all automatic on the gateway path. If you run your own LLM, [Retrieve Memory Context](/memory/retrieval/#retrieve-memory-context) returns exactly what this pipeline would inject, as ready-made prompt blocks. ## The graph model Three kinds of records make up a user's memory: | Record | What it is | Example | |--------|-----------|---------| | **Entity** (node) | A person, place, project, or concept, with a prose summary | `Mindvalley` — "You were promoted to Staff Engineer at Mindvalley." | | **Fact** (edge) | A natural-language statement connecting two entities | `Fauzaan —works_at→ Mindvalley` | | **Episode** | The raw conversation chunk or ingested data a fact was extracted from | a recorded chat exchange | Facts carry two timestamps: `created_at` (when recorded) and `valid_at` (when it became true in the real world). Entity summaries come in two renditions — `summary` (humanized, addresses the user as "you") and `summary_raw` (Graphiti's third-person dossier style). ## Memory scoping Each agent is configured with a memory scope that controls how memories are shared: | Scope | Behavior | Example | |-------|----------|---------| | `shared` | Memories are accessible across all agents using the shared pool | `chatbot`, `research-assistant` | | `agent` | Memories are isolated to that specific agent | `rag-assistant` | Memory scope is set per agent — clients cannot override it. See [Agents](/ai-gateway/agents/) for per-agent scope details. ## User memory vs. graph namespaces Everything above describes **user memory** — one graph per user. For knowledge that should be shared *across* users (a product catalog, a coach's methodology, tenant-wide facts), Truememory has [graph namespaces](/memory/graph-namespacing/): standalone graphs written by your backend and blended into any user's retrieval via `graph_ids`. ## Choosing an endpoint | You want to… | Use | Where | |--------------|-----|-------| | Chat with memory, zero integration | `POST /v1/chat/completions` — injection is automatic | [Unified API](/ai-gateway/chat-completions/) | | Make a session's first request fast | warm the user cache on login/app-open | [Search & retrieval](/memory/retrieval/#warming-the-user-cache) | | Feed in outside data (CRM, onboarding, notes) | add data to the graph | [Adding data](/memory/add-data/) | | Get chat-identical context for your own LLM | retrieve memory context | [Search & retrieval](/memory/retrieval/#retrieve-memory-context) | | Get raw ranked facts for a query | search memory | [Search & retrieval](/memory/retrieval/#search-memory) | | Render an interactive graph view | memory graph | [Building memory UIs](/memory/building-uis/#memory-graph-visualization) | | Build a "Memories" tab or feed | memory timeline | [Building memory UIs](/memory/building-uis/#memory-timeline) | | Collapse duplicate entities | merge | [Building memory UIs](/memory/building-uis/#merge-two-memories) | | Let a user delete a memory | delete | [Building memory UIs](/memory/building-uis/#delete-a-memory) | | Back up, migrate, or hand over a user's memory | export & import | [Export & import](/memory/export-import/) | | Share knowledge across users | graph namespaces | [Graph namespacing](/memory/graph-namespacing/) | # Search & retrieval > Query the knowledge graph — chat-identical context blocks, raw ranked facts, and cache warming. Source: https://truememory-docs.mvstg.com/memory/retrieval/ · Markdown: https://truememory-docs.mvstg.com/memory/retrieval/index.md Two endpoints read a user's memory, at different levels of processing — plus a cache-warming call to make the first read of a session fast. For which to use when, see [Choosing an endpoint](/memory/overview/#choosing-an-endpoint). ## Retrieve Memory Context Returns exactly what Truememory injects into the LLM during a chat turn — the same pipeline as `POST /v1/chat/completions`: cached session context, low-information gating, and a per-message graph search. Use this when you run your **own** LLM and want Truememory's memory as a ready-made context block with identical recall to Truememory's automatic injection. ```bash POST https://your-tie-host/memories/retrieve Authorization: Bearer $TOKEN Content-Type: application/json ``` **Request body:** ```json { "query": "what should I eat for breakfast?", "agent_id": "chatbot", "display_name": "Alice Chen", "email": "alice@example.com" } ``` | Field | Type | Required | Description | |-------|------|----------|-------------| | `query` | string | Yes | The user message to retrieve memories for (max 5,000 characters) | | `agent_id` | string | Yes | Determines [memory scope](/memory/overview/#memory-scoping) | | `display_name` | string | No | User display name rendered into the context prompt — pass it for parity with chat, where Truememory uses the authenticated user's name | | `email` | string | No | User email rendered into the context prompt | | `graph_ids` | string[] | No | Up to 4 [graph namespaces](/memory/graph-namespacing/) to blend into the per-message retrieval | **Response:** ```json { "query": "what should I eat for breakfast?", "user_id": "550e8400-e29b-41d4-a716-446655440000", "agent_id": "__shared__", "elapsed_seconds": 0.42, "context": { "data": { "entities": [...], "facts": [...] }, "prompt": "## Known Entities\n...\n## Known Facts\n...", "cache_hit": true, "cache_age_seconds": 12.5, "source": "cache" }, "retrieval": { "results": [ { "id": "edge-uuid", "name": "PREFERS", "content": "User prefers a high-protein breakfast", "created_at": "2026-05-01T08:00:00Z", "valid_at": "2026-05-01T08:00:00Z", "source_name": "User", "target_name": "High-protein breakfast" } ], "prompt": "## Relevant memories for this message\n...", "low_info_skipped": false, "result_count": 1 } } ``` The response has two halves, mirroring the two memory fetches Truememory runs concurrently on every chat turn: - **`context`** — the session-level block ("Known Entities / Known Facts"), cached for 5 minutes per user. `cache_hit` and `source` tell you whether it came from the hot cache or a database fallback. - **`retrieval`** — facts found by a hybrid graph search against `query`. Skipped entirely for low-information messages (greetings, fillers) — indicated by `low_info_skipped: true`. Both halves include a `prompt` string rendered with the same templates Truememory's own agents use. To replicate automatic injection, append `context.prompt` and `retrieval.prompt` to your system prompt as-is. ## Search Memory Raw ranked facts from the knowledge graph — hybrid retrieval (BM25 + cosine similarity + graph traversal + cross-encoder reranking). Returns only the per-message search half, with no cached context block and no rendered prompts. Use [Retrieve Memory Context](#retrieve-memory-context) instead when you want the chat-identical payload. ```bash POST https://your-tie-host/memories/search Authorization: Bearer $TOKEN Content-Type: application/json ``` **Request body:** ```json { "query": "breakfast preferences", "agent_id": "chatbot", "max_results": 10 } ``` | Field | Type | Required | Description | |-------|------|----------|-------------| | `query` | string | Yes | Search query (max 5,000 characters) | | `agent_id` | string | Yes | Determines [memory scope](/memory/overview/#memory-scoping) | | `max_results` | integer | No | 1–50, default 10 | | `created_after` | ISO 8601 datetime | No | Only return memories created after this time | | `created_before` | ISO 8601 datetime | No | Only return memories created before this time | | `graph_ids` | string[] | No | Up to 4 [graph namespaces](/memory/graph-namespacing/) to blend into the search | **Response** — an array of fact edges, most relevant first: ```json [ { "id": "edge-uuid", "name": "PREFERS", "content": "User prefers a high-protein breakfast", "created_at": "2026-05-01T08:00:00Z", "valid_at": "2026-05-01T08:00:00Z", "source_name": "User", "target_name": "High-protein breakfast", "source_labels": ["Entity", "User"], "target_labels": ["Entity", "Object"], "source_descriptions": ["episode-source"], "scope": "user", "graph_id": null } ] ``` Each result carries a `scope` (`"user"` or `"graph"`) and, for namespace hits, the `graph_id` it came from — see [Graph Namespacing](/memory/graph-namespacing/). ## Warming the User Cache Truememory uses a multi-tier retrieval architecture for memory. The fastest retrieval happens from a hot in-memory cache. After several minutes of inactivity, user data transitions to a slower tier — the next request falls back to a direct database query while a background job repopulates the cache. You can signal to Truememory that a user is about to start chatting, prompting the system to proactively load their knowledge graph into cache before the first request arrives. The best time to do this is **when the user logs in or opens your app**. ```bash POST https://your-tie-host/memories/graph/{user_id}/warm Authorization: Bearer $TOKEN ``` | Parameter | In | Required | Description | |-----------|------|----------|-------------| | `user_id` | path | Yes | The user's UUID | | `agent_id` | query | No | Agent whose memory scope to warm. Defaults to the shared graph, which covers most agents. Only needed for agent-scoped memory (e.g. `rag-assistant`). | With a user token, `user_id` must be your own — warming another user's cache returns `403 AUTH_SUBJECT_FORBIDDEN`. A service token may name any user, but if it also sends `X-On-Behalf-Of` or `X-External-User-Id` the two must agree, or the call returns `400 AUTH_SUBJECT_CONFLICT`. **Response:** ```json { "entities": [ { "name": "Project Atlas", "summary": "A migration project the user is leading" }, { "name": "Bruno", "summary": "Backend developer on the Finerminds team" } ], "facts": [ "User is working on Truememory Auth integration", "Staging deployment target is April 1st" ] } ``` The result is cached for **5 minutes**. Subsequent chat requests during that window return instantly from cache. If the cache has expired by the time a chat request arrives, Truememory uses a fast database query while triggering a background refresh. Calling this endpoint again performs a full search and re-populates the cache. :::note[Deprecated path] The previous endpoint `POST /context/{agent_id}` still works but is deprecated. Migrate to `/memories/graph/{user_id}/warm` when convenient. ::: # Agent Creation Source: https://truememory-docs.mvstg.com/platform/agent-creation/ · Markdown: https://truememory-docs.mvstg.com/platform/agent-creation/index.md A user's AI agent is a **persona** stored in TIE, shared across all apps on the platform. This page covers how to correctly create and manage that agent so your app never creates duplicates or forces users to redo setup they already completed in another app. ## AI Coding Prompt Drop this into Claude, Codex, or any AI coding assistant to implement correct agent creation for your app.
``` Implement TIE agent creation following these rules: TIE is the cross-app source of truth for every user's AI agent. Each user has exactly one non-preset persona on TIE, shared across all apps (BlinkLife, Vibrantly, etc). BASE URL: https://your-tie-host AUTH: All persona and voice endpoints use a Bearer token issued by TIE Auth for the end user. Do NOT use a service key here — these are user-scoped endpoints. --- STEP 1 — CHECK FOR EXISTING AGENT GET /personas Authorization: Bearer $USER_TOKEN Response: array of persona objects. Each object has: id: string (UUID) name: string system_prompt: string archetype: string | null description: string | null is_preset: boolean ← presets are platform defaults; false = user's own agent is_active: boolean ← true if currently active for this user created_at: ISO datetime updated_at: ISO datetime Filter for is_preset: false. If one exists → GO TO "SKIP SETUP" below. If none exists → GO TO STEP 2. --- SKIP SETUP (persona already exists): - Do NOT create another persona. Each user has exactly one non-preset persona. - Do NOT prompt the user to choose a name, archetype, or voice. - Voice is already stored on TIE (or falls back to gemini/Kore if never set). - Mark this app's onboarding as complete in your own DB. - Route the user directly into the app. --- STEP 2 — CREATE THE PERSONA POST /personas Authorization: Bearer $USER_TOKEN Content-Type: application/json { "name": "Aria", "system_prompt": "You are Aria, a SAGE companion...", "archetype": "SAGE", "description": null // optional } Field rules: name: required, 1–255 chars system_prompt: required, min 1 char archetype: optional, max 100 chars. Recommended values: SAGE, FRIEND, COACH, REBEL, NURTURER description: optional On success: 201 with the created persona object (same shape as GET /personas items). Save the returned id — you need it for steps 3 and 4. Errors: 422 VALIDATION_ERROR — invalid field values 503 PERSONA_SYSTEM_UNAVAILABLE — TIE persona store is down; surface an error to the user --- STEP 3 — SET PERSONA AS ACTIVE PUT /personas/active/{persona_id} Authorization: Bearer $USER_TOKEN Response: { "status": "active", "persona_id": "..." } Errors: 404 PERSONA_NOT_FOUND — persona_id doesn't exist or isn't owned by this user --- STEP 4 — SAVE VOICE PREFERENCE PUT /v1/voice/preferences/{persona_id} Authorization: Bearer $USER_TOKEN Content-Type: application/json { "provider": "gemini", "voice": "Kore", "language": "en" } Field rules: provider: string, default "gemini" voice: required, one of: Kore, Puck, Charon, Aoede, Fenrir, Leda, Orus, Zephyr language: required, BCP-47 code (e.g. "en") Response: { "provider": "gemini", "voice": "Kore", "language": "en" } If you skip this call, the persona falls back to gemini/Kore. Errors: 404 PERSONA_NOT_FOUND — persona doesn't exist or not owned by user 503 SERVICE_UNAVAILABLE — voice store is down --- VOICE PREVIEW (during voice selection UI): POST /v1/audio/speech Authorization: Bearer $USER_TOKEN Content-Type: application/json { "model": "google/gemini-2.5-flash-tts", "input": "Hi, I'm Aria. Nice to meet you.", "voice": "Kore" } Returns PCM/WAV audio bytes. Play directly in browser or mobile. --- GET SAVED VOICE (to display current selection): GET /v1/voice/preferences/{persona_id} Authorization: Bearer $USER_TOKEN Returns: { "provider": "gemini", "voice": "Kore", "language": "en" } Falls back to gemini/Kore/en defaults if never saved. --- COMPLETE FLOW SUMMARY: 1. GET /personas → find is_preset: false 2a. Found → skip setup, mark app onboarding done, enter app 2b. Missing → POST /personas (save returned id) → PUT /personas/active/{id} → PUT /v1/voice/preferences/{id} → mark app onboarding done, enter app ``` --- ## Core Rules 1. **One agent per user** — each user has exactly one non-preset persona on TIE. Never create a second one. 2. **Always check first** — call `GET /personas` before creating. If a non-preset persona exists, skip setup entirely and mark this app's onboarding complete. 3. **TIE is source of truth** — the agent and its voice preference live on TIE and are shared across every app. 4. **`archetype` is canonical** — always set it on create so any app can read the personality type. 5. **Voice is scoped to the persona** — stored per-persona on TIE via `/v1/voice/preferences/{persona_id}`. Collect it during setup; inherit it when skipping. --- ## Flow ``` 1. GET /personas → find existing non-preset persona 2a. Found → skip setup entirely, mark onboarding complete (voice already on TIE) 2b. Not found → run setup: POST /personas to create PUT /personas/active/{id} PUT /v1/voice/preferences/{id} (selected Gemini voice for that persona) ``` ### Step 1 — Check for existing agent ```bash GET https://your-tie-host/personas Authorization: Bearer $USER_TOKEN ``` Filter the response for `is_preset: false`. If one exists: - The agent is already set up. - The voice preference is also already set (or falls back to `gemini/Kore` if never saved). - **Skip setup entirely** — do not prompt for name, archetype, or voice. Advance the user's onboarding state past this step. ### Step 2 — No agent: create one ```bash POST https://your-tie-host/personas Authorization: Bearer $USER_TOKEN Content-Type: application/json { "name": "Aria", "system_prompt": "You are Aria, a SAGE companion...", "archetype": "SAGE" } ``` Set it as active immediately after: ```bash PUT https://your-tie-host/personas/active/{persona_id} Authorization: Bearer $USER_TOKEN ``` ### Step 3 — Save the chosen voice ```bash PUT https://your-tie-host/v1/voice/preferences/{persona_id} Authorization: Bearer $USER_TOKEN Content-Type: application/json { "provider": "gemini", "voice": "Kore", "language": "en" } ``` Use the `persona_id` returned from `POST /personas` in Step 2. Accepted Gemini voice names: `Kore`, `Puck`, `Charon`, `Aoede`, `Fenrir`, `Leda`, `Orus`, `Zephyr`. If you skip this call, the persona falls back to the default (`gemini/Kore`). For voice preview during selection, call `POST /v1/audio/speech` with `{ model: "google/gemini-2.5-flash-tts", input, voice }` and play the returned PCM/WAV bytes. See [Audio — Gemini TTS](/ai-gateway/audio/#gemini-tts-same-endpoint-different-model). # 3rd-Party Memory Integration Source: https://truememory-docs.mvstg.com/platform/memory-integration/ · Markdown: https://truememory-docs.mvstg.com/platform/memory-integration/index.md Use TIE's memory as a standalone service from any backend — even if your users never sign in to TIE and your app runs its own LLM. Two endpoints cover the whole loop: **write** facts into a user's knowledge graph, and **retrieve** a prompt-ready context block with the same recall quality as TIE's own chat agents. ## Before You Start | What | Where to get it | |------|----------------| | **AUTH_SECRET** | Issued by the TIE team — see [Service accounts](/authentication/service-accounts/). | | **Stable user IDs** | Any string identifier from your own system (database ID, CRM ID). TIE derives a deterministic user UUID from it — no TIE sign-up needed. | Send your user's ID in the `X-External-User-Id` header on every call. The value is **case-sensitive** and must stay stable — see the [header rules](/authentication/service-accounts/#acting-for-users-with-external-ids). ## Step 1 — Write memories Push facts into the user's knowledge graph whenever something worth remembering happens in your app — a profile update, an onboarding answer, a meaningful conversation turn. ```bash curl -X POST https://your-tie-host/memories/graph \ -H "Authorization: Bearer $AUTH_SECRET" \ -H "X-External-User-Id: crm-user-8841" \ -H "Content-Type: application/json" \ -d '{ "data": "User runs a bakery in Penang and wants to automate invoicing.", "type": "text", "agent_id": "chatbot", "source_description": "CRM profile sync" }' ``` Returns `202` immediately; entity extraction runs in the background. Pass `"wait": true` to block until ingestion completes (useful in tests). Extraction quality depends on how you phrase the data — write declarative statements, not questions. See [Formatting Data for Extraction](/memory/add-data/#formatting-data-for-extraction). ## Step 2 — Retrieve context for your LLM Before each of your own LLM calls, fetch the user's memory context: ```bash curl -X POST https://your-tie-host/memories/retrieve \ -H "Authorization: Bearer $AUTH_SECRET" \ -H "X-External-User-Id: crm-user-8841" \ -H "Content-Type: application/json" \ -d '{ "query": "how can I save time on paperwork?", "agent_id": "chatbot", "display_name": "Aisha" }' ``` This runs the **exact pipeline TIE uses for automatic memory injection** in its own chat endpoint — a cached session-context block plus a per-message graph search. The response includes two rendered prompt strings; append them to your system prompt as-is: ```python context = response["context"]["prompt"] # "## Known Entities ... ## Known Facts ..." retrieval = response["retrieval"]["prompt"] # "## Relevant memories for this message ..." system_prompt = f"{your_instructions}\n\n{context}\n\n{retrieval}" ``` Full request/response reference: [Retrieve Memory Context](/memory/retrieval/#retrieve-memory-context). If you only want raw ranked facts to format yourself, use [Search Memory](/memory/retrieval/#search-memory) instead. ## Optional — warm the cache on app open The context block is cached for 5 minutes per user. To make the first retrieve of a session fast, [warm the cache](/memory/retrieval/#warming-the-user-cache) when the user opens your app. Note the warm endpoint takes the **derived UUID** in its path, not your external ID — in practice it's simpler to let the first `retrieve` call warm the cache itself. ## Good to know - **One ID, one graph — everywhere.** The same external ID always maps to the same user, across all apps sharing the TIE instance. Namespace app-local IDs (`myapp:42`) if they aren't globally meaningful. - **Case-sensitivity.** `Alice` and `alice` are different users. Canonicalize before sending. - **Writes are eventually consistent.** A `202` means accepted, not ingested — allow a few seconds before expecting new facts in retrieval, or use `"wait": true`. # Onboarding Source: https://truememory-docs.mvstg.com/platform/onboarding/ · Markdown: https://truememory-docs.mvstg.com/platform/onboarding/index.md TIE's `onboarding` agent collects a user's identity fields through a natural conversation before their memory graph is established. All apps on the platform share the same unified field set — a user who onboarded in Vibrantly won't be re-asked for their name in BlinkLife. ## AI Coding Prompt Drop this into Claude, Codex, or any AI coding assistant to implement TIE onboarding for your app.
``` Implement TIE onboarding following these rules: TIE's onboarding agent collects a user's identity fields through a natural one-question- at-a-time conversation before their memory graph is established. All apps on the platform share the same unified field set so users are never re-asked for information they already gave in another app. BASE URL: https://your-tie-host AUTH: - All endpoints take the user's TIE Bearer token (the same token you use for chat completions). Issue these calls from your backend with the user's token in scope — never with a long-lived service credential. --- STEP 1 — START THE ONBOARDING CONVERSATION POST /v1/chat/completions Authorization: Bearer $USER_TOKEN X-Agent-Id: onboarding Content-Type: application/json { "model": "anthropic/claude-sonnet-4-6", "messages": [{ "role": "user", "content": "Hi" }], "stream": true, "persona_id": "", "skill_instructions": "" } persona_id: the UUID of the user's active persona (from GET /personas, is_preset: false). Pass this so the onboarding agent speaks in the user's chosen persona voice. If omitted, the agent falls back to a generic default voice. Always pass it when the persona exists. skill_instructions fields (serialize as JSON string): questions: array of question objects — your question list. Each question object: id (required) — field ID. Unified IDs (preferred_name, date_of_birth, location) override the default text; any other ID is app-specific. question (required) — question text shown to the user. hint (optional) — inline guidance for the agent about what counts as a valid answer. E.g. "must be a real city, country, or region on Earth". optional (optional, boolean) — when true, the agent signals the user can skip this field, and onboarding_complete succeeds without it. multi (optional, boolean) — when true, the agent asks "anything else?" once to collect multiple values. already_captured: object of { field_id: value } — fields you already know. TIE skips re-asking these. Always pre-fill every field you already have — from a signup form, OAuth profile, age-verification screen, or a prior app session. If you collected date_of_birth during signup and omit it here, the agent will ask for it again mid-conversation, which feels broken to the user. acknowledge_source_app: boolean — when true, TIE opens with "I already know a bit about you" for users who onboarded in another app. include_unified_fields: boolean, default true — set false to ask ONLY your questions. Unified fields you don't list are then neither asked nor required by onboarding_complete. List a unified ID in questions to still ask it. rules: string — your own instructions for the agent (e.g. "ask each question exactly as written, never follow up"). Added after TIE's base onboarding rules and win where they conflict. They can't change the tool calls described in step 2. confirm_before_complete: boolean, default true — set false to have the agent send onboarding_complete as soon as the last question is answered, instead of first asking "anything you'd like to adjust, or shall we wrap?". skill_instructions must be the JSON string itself. Text that doesn't start with "{" is ignored. To keep your onboarding wording in Truememory instead of your app, store it as a skill and name it in the request ("skills": ["your-skill-key"]): its prompt is added ahead of "rules", so editing the skill changes the agent without an app release. Questions and the flags above still come from skill_instructions. Example skill_instructions (as JSON string): { "questions": [ { "id": "date_of_birth", "question": "When were you born?", "hint": "a date — e.g. 1993, March 1993, or a full date like Feb 10 1993" }, { "id": "location", "question": "Where in the world are you based?", "hint": "a recognisable city, country, or region on Earth" }, { "id": "currentFocus", "question": "What are you focused on right now?", "optional": true }, { "id": "expectations", "question": "If I could help with one thing, what would it be?", "optional": true } ], "already_captured": { "preferred_name": "Julio", "date_of_birth": "1993-05-14" }, "acknowledge_source_app": false } --- STEP 2 — WATCH THE SSE STREAM The response is an OpenAI-compatible SSE stream. Each data: line carries a chat.completion.chunk JSON payload. Look for choices[0].delta.tool_calls[]. Four events to handle: a) capture_field — fires after each answer (UX only): { "function": { "name": "capture_field", "arguments": "{\"field_id\":\"location\",\"value\":\"Kuala Lumpur\"}" } } Use this ONLY for live UI feedback (checkmarks, progress indicators). Do NOT persist data from capture_field — the user may correct answers. b) skip_field — fires when the user explicitly opts out of a question: { "function": { "name": "skip_field", "arguments": "{\"field_id\":\"currentFocus\",\"reason\":\"privacy\"}" } } The field is omitted from the final onboarding_complete payload. You can store the skip as an opt-out signal in your own DB if needed. c) onboarding_complete — terminal event, fires once when all required fields are collected (and, unless confirm_before_complete is false, the user has confirmed the agent's summary): { "function": { "name": "onboarding_complete", "arguments": "{\"fields\":{\"preferred_name\":\"Julio\",\"date_of_birth\":\"1995-05-23\",\"location\":\"Kuala Lumpur\"}}" } } This is the primary success event. Show a confirmation screen with the collected fields. Fields marked optional that the user skipped are absent from this payload. d) onboarding_aborted — terminal event, fires in three cases: 1. The user chooses to stop ("stop", "later", "not now"). 2. Your skill_instructions JSON is malformed (TIE can't validate the field set). 3. The conversation hits the 20-turn safety cap without reaching onboarding_complete. { "function": { "name": "onboarding_aborted", "arguments": "{\"fields\":{\"preferred_name\":\"Julio\"},\"reason\":\"user_stopped\"}" } } The reason field tells you which case fired: - "user_stopped" / free-form text → user opted out - "max_turns_exceeded (N >= 20)" → safety cap (rare; suggests prompt or LLM issue) fields contains only what was collected so far AND only fields TIE knows about (your skill_instructions question IDs + the unified set) — unknown IDs are dropped. Treat this as PARTIAL data: do NOT show a confirmation screen or write anything to user-metadata automatically. Route the user to a fallback (manual form or retry prompt) rather than silently dropping them. --- STEP 3 — USER CONFIRMS: WRITE METADATA After the user confirms on your confirmation screen, write to TIE Auth using the same user Bearer token you used for the onboarding conversation: a) Unified fields (shared across all TIE apps) — wrap them in a `metadata` object: PATCH /auth/me Authorization: Bearer $USER_TOKEN Content-Type: application/json { "metadata": { "preferred_name": "Julio", "date_of_birth": "1995-05-23", "location": "Kuala Lumpur" } } Unified field IDs: preferred_name, date_of_birth, location Stored in raw_user_meta_data — visible to all apps. b) App-specific fields — body is the patch itself: PATCH /auth/me/app-metadata?app=your_app Authorization: Bearer $USER_TOKEN Content-Type: application/json { "currentFocus": "Personal growth" } Stored in raw_app_meta_data[your_app] — isolated per app namespace. Both PATCH endpoints: - Are atomic shallow merges — keys you send are upserted, omitted keys are unchanged. - Accept null values to delete a top-level key (nested nulls are preserved as data). - Cap the request body at 16 KiB and 64 top-level keys (422 VALIDATION_ERROR if exceeded). - Return 404 if the user doesn't exist (no upsert). --- STEP 4 — CREATE THE AGENT PERSONA See the Agent Creation guide. Summary: - GET /personas with the user's Bearer token, filter for is_preset: false. - If one exists: skip creation (agent already set up from another app). - If none: POST /personas → PUT /personas/active/{id} → PUT /v1/voice/preferences/{id}. --- STEP 5 — MARK ONBOARDING COMPLETE Update your own app's database to record that this user has completed onboarding. Route the user into the app. --- FIELD OWNERSHIP SUMMARY: Unified (raw_user_meta_data, shared): preferred_name, date_of_birth, location App-specific (raw_app_meta_data[app]): any field ID not in the unified set TIE collects all unified fields by default — set include_unified_fields: false to skip the ones you don't list. App-specific questions always appear after unified fields. --- IMPORTANT RULES: - The onboarding agent has NO memory — it runs before the user's knowledge graph is established. Never use it for post-onboarding conversations. - Only write data after onboarding_complete, not during capture_field events. - Always handle onboarding_aborted as a terminal event — it can fire even without explicit user opt-out (malformed config, safety cap). Don't treat it as a stream error. - Always show a confirmation screen before persisting anything. ``` --- ## Flow ``` 1. Start onboarding conversation with TIE (pass already_captured + questions) 2. Stream turns until onboarding_complete fires 3. Show user confirmation screen 4. On confirm → write unified fields to user-metadata → write app-specific fields to app-metadata → create/update agent persona 5. Mark onboarding complete in your app ``` --- ## Field Ownership | Field ID | Type | Stored in | |---|---|---| | `preferred_name` | Unified | `raw_user_meta_data` | | `date_of_birth` | Unified | `raw_user_meta_data` | | `location` | Unified | `raw_user_meta_data` | | `currentFocus` | App-specific | `raw_app_meta_data[your_app]` | | `expectations` | App-specific | `raw_app_meta_data[your_app]` | | *(any custom ID)* | App-specific | `raw_app_meta_data[your_app]` | Unified fields are collected once across all TIE apps — if a user answered them in another app, TIE skips re-asking. App-specific fields are isolated per app namespace. --- ## Running the Onboarding Agent Two auth patterns are supported — use whichever fits your architecture: ```bash # Option A — client-side (mobile / browser): user's own token POST https://your-tie-host/v1/chat/completions X-Agent-Id: onboarding Authorization: Bearer $USER_TOKEN Content-Type: application/json # Option B — server-side: service key on behalf of the user POST https://your-tie-host/v1/chat/completions X-Agent-Id: onboarding Authorization: Bearer $SERVICE_KEY X-On-Behalf-Of: $TIE_USER_ID Content-Type: application/json ``` Request body (same for both): ```json { "model": "anthropic/claude-sonnet-4-6", "messages": [{ "role": "user", "content": "Hi" }], "stream": true, "skill_instructions": "{\"questions\": [{\"id\": \"preferred_name\", \"question\": \"What should I call you?\"}, {\"id\": \"date_of_birth\", \"question\": \"When were you born?\"}, {\"id\": \"location\", \"question\": \"Where in the world are you based?\"}, {\"id\": \"currentFocus\", \"question\": \"What are you currently focused on?\"}], \"already_captured\": {\"preferred_name\": \"Julio\"}, \"acknowledge_source_app\": false}" } ``` | `skill_instructions` field | Description | |---|---| | `questions` | Your question list. Each entry: `id` (required), `question` (required), `hint` (optional — guidance on what counts as valid), `optional` (optional bool — user can skip, completion succeeds without it), `multi` (optional bool — collects multiple values). Unified IDs (`preferred_name`, `date_of_birth`, `location`) override default text; any other ID is app-specific, appended after unified fields. | | `already_captured` | Fields already known — TIE skips re-asking these. **Pre-fill any fields you collected before the conversation** (e.g. DOB from a signup screen, name from OAuth). If you don't, the agent will ask for data you already have. | | `acknowledge_source_app` | When `true`, TIE opens with "I already know a bit about you" for returning users | | `include_unified_fields` | Default `true`. Set `false` to ask only your `questions` — unified fields you don't list are neither asked nor required by `onboarding_complete`. | | `rules` | Your own instructions for the agent, added after TIE's base onboarding rules. They win where the two conflict (wording, acknowledgments, follow-ups) but can't change the tool calls. | | `confirm_before_complete` | Default `true`: once every question is answered, the agent mirrors the answers back and asks whether to adjust or wrap. Set `false` to send `onboarding_complete` right after the last answer. | `skill_instructions` must be the JSON string itself. Text that doesn't start with `{` is ignored. To keep your onboarding wording in Truememory instead of your app, store it as a [skill](/ai-gateway/skills/) and name it in the request with `"skills": ["your-skill-key"]`. Its prompt is added ahead of `rules`, so editing the skill changes the agent without an app release. The questions and the flags above still come from `skill_instructions`, and `skill_selection: "auto"` doesn't apply to onboarding. :::tip[Pre-fill what you already know] Pass every field you've already collected in `already_captured`, even unified fields like `date_of_birth`. If a user provided their birthday during signup and you don't include it here, the agent will ask for it again mid-conversation — which feels broken. This applies to any app-collected data: DOB from age-verification screens, name from OAuth, location from signup forms. ::: ### Asking only your own questions An app with its own short flow can turn off the unified fields and the confirmation turn. This asks exactly two questions and sends `onboarding_complete` after the second answer, while still using the user's name from `already_captured`: ```json { "include_unified_fields": false, "confirm_before_complete": false, "rules": "Ask each question exactly as written, one per message. Never ask follow-ups.", "questions": [ { "id": "currentFocus", "question": "What's the one thing you want to get better at?" }, { "id": "obstacle", "question": "What usually gets in the way?" } ], "already_captured": { "preferred_name": "Julio" }, "acknowledge_source_app": false } ``` --- ## Watching the Tool Calls The onboarding agent emits four observable tool calls. These appear in the same place regardless of whether you use streaming or non-streaming mode. **Streaming (`"stream": true`)** — tool calls arrive as SSE delta chunks. Each `data:` line carries a `chat.completion.chunk` JSON payload. Look for `choices[0].delta.tool_calls[]`: **Non-streaming (`"stream": false`)** — tool calls arrive in the final response body at `choices[0].message.tool_calls[]`. Same shape, no stream parsing needed. BlinkLife uses this mode. ```json // Field collected — update UI only (e.g. mark a question as ✓), persist nothing yet {"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"id":"call_a","type":"function","function":{"name":"capture_field","arguments":"{\"field_id\":\"date_of_birth\",\"value\":\"1995-05-23\"}"}}]}}]} // User skipped an optional field — store as opt-out signal if needed, do not require it {"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"id":"call_b","type":"function","function":{"name":"skip_field","arguments":"{\"field_id\":\"currentFocus\",\"reason\":\"privacy\"}"}}]}}]} // All required fields collected — show confirmation screen {"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"id":"call_c","type":"function","function":{"name":"onboarding_complete","arguments":"{\"fields\":{\"preferred_name\":\"Julio\",\"date_of_birth\":\"1995-05-23\",\"location\":\"Kuala Lumpur\"}}"}}]}}]} // User chose to stop — treat as partial, do not persist {"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"id":"call_d","type":"function","function":{"name":"onboarding_aborted","arguments":"{\"fields\":{\"preferred_name\":\"Julio\"},\"reason\":\"user_stopped\"}"}}]}}]} ``` Only act on `onboarding_complete`. Do not write data or create the agent persona until the user confirms on your confirmation screen. If you receive `onboarding_aborted`, treat the data as partial — do not write to user-metadata and do not show a confirmation screen. Route the user to a fallback such as a manual form or a retry prompt. :::caution[onboarding_aborted can fire automatically] This event fires not just when the user opts out — it also fires when: - Your `skill_instructions` JSON is malformed (TIE can't validate the field set). - The conversation crosses the **20-turn safety cap** without reaching `onboarding_complete` (rare; usually means the LLM is stuck on an unparseable answer). Inspect the `reason` field to distinguish: a string starting with `"max_turns_exceeded"` indicates the safety cap fired. Either way the contract is the same — partial data, no auto-persist, route the user to a fallback. ::: --- ## After Confirmation ```bash # 1. Write unified identity fields (shared across apps) — wrap in `metadata` PATCH https://your-tie-host/auth/me Authorization: Bearer $USER_TOKEN Content-Type: application/json { "metadata": { "preferred_name": "Julio", "date_of_birth": "1995-05-23", "location": "Kuala Lumpur" } } # 2. Write app-specific fields — body is the patch itself PATCH https://your-tie-host/auth/me/app-metadata?app=your_app Authorization: Bearer $USER_TOKEN Content-Type: application/json { "currentFocus": "Personal growth" } # 3. Create or update the agent persona # → See the Agent Creation guide ``` > The onboarding agent has **no memory** — it runs before the user's knowledge graph is established. Never write data based on `capture_field` events alone — the user may correct answers before `onboarding_complete` fires. --- ## Manual Update (Skip the Agent) If you already have the user's data — from a signup form, a previous app, or a migration — you can write it directly without running the onboarding conversation at all. Use the same metadata endpoints: ```bash # Unified identity fields (shared across all TIE apps) — wrap in `metadata` PATCH https://your-tie-host/auth/me Authorization: Bearer $USER_TOKEN Content-Type: application/json { "metadata": { "preferred_name": "Julio", "date_of_birth": "1995-05-23", "location": "Kuala Lumpur" } } # App-specific fields (isolated to your app namespace) PATCH https://your-tie-host/auth/me/app-metadata?app=your_app Authorization: Bearer $USER_TOKEN Content-Type: application/json { "currentFocus": "Personal growth" } ``` Both endpoints do a **shallow merge** — keys you send are upserted, keys you omit are left unchanged. Pass `null` as a value to delete a key from the namespace. :::note[When to use this] - Migrating existing users who already filled out a profile in your system - Pre-populating data collected on a signup screen (DOB, location) before the first session - Correcting data after the fact without re-running the onboarding flow - Testing without going through the full conversation :::